Skip to content

[lld][wasm] Allow stub libraries to depend on optional linker symbols - #215112

Open
ANAMASGARD wants to merge 3 commits into
llvm:mainfrom
ANAMASGARD:fix/wasm-ld-stub-optional-symbols
Open

[lld][wasm] Allow stub libraries to depend on optional linker symbols#215112
ANAMASGARD wants to merge 3 commits into
llvm:mainfrom
ANAMASGARD:fix/wasm-ld-stub-optional-symbols

Conversation

@ANAMASGARD

@ANAMASGARD ANAMASGARD commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Materialize optional linker-created symbols on demand when stub libraries require them
  • Handle both missing and already-undefined stub dependencies (e.g. __heap_base)
  • Preserve exact PIC semantics: data-layout symbols gated on isPic; __dso_handle and __wasm_first_page_end are not
  • Remove ctx.sym.* fallback to user-defined symbols (review fix)
  • Consolidate tests into split-file layout per @sbc100
  • Apply clang-format (fixes code_formatter CI)

Test plan

  • stub-library-optional-symbol.s (absent/undefined __heap_base, user __data_end DefinedData, __memory_base, PIC __dso_handle, PIC __wasm_first_page_end, PIC __heap_base negative, __tls_base, __start_foo probe)
  • stub-library.s, stub-library-archive.s, lto/stub-library*.s, data-layout.s, export-optional.s
  • check-lld (487/488 runnable; 1 unrelated pre-existing thinlto env failure)

Addresses #180632

Note: _start/_stop stub deps are not yet supported (case 11 documents current limitation).

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lld

Author: Gauarv Chaudhary (ANAMASGARD)

Changes

Fixes #180632

Summary

  • Materialize optional linker-created symbols on demand when stub libraries require them
  • Handle both missing and already-undefined stub dependencies (e.g. __heap_base)
  • Preserve exact PIC semantics: data-layout symbols gated on isPic; __dso_handle and __wasm_first_page_end are not

Test plan

  • stub-library-optional-symbol.s (absent/undefined __heap_base, user-defined, __memory_base, PIC __dso_handle, PIC __wasm_first_page_end, PIC __heap_base negative)
  • stub-library.s, stub-library-archive.s, lto/stub-library*.s, data-layout.s, export-optional.s

Full diff: https://github.com/llvm/llvm-project/pull/215112.diff

12 Files Affected:

  • (added) lld/test/wasm/Inputs/libstub-dso-handle.so (+2)
  • (added) lld/test/wasm/Inputs/libstub-first-page-end.so (+2)
  • (added) lld/test/wasm/Inputs/libstub-heap-base.so (+2)
  • (added) lld/test/wasm/Inputs/libstub-memory-base.so (+2)
  • (added) lld/test/wasm/Inputs/stub-optional-absent.s (+8)
  • (added) lld/test/wasm/Inputs/stub-optional-memory.s (+8)
  • (added) lld/test/wasm/Inputs/stub-optional-undef.s (+10)
  • (added) lld/test/wasm/Inputs/stub-optional-user-heap.s (+11)
  • (added) lld/test/wasm/stub-library-optional-symbol.s (+76)
  • (modified) lld/wasm/Driver.cpp (+138-48)
  • (modified) lld/wasm/SymbolTable.cpp (+6-5)
  • (modified) lld/wasm/SymbolTable.h (+4-2)
