diff --git a/src/backend/main/Makefile b/src/backend/main/Makefile
index 6d34072624b..e49b10fe0c4 100644
--- a/src/backend/main/Makefile
+++ b/src/backend/main/Makefile
@@ -13,6 +13,11 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = \
-	main.o
+	main.o \
+	sanitizer_hook.o
+
+# Custom rule to build sanitizer_hook.o without sanitizer instrumentation
+sanitizer_hook.o: sanitizer_hook.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) -fno-sanitize=thread,address,undefined -c -o $@ $<
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index 4672aab8378..be61d8db0a6 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -42,7 +42,7 @@
 
 
 const char *progname;
-static bool reached_main = false;
+bool reached_main = false;
 
 
 static void startup_hacks(const char *progname);
@@ -415,29 +415,3 @@ check_root(const char *progname)
 #endif							/* WIN32 */
 }
 
-/*
- * At least on linux, set_ps_display() breaks /proc/$pid/environ. The
- * sanitizer library uses /proc/$pid/environ to implement getenv() as it wants
- * to work independent of libc. When just using undefined and alignment
- * sanitizers, the sanitizer library is only initialized when the first error
- * occurs, by which time we've often already called set_ps_display(),
- * preventing the sanitizer libraries from seeing the options.
- *
- * We can work around that by defining __ubsan_default_options, a weak symbol
- * libsanitizer uses to get defaults from the application, and return
- * getenv("UBSAN_OPTIONS"). But only if main already was reached, so that we
- * don't end up relying on a not-yet-working getenv().
- *
- * As this function won't get called when not running a sanitizer, it doesn't
- * seem necessary to only compile it conditionally.
- */
-const char *__ubsan_default_options(void);
-const char *
-__ubsan_default_options(void)
-{
-	/* don't call libc before it's guaranteed to be initialized */
-	if (!reached_main)
-		return "";
-
-	return getenv("UBSAN_OPTIONS");
-}
diff --git a/src/backend/main/sanitizer_hook.c b/src/backend/main/sanitizer_hook.c
new file mode 100644
index 00000000000..61302de0ead
--- /dev/null
+++ b/src/backend/main/sanitizer_hook.c
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * sanitizer_hook.c
+ *	  UBSan options hook to avoid initialization timing issues
+ *
+ * This file provides __ubsan_default_options() without sanitizer
+ * instrumentation to prevent circular dependencies during early
+ * program startup when set_ps_display() breaks /proc/$pid/environ.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/main/sanitizer_hook.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <signal.h>
+#include <stdlib.h>
+
+
+extern bool reached_main;
+
+
+/*
+ * At least on linux, set_ps_display() breaks /proc/$pid/environ. The
+ * sanitizer library uses /proc/$pid/environ to implement getenv() as it wants
+ * to work independent of libc. When just using undefined and alignment
+ * sanitizers, the sanitizer library is only initialized when the first error
+ * occurs, by which time we've often already called set_ps_display(),
+ * preventing the sanitizer libraries from seeing the options.
+ *
+ * We can work around that by defining __ubsan_default_options, a weak symbol
+ * libsanitizer uses to get defaults from the application, and return
+ * getenv("UBSAN_OPTIONS"). But only if main already was reached, so that we
+ * don't end up relying on a not-yet-working getenv().
+ *
+ * As this function won't get called when not running a sanitizer, it doesn't
+ * seem necessary to only compile it conditionally.
+ */
+const char *
+__ubsan_default_options(void)
+{
+	/*
+	 * Avoid calling libc until program startup is complete (reached_main).
+	 * This prevents sanitizer initialization issues when set_ps_display()
+	 * breaks /proc/$pid/environ before sanitizer can read UBSAN_OPTIONS.
+	 */
+	if (!reached_main)
+		return "";
+
+	return getenv("UBSAN_OPTIONS");
+}
