[lld][wasm] Allow stub libraries to depend on optional linker symbols - #215112
Open
ANAMASGARD wants to merge 3 commits into
Open
[lld][wasm] Allow stub libraries to depend on optional linker symbols#215112ANAMASGARD wants to merge 3 commits into
ANAMASGARD wants to merge 3 commits into
Conversation
|
@llvm/pr-subscribers-lld Author: Gauarv Chaudhary (ANAMASGARD) ChangesFixes #180632 Summary
Test plan
Full diff: https://github.com/llvm/llvm-project/pull/215112.diff 12 Files Affected:
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);
|
|
@llvm/pr-subscribers-lld-wasm Author: Gauarv Chaudhary (ANAMASGARD) ChangesFixes #180632 Summary
Test plan
Full diff: https://github.com/llvm/llvm-project/pull/215112.diff 12 Files Affected:
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);
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
sbc100
reviewed
Aug 9, 2026
| @@ -0,0 +1,11 @@ | |||
| .functype foo () -> () | |||
Contributor
There was a problem hiding this comment.
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
force-pushed
the
fix/wasm-ld-stub-optional-symbols
branch
from
August 10, 2026 04:15
5f5bd0c to
6057148
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test plan
Addresses #180632
Note: _start/_stop stub deps are not yet supported (case 11 documents current limitation).