diff --git a/lld/test/wasm/Inputs/libstub-dso-handle.so b/lld/test/wasm/Inputs/libstub-dso-handle.so
new file mode 100644
index 0000000000000..bb500f885468c
--- /dev/null
+++ b/lld/test/wasm/Inputs/libstub-dso-handle.so
@@ -0,0 +1,2 @@
+#STUB
+foo_import: __dso_handle
diff --git a/lld/test/wasm/Inputs/libstub-first-page-end.so b/lld/test/wasm/Inputs/libstub-first-page-end.so
new file mode 100644
index 0000000000000..55cc723835afb
--- /dev/null
+++ b/lld/test/wasm/Inputs/libstub-first-page-end.so
@@ -0,0 +1,2 @@
+#STUB
+foo_import: __wasm_first_page_end
diff --git a/lld/test/wasm/Inputs/libstub-heap-base.so b/lld/test/wasm/Inputs/libstub-heap-base.so
new file mode 100644
index 0000000000000..d0c2f37f023ff
--- /dev/null
+++ b/lld/test/wasm/Inputs/libstub-heap-base.so
@@ -0,0 +1,2 @@
+#STUB
+foo_import: __heap_base
diff --git a/lld/test/wasm/Inputs/libstub-memory-base.so b/lld/test/wasm/Inputs/libstub-memory-base.so
new file mode 100644
index 0000000000000..740e683da8d46
--- /dev/null
+++ b/lld/test/wasm/Inputs/libstub-memory-base.so
@@ -0,0 +1,2 @@
+#STUB
+foo_import: __memory_base
diff --git a/lld/test/wasm/Inputs/stub-optional-absent.s b/lld/test/wasm/Inputs/stub-optional-absent.s
new file mode 100644
index 0000000000000..4b412cbf91698
--- /dev/null
+++ b/lld/test/wasm/Inputs/stub-optional-absent.s
@@ -0,0 +1,8 @@
+.functype foo () -> ()
+.import_name foo, foo_import
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    call foo
+    end_function
diff --git a/lld/test/wasm/Inputs/stub-optional-memory.s b/lld/test/wasm/Inputs/stub-optional-memory.s
new file mode 100644
index 0000000000000..4b412cbf91698
--- /dev/null
+++ b/lld/test/wasm/Inputs/stub-optional-memory.s
@@ -0,0 +1,8 @@
+.functype foo () -> ()
+.import_name foo, foo_import
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    call foo
+    end_function
diff --git a/lld/test/wasm/Inputs/stub-optional-undef.s b/lld/test/wasm/Inputs/stub-optional-undef.s
new file mode 100644
index 0000000000000..eede83f8dcc35
--- /dev/null
+++ b/lld/test/wasm/Inputs/stub-optional-undef.s
@@ -0,0 +1,10 @@
+.functype foo () -> ()
+.import_name foo, foo_import
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    i32.const __heap_base@GOT
+    drop
+    call foo
+    end_function
diff --git a/lld/test/wasm/Inputs/stub-optional-user-heap.s b/lld/test/wasm/Inputs/stub-optional-user-heap.s
new file mode 100644
index 0000000000000..ed051470deac9
--- /dev/null
+++ b/lld/test/wasm/Inputs/stub-optional-user-heap.s
@@ -0,0 +1,11 @@
+.functype foo () -> ()
+.import_name foo, foo_import
+
+.globaltype __heap_base, i32, immutable
+__heap_base:
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    call foo
+    end_function
diff --git a/lld/test/wasm/stub-library-optional-symbol.s b/lld/test/wasm/stub-library-optional-symbol.s
new file mode 100644
index 0000000000000..d882d5dd660a6
--- /dev/null
+++ b/lld/test/wasm/stub-library-optional-symbol.s
@@ -0,0 +1,76 @@
+## Test stub library dependencies on optional linker-created symbols.
+## See https://github.com/llvm/llvm-project/issues/180632
+
+# --- Case 1: __heap_base completely absent from object (exact #180632 bug) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-absent.o %S/Inputs/stub-optional-absent.s
+# RUN: wasm-ld %t-absent.o %S/Inputs/libstub-heap-base.so -o %t-absent.wasm
+# RUN: obj2yaml %t-absent.wasm | FileCheck %s --check-prefix=CHECK-ABSENT
+
+# --- Case 2: __heap_base already Undefined in object (subtle v2 bug) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-undef.o %S/Inputs/stub-optional-undef.s
+# RUN: wasm-ld %t-undef.o %S/Inputs/libstub-heap-base.so -o %t-undef.wasm
+# RUN: obj2yaml %t-undef.wasm | FileCheck %s --check-prefix=CHECK-UNDEF
+
+# --- Case 3: unknown stub dependency still errors ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %S/Inputs/stub-optional-absent.s
+# RUN: not wasm-ld %t.o %p/Inputs/libstub-missing-dep.so -o %t.wasm 2>&1 | FileCheck %s --check-prefix=CHECK-MISSING
+# CHECK-MISSING: libstub-missing-dep.so: undefined symbol: missing_dep. Required by foo
+
+# --- Case 5: user-defined __heap_base — linker must not override ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-user.o %S/Inputs/stub-optional-user-heap.s
+# RUN: wasm-ld %t-user.o %S/Inputs/libstub-heap-base.so -o %t-user.wasm
+# RUN: obj2yaml %t-user.wasm | FileCheck %s --check-prefix=CHECK-USER
+
+# --- Case 6: __memory_base optional global path (non-PIC) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-mem.o %S/Inputs/stub-optional-memory.s
+# RUN: wasm-ld %t-mem.o %S/Inputs/libstub-memory-base.so -o %t-mem.wasm
+# RUN: obj2yaml %t-mem.wasm | FileCheck %s --check-prefix=CHECK-MEM
+
+# CHECK-ABSENT:         Field:           foo_import
+# CHECK-ABSENT:       - Type:            GLOBAL
+# CHECK-ABSENT:         InitExpr:
+# CHECK-ABSENT-NEXT:      Opcode:          I32_CONST
+# CHECK-ABSENT-NEXT:      Value:           65536
+# CHECK-ABSENT:       - Name:            __heap_base
+# CHECK-ABSENT-NEXT:    Kind:            GLOBAL
+
+# CHECK-UNDEF:         Field:           foo_import
+# CHECK-UNDEF:       - Type:            GLOBAL
+# CHECK-UNDEF:         InitExpr:
+# CHECK-UNDEF-NEXT:      Opcode:          I32_CONST
+# CHECK-UNDEF-NEXT:      Value:           65536
+# CHECK-UNDEF:       - Name:            __heap_base
+# CHECK-UNDEF-NEXT:    Kind:            GLOBAL
+
+# CHECK-USER:       - Name:            __heap_base
+# CHECK-USER-NEXT:    Kind:            GLOBAL
+
+# CHECK-MEM:         Field:           foo_import
+# CHECK-MEM:       - Name:            __memory_base
+# CHECK-MEM-NEXT:    Kind:            GLOBAL
+
+# --- Case 7: PIC __dso_handle stub dependency (must be created) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-pic-dso.o %S/Inputs/stub-optional-absent.s
+# RUN: wasm-ld --experimental-pic -pie --import-memory %t-pic-dso.o %S/Inputs/libstub-dso-handle.so -o %t-pic-dso.wasm
+# RUN: obj2yaml %t-pic-dso.wasm | FileCheck %s --check-prefix=CHECK-PIC-DSO
+
+# --- Case 8: PIC __wasm_first_page_end stub dependency (must be created) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-pic-fpe.o %S/Inputs/stub-optional-absent.s
+# RUN: wasm-ld --experimental-pic -pie --import-memory %t-pic-fpe.o %S/Inputs/libstub-first-page-end.so -o %t-pic-fpe.wasm
+# RUN: obj2yaml %t-pic-fpe.wasm | FileCheck %s --check-prefix=CHECK-PIC-FPE
+
+# --- Case 9: PIC __heap_base stub dependency (must NOT be linker-created) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-pic-heap.o %S/Inputs/stub-optional-absent.s
+# RUN: not wasm-ld --experimental-pic -pie --import-memory %t-pic-heap.o %S/Inputs/libstub-heap-base.so -o %t-pic-heap.wasm 2>&1 | FileCheck %s --check-prefix=CHECK-PIC-HEAP-FAIL
+
+# CHECK-PIC-DSO:         Field:           foo_import
+# CHECK-PIC-DSO:       - Name:            __dso_handle
+# CHECK-PIC-DSO-NEXT:    Kind:            GLOBAL
+
+# CHECK-PIC-FPE:         InitExpr:
+# CHECK-PIC-FPE-NEXT:      Opcode:          I32_CONST
+# CHECK-PIC-FPE-NEXT:      Value:           65536
+# CHECK-PIC-FPE:       - Name:            __wasm_first_page_end
+# CHECK-PIC-FPE-NEXT:    Kind:            GLOBAL
+
+# CHECK-PIC-HEAP-FAIL: undefined symbol: __heap_base
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 740e8878c6e03..3f26c7dcf32d4 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -922,9 +922,143 @@ static DefinedGlobal *createGlobalVariable(StringRef name, bool isMutable,
   return symtab->addSyntheticGlobal(name, flags, g);
 }
 
