diff --git a/lld/test/wasm/data-segments.ll b/lld/test/wasm/data-segments.ll
--- a/lld/test/wasm/data-segments.ll
+++ b/lld/test/wasm/data-segments.ll
@@ -13,6 +13,10 @@
 ; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.o -o %t.atomics.bulk-mem.wasm
 ; RUN: obj2yaml %t.atomics.bulk-mem.wasm | FileCheck %s --check-prefixes PASSIVE
 
+; Also test in combination with PIC/pie
+; RUN: llc -filetype=obj -relocation-model=pic %s -o %t.atomics.bulk-mem.pic.o -mattr=+atomics,+bulk-memory,+mutable-globals
+; RUN: wasm-ld --experimental-pic -pie -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.pic.o -o %t.pic.wasm
+
 target triple = "wasm32-unknown-unknown"
 
 @a = hidden global [6 x i8] c"hello\00", align 1
diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp
--- a/lld/wasm/MarkLive.cpp
+++ b/lld/wasm/MarkLive.cpp
@@ -96,15 +96,13 @@
     if (sym->isNoStrip() || sym->isExported())
       enqueue(sym);
 
-  // If we'll be calling the user's `__wasm_call_dtors` function, mark it live.
-  if (Symbol *callDtors = WasmSym::callDtors)
-    enqueue(callDtors);
+  if (WasmSym::callDtors)
+    enqueue(WasmSym::callDtors);
 
-  // In Emscripten-style PIC, `__wasm_call_ctors` calls `__wasm_apply_relocs`.
-  if (config->isPic)
+  if (WasmSym::applyRelocs)
     enqueue(WasmSym::applyRelocs);
 
-  if (config->sharedMemory && !config->shared)
+  if (WasmSym::initMemory)
     enqueue(WasmSym::initMemory);
 
   // Enqueue constructors in objects explicitly live from the command-line.
diff --git a/lld/wasm/OutputSections.cpp b/lld/wasm/OutputSections.cpp
--- a/lld/wasm/OutputSections.cpp
+++ b/lld/wasm/OutputSections.cpp
@@ -90,6 +90,8 @@
     func->outputSec = this;
     func->outputOffset = bodySize;
     func->calculateSize();
+    // All functions should have a non-empty body at this point
+    assert(func->getSize());
     bodySize += func->getSize();
   }
 
@@ -101,6 +103,7 @@
   log(" size=" + Twine(getSize()));
   log(" headersize=" + Twine(header.size()));
   log(" codeheadersize=" + Twine(codeSectionHeader.size()));
+  log(" functions=" + Twine(functions.size()));
   buf += offset;
 
   // Write section header
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -209,6 +209,7 @@
     return;
   uint32_t functionIndex =
       out.importSec->getNumImportedFunctions() + inputFunctions.size();
+  LLVM_DEBUG(dbgs() << "addFunction: " << inputFunctions.size() << "\n");
   inputFunctions.emplace_back(func);
   func->setFunctionIndex(functionIndex);
 }
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -1214,11 +1214,12 @@
   calculateInitFunctions();
 
   if (!config->relocatable) {
-    // Create linker synthesized functions
-    if (config->isPic)
+    if (WasmSym::applyRelocs)
       createApplyRelocationsFunction();
-    else if (config->sharedMemory)
+    if (WasmSym::initMemory)
       createInitMemoryFunction();
+
+    // Create linker synthesized functions
     createCallCtorsFunction();
 
     // Create export wrappers for commands if needed.