diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index b2b385e0cabe1f39616cbfd1930b3eb6e7bef6ae..6f7c18cd24b3c408ef340239944a7cb7f864b79b 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -198,14 +198,6 @@ AuxiliaryProcessMain(int argc, char *argv[])
 
 	MyStartTime = time(NULL);
 
-	/*
-	 * Fire up essential subsystems: error and memory management
-	 *
-	 * If we are running under the postmaster, this is done already.
-	 */
-	if (!IsUnderPostmaster)
-		MemoryContextInit();
-
 	/* Compute paths, if we didn't inherit them from postmaster */
 	if (my_exec_path[0] == '\0')
 	{
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index e0183029e9f7de9557afbb3336a28a974a6e3c99..fc5e7c396b6ecd5f1c1154b3e90b8af87e00a566 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -39,6 +39,7 @@
 #include "postmaster/postmaster.h"
 #include "tcop/tcopprot.h"
 #include "utils/help_config.h"
+#include "utils/memutils.h"
 #include "utils/pg_locale.h"
 #include "utils/ps_status.h"
 #ifdef WIN32
@@ -89,6 +90,15 @@ main(int argc, char *argv[])
 	pgwin32_install_crashdump_handler();
 #endif
 
+	/*
+	 * Fire up essential subsystems: error and memory management
+	 *
+	 * Code after this point is allowed to use elog/ereport, though
+	 * localization of messages may not work right away, and messages won't go
+	 * anywhere but stderr until GUC settings get loaded.
+	 */
+	MemoryContextInit();
+
 	/*
 	 * Set up locale information from environment.	Note that LC_CTYPE and
 	 * LC_COLLATE will be overridden later from pg_control if we are in an
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 437b7fae6deddaf98b6b973e6b66bd2d8a83b58f..b96b505834a19e9888c2ae7d2ffb4d8341d43a6f 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -512,11 +512,6 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	umask(S_IRWXG | S_IRWXO);
 
-	/*
-	 * Fire up essential subsystems: memory management
-	 */
-	MemoryContextInit();
-
 	/*
 	 * By default, palloc() requests in the postmaster will be allocated in
 	 * the PostmasterContext, which is space that can be recycled by backends.
@@ -3991,7 +3986,6 @@ SubPostmasterMain(int argc, char *argv[])
 	whereToSendOutput = DestNone;
 
 	/* Setup essential subsystems (to ensure elog() behaves sanely) */
-	MemoryContextInit();
 	InitializeGUCOptions();
 
 	/* Read in the variables file */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 470184bc5bbe96cc8c0f66cc283e42a3e781c126..8b76af89a3406f9e42a189f5cbe1081a41b1c66e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3525,14 +3525,6 @@ PostgresMain(int argc, char *argv[],
 		MyStartTime = time(NULL);
 	}
 
-	/*
-	 * Fire up essential subsystems: error and memory management
-	 *
-	 * If we are running under the postmaster, this is done already.
-	 */
-	if (!IsUnderPostmaster)
-		MemoryContextInit();
-
 	SetProcessingMode(InitProcessing);
 
 	/* Compute paths, if we didn't inherit them from postmaster */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 831fe07120f6beb63d4ee7f13587e43a830be00f..f5dfe64df2ea07f637f5acd21e1493bdca0c13d2 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -311,6 +311,18 @@ errstart(int elevel, const char *filename, int lineno,
 	if (elevel < ERROR && !output_to_server && !output_to_client)
 		return false;
 
+	/*
+	 * We need to do some actual work.  Make sure that memory context
+	 * initialization has finished, else we can't do anything useful.
+	 */
+	if (ErrorContext == NULL)
+	{
+		/* Ooops, hard crash time; very little we can do safely here */
+		write_stderr("error occurred at %s:%d before error message processing is available\n",
+					 filename ? filename : "(unknown file)", lineno);
+		exit(2);
+	}
+
 	/*
 	 * Okay, crank up a stack entry to store the info in.
 	 */
@@ -1147,6 +1159,15 @@ elog_start(const char *filename, int lineno, const char *funcname)
 {
 	ErrorData  *edata;
 
+	/* Make sure that memory context initialization has finished */
+	if (ErrorContext == NULL)
+	{
+		/* Ooops, hard crash time; very little we can do safely here */
+		write_stderr("error occurred at %s:%d before error message processing is available\n",
+					 filename ? filename : "(unknown file)", lineno);
+		exit(2);
+	}
+
 	if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
 	{
 		/*
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 466d53d57ef555cc717ee6b57b627a084d70ea46..024f2cd65b0ea74650396037bacbf39648d6254a 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -68,7 +68,8 @@ static void MemoryContextStatsInternal(MemoryContext context, int level);
  * In normal multi-backend operation, this is called once during
  * postmaster startup, and not at all by individual backend startup
  * (since the backends inherit an already-initialized context subsystem
- * by virtue of being forked off the postmaster).
+ * by virtue of being forked off the postmaster).  But in an EXEC_BACKEND
+ * build, each process must do this for itself.
  *
  * In a standalone backend this must be called during backend startup.
  */
@@ -102,6 +103,9 @@ MemoryContextInit(void)
 	 * where retained memory in a context is *essential* --- we want to be
 	 * sure ErrorContext still has some memory even if we've run out
 	 * elsewhere!
+	 *
+	 * This should be the last step in this function, as elog.c assumes memory
+	 * management works once ErrorContext is non-null.
 	 */
 	ErrorContext = AllocSetContextCreate(TopMemoryContext,
 										 "ErrorContext",