-static DefinedGlobal *createOptionalGlobal(StringRef name, bool isMutable) {
+static DefinedGlobal *createOptionalGlobal(StringRef name, bool isMutable,
+                                           bool force = false) {
+  if (Symbol *s = symtab->find(name)) {
+    if (s->isDefined())
+      return dyn_cast<DefinedGlobal>(s);
+  }
   InputGlobal *g = createGlobal(name, isMutable);
-  return symtab->addOptionalGlobalSymbol(name, g);
+  return symtab->addOptionalGlobalSymbol(name, g, force);
+}
+
+static DefinedData *materializeOptionalDataSymbol(StringRef name,
+                                                  DefinedData *&slot,
+                                                  bool force) {
+  if (slot)
+    return slot;
+  if (DefinedData *d = symtab->addOptionalDataSymbol(name, 0, force))
+    slot = d;
+  else if (Symbol *s = symtab->find(name))
+    slot = dyn_cast<DefinedData>(s);
+  return slot;
+}
+
+static DefinedData *materializeOptionalDataLayoutSymbol(StringRef name,
+                                                        DefinedData *&slot,
+                                                        bool force) {
+  if (slot)
+    return slot;
+  if (ctx.isPic) {
+    ctx.arg.allowUndefinedSymbols.insert(name);
+    return nullptr;
+  }
+  return materializeOptionalDataSymbol(name, slot, force);
+}
+
+// Materialize a known optional linker-created symbol when required by a stub
+// library dependency. Returns a defined symbol, or nullptr if the name is not
+// a known optional linker symbol (or cannot be created in the current config).
+static Symbol *resolveStubDependency(StringRef name) {
+  if (ctx.arg.relocatable)
+    return nullptr;
+
+  if (name == "__dso_handle") {
+    materializeOptionalDataSymbol(name, ctx.sym.dsoHandle, true);
+    return symtab->find(name);
+  }
+  if (name == "__data_end") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.dataEnd, true);
+    return symtab->find(name);
+  }
+  if (name == "__rodata_start") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.rodataStart, true);
+    return symtab->find(name);
+  }
+  if (name == "__rodata_end") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.rodataEnd, true);
+    return symtab->find(name);
+  }
+  if (name == "__stack_low") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.stackLow, true);
+    return symtab->find(name);
+  }
+  if (name == "__stack_high") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.stackHigh, true);
+    return symtab->find(name);
+  }
+  if (name == "__global_base") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.globalBase, true);
+    return symtab->find(name);
+  }
+  if (name == "__heap_base") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.heapBase, true);
+    return symtab->find(name);
+  }
+  if (name == "__heap_end") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.heapEnd, true);
+    return symtab->find(name);
+  }
+  if (name == "__memory_base" && !ctx.isPic) {
+    if (!ctx.sym.memoryBase)
+      ctx.sym.memoryBase = createOptionalGlobal(name, false, true);
+    return symtab->find(name);
+  }
+  if (name == "__table_base" && !ctx.isPic) {
+    if (!ctx.sym.tableBase)
+      ctx.sym.tableBase = createOptionalGlobal(name, false, true);
+    return symtab->find(name);
+  }
+  if (name == "__wasm_first_page_end") {
+    materializeOptionalDataSymbol(name, ctx.sym.firstPageEnd, true);
+    if (ctx.sym.firstPageEnd)
+      ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);
+    return symtab->find(name);
+  }
+  if (name == "__tls_base" && !ctx.sym.tlsBase) {
+    ctx.sym.tlsBase = createOptionalGlobal(name, false, true);
+    return symtab->find(name);
+  }
+  return nullptr;
+}
+
+static void createOptionalSymbols() {
+  if (ctx.arg.relocatable)
+    return;
+
+  if (!ctx.sym.dsoHandle)
+    materializeOptionalDataSymbol("__dso_handle", ctx.sym.dsoHandle, false);
+
+  materializeOptionalDataLayoutSymbol("__data_end", ctx.sym.dataEnd, false);
+  materializeOptionalDataLayoutSymbol("__rodata_start", ctx.sym.rodataStart, false);
+  materializeOptionalDataLayoutSymbol("__rodata_end", ctx.sym.rodataEnd, false);
+  materializeOptionalDataLayoutSymbol("__stack_low", ctx.sym.stackLow, false);
+  materializeOptionalDataLayoutSymbol("__stack_high", ctx.sym.stackHigh, false);
+  materializeOptionalDataLayoutSymbol("__global_base", ctx.sym.globalBase, false);
+  materializeOptionalDataLayoutSymbol("__heap_base", ctx.sym.heapBase, false);
+  materializeOptionalDataLayoutSymbol("__heap_end", ctx.sym.heapEnd, false);
+
+  // for pic, __memory_base and __table_base are handled in
+  // createSyntheticSymbols.
+  if (!ctx.isPic) {
+    if (!ctx.sym.memoryBase)
+      ctx.sym.memoryBase = createOptionalGlobal("__memory_base", false);
+    if (!ctx.sym.tableBase)
+      ctx.sym.tableBase = createOptionalGlobal("__table_base", false);
+  }
+
+  if (!ctx.sym.firstPageEnd)
+    materializeOptionalDataSymbol("__wasm_first_page_end", ctx.sym.firstPageEnd, false);
+  if (ctx.sym.firstPageEnd)
+    ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);
+
+  // TLS object files may be linked into single-threaded programs, so
+  // __tls_base must always be defined. In this case it is immutable and points
+  // directly to the start of the `.tdata` segment. __tls_size and __tls_align
+  // are omitted since they are only used by __wasm_init_tls, which is not
+  // created in this case.
+  if (!ctx.sym.tlsBase)
+    ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
 }
 
 // Create ABI-defined synthetic symbols
@@ -1008,52 +1142,6 @@ static void createSyntheticSymbols() {
   }
 }
 
-static void createOptionalSymbols() {
-  if (ctx.arg.relocatable)
-    return;
-
-  ctx.sym.dsoHandle = symtab->addOptionalDataSymbol("__dso_handle");
-
-  auto addDataLayoutSymbol = [&](StringRef s) -> DefinedData * {
-    // Data layout symbols are either defined by lld, or (in the case
-    // of PIC code) defined by the dynamic linker / embedder.
-    if (ctx.isPic) {
-      ctx.arg.allowUndefinedSymbols.insert(s);
-      return nullptr;
-    } else {
-      return symtab->addOptionalDataSymbol(s);
-    }
-  };
-
-  ctx.sym.dataEnd = addDataLayoutSymbol("__data_end");
-  ctx.sym.rodataStart = addDataLayoutSymbol("__rodata_start");
-  ctx.sym.rodataEnd = addDataLayoutSymbol("__rodata_end");
-  ctx.sym.stackLow = addDataLayoutSymbol("__stack_low");
-  ctx.sym.stackHigh = addDataLayoutSymbol("__stack_high");
-  ctx.sym.globalBase = addDataLayoutSymbol("__global_base");
-  ctx.sym.heapBase = addDataLayoutSymbol("__heap_base");
-  ctx.sym.heapEnd = addDataLayoutSymbol("__heap_end");
-
-  // for pic, __memory_base and __table_base are handled in
-  // createSyntheticSymbols.
-  if (!ctx.isPic) {
-    ctx.sym.memoryBase = createOptionalGlobal("__memory_base", false);
-    ctx.sym.tableBase = createOptionalGlobal("__table_base", false);
-  }
-
-  ctx.sym.firstPageEnd = symtab->addOptionalDataSymbol("__wasm_first_page_end");
-  if (ctx.sym.firstPageEnd)
-    ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);
-
-  // TLS object files may be linked into single-threaded programs, so
-  // __tls_base must always be defined. In this case it is immutable and points
-  // directly to the start of the `.tdata` segment. __tls_size and __tls_align
-  // are omitted since they are only used by __wasm_init_tls, which is not
-  // created in this case.
-  if (!ctx.sym.tlsBase)
-    ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
-}
-
 static void processStubLibrariesPreLTO() {
   log("-- processStubLibrariesPreLTO");
   for (auto &stub_file : ctx.stubFiles) {
@@ -1102,6 +1190,8 @@ static bool addStubSymbolDeps(const StubFile *stub_file, Symbol *sym,
   bool depsAdded = false;
   for (const auto dep : deps) {
     auto *needed = symtab->find(dep);
+    if (!needed || needed->isUndefined())
+      needed = resolveStubDependency(dep);
     if (!needed) {
       error(toString(stub_file) + ": undefined symbol: " + dep +
             ". Required by " + toString(*sym));
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 88ac54302c286..5b1b0b3c51507 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -267,10 +267,10 @@ DefinedFunction *SymbolTable::addSyntheticFunction(StringRef name,
 // added if there is an undefine reference to it, or if it is explicitly
 // exported via the --export flag.  Otherwise we don't add the symbol and return
 // nullptr.
-DefinedData *SymbolTable::addOptionalDataSymbol(StringRef name,
-                                                uint64_t value) {
+DefinedData *SymbolTable::addOptionalDataSymbol(StringRef name, uint64_t value,
+                                                bool force) {
   Symbol *s = find(name);
-  if (!s && (ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(name)))
+  if (!s && (force || ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(name)))
     s = insertName(name).first;
   else if (!s || s->isDefined())
     return nullptr;
@@ -301,9 +301,10 @@ DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef name, uint32_t flags,
 }
 
 DefinedGlobal *SymbolTable::addOptionalGlobalSymbol(StringRef name,
-                                                    InputGlobal *global) {
+                                                    InputGlobal *global,
+                                                    bool force) {
   Symbol *s = find(name);
-  if (!s && (ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(name)))
+  if (!s && (force || ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(name)))
     s = insertName(name).first;
   else if (!s || s->isDefined())
     return nullptr;
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index 0667cedecdb4f..f89390e88e6b7 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -97,8 +97,10 @@ class SymbolTable {
                                     InputGlobal *global);
   DefinedFunction *addSyntheticFunction(StringRef name, uint32_t flags,
                                         InputFunction *function);
-  DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0);
-  DefinedGlobal *addOptionalGlobalSymbol(StringRef name, InputGlobal *global);
+  DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0,
+                                     bool force = false);
+  DefinedGlobal *addOptionalGlobalSymbol(StringRef name, InputGlobal *global,
+                                         bool force = false);
   DefinedTable *addSyntheticTable(StringRef name, uint32_t flags,
                                   InputTable *global);
 

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lld-wasm

Author: Gauarv Chaudhary (ANAMASGARD)

Changes

Fixes #180632

Summary

  • Materialize optional linker-created symbols on demand when stub libraries require them
  • Handle both missing and already-undefined stub dependencies (e.g. __heap_base)
  • Preserve exact PIC semantics: data-layout symbols gated on isPic; __dso_handle and __wasm_first_page_end are not

Test plan

  • stub-library-optional-symbol.s (absent/undefined __heap_base, user-defined, __memory_base, PIC __dso_handle, PIC __wasm_first_page_end, PIC __heap_base negative)
  • stub-library.s, stub-library-archive.s, lto/stub-library*.s, data-layout.s, export-optional.s

Full diff: https://github.com/llvm/llvm-project/pull/215112.diff

12 Files Affected:

  • (added) lld/test/wasm/Inputs/libstub-dso-handle.so (+2)
  • (added) lld/test/wasm/Inputs/libstub-first-page-end.so (+2)
  • (added) lld/test/wasm/Inputs/libstub-heap-base.so (+2)
  • (added) lld/test/wasm/Inputs/libstub-memory-base.so (+2)
  • (added) lld/test/wasm/Inputs/stub-optional-absent.s (+8)
  • (added) lld/test/wasm/Inputs/stub-optional-memory.s (+8)
  • (added) lld/test/wasm/Inputs/stub-optional-undef.s (+10)
  • (added) lld/test/wasm/Inputs/stub-optional-user-heap.s (+11)
  • (added) lld/test/wasm/stub-library-optional-symbol.s (+76)
  • (modified) lld/wasm/Driver.cpp (+138-48)
  • (modified) lld/wasm/SymbolTable.cpp (+6-5)
  • (modified) lld/wasm/SymbolTable.h (+4-2)
diff --git a/lld/test/wasm/Inputs/libstub-dso-handle.so b/lld/test/wasm/Inputs/libstub-dso-handle.so
new file mode 100644
index 0000000000000..bb500f885468c
--- /dev/null
+++ b/lld/test/wasm/Inputs/libstub-dso-handle.so
@@ -0,0 +1,2 @@
+#STUB
+foo_import: __dso_handle
diff --git a/lld/test/wasm/Inputs/libstub-first-page-end.so b/lld/test/wasm/Inputs/libstub-first-page-end.so
new file mode 100644
index 0000000000000..55cc723835afb
--- /dev/null
+++ b/lld/test/wasm/Inputs/libstub-first-page-end.so
@@ -0,0 +1,2 @@
+#STUB
+foo_import: __wasm_first_page_end
diff --git a/lld/test/wasm/Inputs/libstub-heap-base.so b/lld/test/wasm/Inputs/libstub-heap-base.so
new file mode 100644
index 0000000000000..d0c2f37f023ff
--- /dev/null
+++ b/lld/test/wasm/Inputs/libstub-heap-base.so
@@ -0,0 +1,2 @@
+#STUB
+foo_import: __heap_base
diff --git a/lld/test/wasm/Inputs/libstub-memory-base.so b/lld/test/wasm/Inputs/libstub-memory-base.so
new file mode 100644
index 0000000000000..740e683da8d46
--- /dev/null
+++ b/lld/test/wasm/Inputs/libstub-memory-base.so
@@ -0,0 +1,2 @@
+#STUB
+foo_import: __memory_base
diff --git a/lld/test/wasm/Inputs/stub-optional-absent.s b/lld/test/wasm/Inputs/stub-optional-absent.s
new file mode 100644
index 0000000000000..4b412cbf91698
--- /dev/null
+++ b/lld/test/wasm/Inputs/stub-optional-absent.s
@@ -0,0 +1,8 @@
+.functype foo () -> ()
+.import_name foo, foo_import
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    call foo
+    end_function
diff --git a/lld/test/wasm/Inputs/stub-optional-memory.s b/lld/test/wasm/Inputs/stub-optional-memory.s
new file mode 100644
index 0000000000000..4b412cbf91698
--- /dev/null
+++ b/lld/test/wasm/Inputs/stub-optional-memory.s
@@ -0,0 +1,8 @@
+.functype foo () -> ()
+.import_name foo, foo_import
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    call foo
+    end_function
diff --git a/lld/test/wasm/Inputs/stub-optional-undef.s b/lld/test/wasm/Inputs/stub-optional-undef.s
new file mode 100644
index 0000000000000..eede83f8dcc35
--- /dev/null
+++ b/lld/test/wasm/Inputs/stub-optional-undef.s
@@ -0,0 +1,10 @@
+.functype foo () -> ()
+.import_name foo, foo_import
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    i32.const __heap_base@GOT
+    drop
+    call foo
+    end_function
diff --git a/lld/test/wasm/Inputs/stub-optional-user-heap.s b/lld/test/wasm/Inputs/stub-optional-user-heap.s
new file mode 100644
index 0000000000000..ed051470deac9
--- /dev/null
+++ b/lld/test/wasm/Inputs/stub-optional-user-heap.s
@@ -0,0 +1,11 @@
+.functype foo () -> ()
+.import_name foo, foo_import
+
+.globaltype __heap_base, i32, immutable
+__heap_base:
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    call foo
+    end_function
diff --git a/lld/test/wasm/stub-library-optional-symbol.s b/lld/test/wasm/stub-library-optional-symbol.s
new file mode 100644
index 0000000000000..d882d5dd660a6
--- /dev/null
+++ b/lld/test/wasm/stub-library-optional-symbol.s
@@ -0,0 +1,76 @@
+## Test stub library dependencies on optional linker-created symbols.
+## See https://github.com/llvm/llvm-project/issues/180632
+
+# --- Case 1: __heap_base completely absent from object (exact #180632 bug) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-absent.o %S/Inputs/stub-optional-absent.s
+# RUN: wasm-ld %t-absent.o %S/Inputs/libstub-heap-base.so -o %t-absent.wasm
+# RUN: obj2yaml %t-absent.wasm | FileCheck %s --check-prefix=CHECK-ABSENT
+
+# --- Case 2: __heap_base already Undefined in object (subtle v2 bug) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-undef.o %S/Inputs/stub-optional-undef.s
+# RUN: wasm-ld %t-undef.o %S/Inputs/libstub-heap-base.so -o %t-undef.wasm
+# RUN: obj2yaml %t-undef.wasm | FileCheck %s --check-prefix=CHECK-UNDEF
+
+# --- Case 3: unknown stub dependency still errors ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %S/Inputs/stub-optional-absent.s
+# RUN: not wasm-ld %t.o %p/Inputs/libstub-missing-dep.so -o %t.wasm 2>&1 | FileCheck %s --check-prefix=CHECK-MISSING
+# CHECK-MISSING: libstub-missing-dep.so: undefined symbol: missing_dep. Required by foo
+
+# --- Case 5: user-defined __heap_base — linker must not override ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-user.o %S/Inputs/stub-optional-user-heap.s
+# RUN: wasm-ld %t-user.o %S/Inputs/libstub-heap-base.so -o %t-user.wasm
+# RUN: obj2yaml %t-user.wasm | FileCheck %s --check-prefix=CHECK-USER
+
+# --- Case 6: __memory_base optional global path (non-PIC) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-mem.o %S/Inputs/stub-optional-memory.s
+# RUN: wasm-ld %t-mem.o %S/Inputs/libstub-memory-base.so -o %t-mem.wasm
+# RUN: obj2yaml %t-mem.wasm | FileCheck %s --check-prefix=CHECK-MEM
+
+# CHECK-ABSENT:         Field:           foo_import
+# CHECK-ABSENT:       - Type:            GLOBAL
+# CHECK-ABSENT:         InitExpr:
+# CHECK-ABSENT-NEXT:      Opcode:          I32_CONST
+# CHECK-ABSENT-NEXT:      Value:           65536
+# CHECK-ABSENT:       - Name:            __heap_base
+# CHECK-ABSENT-NEXT:    Kind:            GLOBAL
+
+# CHECK-UNDEF:         Field:           foo_import
+# CHECK-UNDEF:       - Type:            GLOBAL
+# CHECK-UNDEF:         InitExpr:
+# CHECK-UNDEF-NEXT:      Opcode:          I32_CONST
+# CHECK-UNDEF-NEXT:      Value:           65536
+# CHECK-UNDEF:       - Name:            __heap_base
+# CHECK-UNDEF-NEXT:    Kind:            GLOBAL
+
+# CHECK-USER:       - Name:            __heap_base
+# CHECK-USER-NEXT:    Kind:            GLOBAL
+
+# CHECK-MEM:         Field:           foo_import
+# CHECK-MEM:       - Name:            __memory_base
+# CHECK-MEM-NEXT:    Kind:            GLOBAL
+
+# --- Case 7: PIC __dso_handle stub dependency (must be created) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-pic-dso.o %S/Inputs/stub-optional-absent.s
+# RUN: wasm-ld --experimental-pic -pie --import-memory %t-pic-dso.o %S/Inputs/libstub-dso-handle.so -o %t-pic-dso.wasm
+# RUN: obj2yaml %t-pic-dso.wasm | FileCheck %s --check-prefix=CHECK-PIC-DSO
+
+# --- Case 8: PIC __wasm_first_page_end stub dependency (must be created) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-pic-fpe.o %S/Inputs/stub-optional-absent.s
+# RUN: wasm-ld --experimental-pic -pie --import-memory %t-pic-fpe.o %S/Inputs/libstub-first-page-end.so -o %t-pic-fpe.wasm
+# RUN: obj2yaml %t-pic-fpe.wasm | FileCheck %s --check-prefix=CHECK-PIC-FPE
+
+# --- Case 9: PIC __heap_base stub dependency (must NOT be linker-created) ---
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-pic-heap.o %S/Inputs/stub-optional-absent.s
+# RUN: not wasm-ld --experimental-pic -pie --import-memory %t-pic-heap.o %S/Inputs/libstub-heap-base.so -o %t-pic-heap.wasm 2>&1 | FileCheck %s --check-prefix=CHECK-PIC-HEAP-FAIL
+
+# CHECK-PIC-DSO:         Field:           foo_import
+# CHECK-PIC-DSO:       - Name:            __dso_handle
+# CHECK-PIC-DSO-NEXT:    Kind:            GLOBAL
+
+# CHECK-PIC-FPE:         InitExpr:
+# CHECK-PIC-FPE-NEXT:      Opcode:          I32_CONST
+# CHECK-PIC-FPE-NEXT:      Value:           65536
+# CHECK-PIC-FPE:       - Name:            __wasm_first_page_end
+# CHECK-PIC-FPE-NEXT:    Kind:            GLOBAL
+
+# CHECK-PIC-HEAP-FAIL: undefined symbol: __heap_base
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 740e8878c6e03..3f26c7dcf32d4 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -922,9 +922,143 @@ static DefinedGlobal *createGlobalVariable(StringRef name, bool isMutable,
   return symtab->addSyntheticGlobal(name, flags, g);
 }
 
-static DefinedGlobal *createOptionalGlobal(StringRef name, bool isMutable) {
+static DefinedGlobal *createOptionalGlobal(StringRef name, bool isMutable,
+                                           bool force = false) {
+  if (Symbol *s = symtab->find(name)) {
+    if (s->isDefined())
+      return dyn_cast<DefinedGlobal>(s);
+  }
   InputGlobal *g = createGlobal(name, isMutable);
-  return symtab->addOptionalGlobalSymbol(name, g);
+  return symtab->addOptionalGlobalSymbol(name, g, force);
+}
+
+static DefinedData *materializeOptionalDataSymbol(StringRef name,
+                                                  DefinedData *&slot,
+                                                  bool force) {
+  if (slot)
+    return slot;
+  if (DefinedData *d = symtab->addOptionalDataSymbol(name, 0, force))
+    slot = d;
+  else if (Symbol *s = symtab->find(name))
+    slot = dyn_cast<DefinedData>(s);
+  return slot;
+}
+
+static DefinedData *materializeOptionalDataLayoutSymbol(StringRef name,
+                                                        DefinedData *&slot,
+                                                        bool force) {
+  if (slot)
+    return slot;
+  if (ctx.isPic) {
+    ctx.arg.allowUndefinedSymbols.insert(name);
+    return nullptr;
+  }
+  return materializeOptionalDataSymbol(name, slot, force);
+}
+
+// Materialize a known optional linker-created symbol when required by a stub
+// library dependency. Returns a defined symbol, or nullptr if the name is not
+// a known optional linker symbol (or cannot be created in the current config).
+static Symbol *resolveStubDependency(StringRef name) {
+  if (ctx.arg.relocatable)
+    return nullptr;
+
+  if (name == "__dso_handle") {
+    materializeOptionalDataSymbol(name, ctx.sym.dsoHandle, true);
+    return symtab->find(name);
+  }
+  if (name == "__data_end") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.dataEnd, true);
+    return symtab->find(name);
+  }
+  if (name == "__rodata_start") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.rodataStart, true);
+    return symtab->find(name);
+  }
+  if (name == "__rodata_end") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.rodataEnd, true);
+    return symtab->find(name);
+  }
+  if (name == "__stack_low") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.stackLow, true);
+    return symtab->find(name);
+  }
+  if (name == "__stack_high") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.stackHigh, true);
+    return symtab->find(name);
+  }
+  if (name == "__global_base") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.globalBase, true);
+    return symtab->find(name);
+  }
+  if (name == "__heap_base") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.heapBase, true);
+    return symtab->find(name);
+  }
+  if (name == "__heap_end") {
+    materializeOptionalDataLayoutSymbol(name, ctx.sym.heapEnd, true);
+    return symtab->find(name);
+  }
+  if (name == "__memory_base" && !ctx.isPic) {
+    if (!ctx.sym.memoryBase)
+      ctx.sym.memoryBase = createOptionalGlobal(name, false, true);
+    return symtab->find(name);
+  }
+  if (name == "__table_base" && !ctx.isPic) {
+    if (!ctx.sym.tableBase)
+      ctx.sym.tableBase = createOptionalGlobal(name, false, true);
+    return symtab->find(name);
+  }
+  if (name == "__wasm_first_page_end") {
+    materializeOptionalDataSymbol(name, ctx.sym.firstPageEnd, true);
+    if (ctx.sym.firstPageEnd)
+      ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);
+    return symtab->find(name);
+  }
+  if (name == "__tls_base" && !ctx.sym.tlsBase) {
+    ctx.sym.tlsBase = createOptionalGlobal(name, false, true);
+    return symtab->find(name);
+  }
+  return nullptr;
+}
+
+static void createOptionalSymbols() {
+  if (ctx.arg.relocatable)
+    return;
+
+  if (!ctx.sym.dsoHandle)
+    materializeOptionalDataSymbol("__dso_handle", ctx.sym.dsoHandle, false);
+
+  materializeOptionalDataLayoutSymbol("__data_end", ctx.sym.dataEnd, false);
+  materializeOptionalDataLayoutSymbol("__rodata_start", ctx.sym.rodataStart, false);
+  materializeOptionalDataLayoutSymbol("__rodata_end", ctx.sym.rodataEnd, false);
+  materializeOptionalDataLayoutSymbol("__stack_low", ctx.sym.stackLow, false);
+  materializeOptionalDataLayoutSymbol("__stack_high", ctx.sym.stackHigh, false);
+  materializeOptionalDataLayoutSymbol("__global_base", ctx.sym.globalBase, false);
+  materializeOptionalDataLayoutSymbol("__heap_base", ctx.sym.heapBase, false);
+  materializeOptionalDataLayoutSymbol("__heap_end", ctx.sym.heapEnd, false);
+
+  // for pic, __memory_base and __table_base are handled in
+  // createSyntheticSymbols.
+  if (!ctx.isPic) {
+    if (!ctx.sym.memoryBase)
+      ctx.sym.memoryBase = createOptionalGlobal("__memory_base", false);
+    if (!ctx.sym.tableBase)
+      ctx.sym.tableBase = createOptionalGlobal("__table_base", false);
+  }
+
+  if (!ctx.sym.firstPageEnd)
+    materializeOptionalDataSymbol("__wasm_first_page_end", ctx.sym.firstPageEnd, false);
+  if (ctx.sym.firstPageEnd)
+    ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);
+
+  // TLS object files may be linked into single-threaded programs, so
+  // __tls_base must always be defined. In this case it is immutable and points
+  // directly to the start of the `.tdata` segment. __tls_size and __tls_align
+  // are omitted since they are only used by __wasm_init_tls, which is not
+  // created in this case.
+  if (!ctx.sym.tlsBase)
+    ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
 }
 
 // Create ABI-defined synthetic symbols
@@ -1008,52 +1142,6 @@ static void createSyntheticSymbols() {
   }
 }
 
-static void createOptionalSymbols() {
-  if (ctx.arg.relocatable)
-    return;
-
-  ctx.sym.dsoHandle = symtab->addOptionalDataSymbol("__dso_handle");
-
-  auto addDataLayoutSymbol = [&](StringRef s) -> DefinedData * {
-    // Data layout symbols are either defined by lld, or (in the case
-    // of PIC code) defined by the dynamic linker / embedder.
-    if (ctx.isPic) {
-      ctx.arg.allowUndefinedSymbols.insert(s);
-      return nullptr;
-    } else {
-      return symtab->addOptionalDataSymbol(s);
-    }
-  };
-
-  ctx.sym.dataEnd = addDataLayoutSymbol("__data_end");
-  ctx.sym.rodataStart = addDataLayoutSymbol("__rodata_start");
-  ctx.sym.rodataEnd = addDataLayoutSymbol("__rodata_end");
-  ctx.sym.stackLow = addDataLayoutSymbol("__stack_low");
-  ctx.sym.stackHigh = addDataLayoutSymbol("__stack_high");
-  ctx.sym.globalBase = addDataLayoutSymbol("__global_base");
-  ctx.sym.heapBase = addDataLayoutSymbol("__heap_base");
-  ctx.sym.heapEnd = addDataLayoutSymbol("__heap_end");
-
-  // for pic, __memory_base and __table_base are handled in
-  // createSyntheticSymbols.
-  if (!ctx.isPic) {
-    ctx.sym.memoryBase = createOptionalGlobal("__memory_base", false);
-    ctx.sym.tableBase = createOptionalGlobal("__table_base", false);
-  }
-
-  ctx.sym.firstPageEnd = symtab->addOptionalDataSymbol("__wasm_first_page_end");
-  if (ctx.sym.firstPageEnd)
-    ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);
-
-  // TLS object files may be linked into single-threaded programs, so
-  // __tls_base must always be defined. In this case it is immutable and points
-  // directly to the start of the `.tdata` segment. __tls_size and __tls_align
-  // are omitted since they are only used by __wasm_init_tls, which is not
-  // created in this case.
-  if (!ctx.sym.tlsBase)
-    ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
-}
-
 static void processStubLibrariesPreLTO() {
   log("-- processStubLibrariesPreLTO");
   for (auto &stub_file : ctx.stubFiles) {
@@ -1102,6 +1190,8 @@ static bool addStubSymbolDeps(const StubFile *stub_file, Symbol *sym,
   bool depsAdded = false;
   for (const auto dep : deps) {
     auto *needed = symtab->find(dep);
+    if (!needed || needed->isUndefined())
+      needed = resolveStubDependency(dep);
     if (!needed) {
       error(toString(stub_file) + ": undefined symbol: " + dep +
             ". Required by " + toString(*sym));
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 88ac54302c286..5b1b0b3c51507 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -267,10 +267,10 @@ DefinedFunction *SymbolTable::addSyntheticFunction(StringRef name,
 // added if there is an undefine reference to it, or if it is explicitly
 // exported via the --export flag.  Otherwise we don't add the symbol and return
 // nullptr.
-DefinedData *SymbolTable::addOptionalDataSymbol(StringRef name,
-                                                uint64_t value) {
+DefinedData *SymbolTable::addOptionalDataSymbol(StringRef name, uint64_t value,
+                                                bool force) {
   Symbol *s = find(name);
-  if (!s && (ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(name)))
+  if (!s && (force || ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(name)))
     s = insertName(name).first;
   else if (!s || s->isDefined())
     return nullptr;
@@ -301,9 +301,10 @@ DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef name, uint32_t flags,
 }
 
 DefinedGlobal *SymbolTable::addOptionalGlobalSymbol(StringRef name,
-                                                    InputGlobal *global) {
+                                                    InputGlobal *global,
+                                                    bool force) {
   Symbol *s = find(name);
-  if (!s && (ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(name)))
+  if (!s && (force || ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(name)))
     s = insertName(name).first;
   else if (!s || s->isDefined())
     return nullptr;
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index 0667cedecdb4f..f89390e88e6b7 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -97,8 +97,10 @@ class SymbolTable {
                                     InputGlobal *global);
   DefinedFunction *addSyntheticFunction(StringRef name, uint32_t flags,
                                         InputFunction *function);
-  DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0);
-  DefinedGlobal *addOptionalGlobalSymbol(StringRef name, InputGlobal *global);
+  DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0,
+                                     bool force = false);
+  DefinedGlobal *addOptionalGlobalSymbol(StringRef name, InputGlobal *global,
+                                         bool force = false);
   DefinedTable *addSyntheticTable(StringRef name, uint32_t flags,
                                   InputTable *global);
 

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@@ -0,0 +1,11 @@
.functype foo () -> ()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than adding all these small Input files you can put them all in test file and use the split-file tool.

Stub library dependency resolution runs before createOptionalSymbols()
and rejects missing or undefined deps. Materialize known optional
linker-created symbols on demand when a stub library requires them.
Fixes llvm#180632

Signed-off-by: Gaurav Chaudhary <chaudharygaurav2004@gmail.com>
Split materializeOptionalDataSymbol into generic and PIC-gated
data-layout helpers so __dso_handle and __wasm_first_page_end are
created in PIC mode while __heap_base and other data-layout symbols
are not. Add PIC stub regression tests.
Part of fix for llvm#180632

Signed-off-by: Gaurav Chaudhary <chaudharygaurav2004@gmail.com>
Remove ctx.sym.* fallback to user-defined symbols. Consolidate tests
into split-file layout. Add __tls_base and __start_foo probe cases.
Apply clang-format.

Addresses llvm#180632

Signed-off-by: Gaurav Chaudhary <chaudharygaurav2004@gmail.com>
@ANAMASGARD
ANAMASGARD force-pushed the fix/wasm-ld-stub-optional-symbols branch from 5f5bd0c to 6057148 Compare August 10, 2026 04:15
@ANAMASGARD
ANAMASGARD requested a review from sbc100 August 10, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants