Skip to content

Commit 9cbbb74

Browse files
[wasm-ld] Refactor WasmSym from static globals to per-link context (#134970)
Towards ##134809 (comment) This change moves WasmSym from a static global struct to an instance owned by Ctx, allowing it to be reset cleanly between linker runs. This enables safe support for multiple invocations of wasm-ld within the same process Changes done - Converted WasmSym from a static struct to a regular struct with instance members. - Added a std::unique_ptr<WasmSym> wasmSym field inside Ctx. - Reset wasmSym in Ctx::reset() to clear state between links. - Replaced all WasmSym:: references with ctx.wasmSym->. - Removed global symbol definitions from Symbols.cpp that are no longer needed. Clearing wasmSym in ctx.reset() ensures a clean slate for each link invocation, preventing symbol leakage across runs—critical when using wasm-ld/lld as a reentrant library where global state can cause subtle, hard-to-debug errors. --------- Co-authored-by: Vassil Vassilev <v.g.vassilev@gmail.com>
1 parent db53dfc commit 9cbbb74

9 files changed

+260
-283
lines changed

lld/wasm/Config.h

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class InputTable;
3232
class InputGlobal;
3333
class InputFunction;
3434
class Symbol;
35+
class DefinedData;
36+
class GlobalSymbol;
37+
class DefinedFunction;
38+
class UndefinedGlobal;
39+
class TableSymbol;
3540

3641
// For --unresolved-symbols.
3742
enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic };
@@ -141,6 +146,111 @@ struct Ctx {
141146
llvm::SmallVector<InputGlobal *, 0> syntheticGlobals;
142147
llvm::SmallVector<InputTable *, 0> syntheticTables;
143148

149+
// linker-generated symbols
150+
struct WasmSym {
151+
// __global_base
152+
// Symbol marking the start of the global section.
153+
DefinedData *globalBase;
154+
155+
// __stack_pointer/__stack_low/__stack_high
156+
// Global that holds current value of stack pointer and data symbols marking
157+
// the start and end of the stack region. stackPointer is initialized to
158+
// stackHigh and grows downwards towards stackLow
159+
GlobalSymbol *stackPointer;
160+
DefinedData *stackLow;
161+
DefinedData *stackHigh;
162+
163+
// __tls_base
164+
// Global that holds the address of the base of the current thread's
165+
// TLS block.
166+
GlobalSymbol *tlsBase;
167+
168+
// __tls_size
169+
// Symbol whose value is the size of the TLS block.
170+
GlobalSymbol *tlsSize;
171+
172+
// __tls_size
173+
// Symbol whose value is the alignment of the TLS block.
174+
GlobalSymbol *tlsAlign;
175+
176+
// __data_end
177+
// Symbol marking the end of the data and bss.
178+
DefinedData *dataEnd;
179+
180+
// __heap_base/__heap_end
181+
// Symbols marking the beginning and end of the "heap". It starts at the end
182+
// of the data, bss and explicit stack, and extends to the end of the linear
183+
// memory allocated by wasm-ld. This region of memory is not used by the
184+
// linked code, so it may be used as a backing store for `sbrk` or `malloc`
185+
// implementations.
186+
DefinedData *heapBase;
187+
DefinedData *heapEnd;
188+
189+
// __wasm_first_page_end
190+
// A symbol whose address is the end of the first page in memory (if any).
191+
DefinedData *firstPageEnd;
192+
193+
// __wasm_init_memory_flag
194+
// Symbol whose contents are nonzero iff memory has already been
195+
// initialized.
196+
DefinedData *initMemoryFlag;
197+
198+
// __wasm_init_memory
199+
// Function that initializes passive data segments during instantiation.
200+
DefinedFunction *initMemory;
201+
202+
// __wasm_call_ctors
203+
// Function that directly calls all ctors in priority order.
204+
DefinedFunction *callCtors;
205+
206+
// __wasm_call_dtors
207+
// Function that calls the libc/etc. cleanup function.
208+
DefinedFunction *callDtors;
209+
210+
// __wasm_apply_global_relocs
211+
// Function that applies relocations to wasm globals post-instantiation.
212+
// Unlike __wasm_apply_data_relocs this needs to run on every thread.
213+
DefinedFunction *applyGlobalRelocs;
214+
215+
// __wasm_apply_tls_relocs
216+
// Like __wasm_apply_data_relocs but for TLS section. These must be
217+
// delayed until __wasm_init_tls.
218+
DefinedFunction *applyTLSRelocs;
219+
220+
// __wasm_apply_global_tls_relocs
221+
// Like applyGlobalRelocs but for globals that hold TLS addresses. These
222+
// must be delayed until __wasm_init_tls.
223+
DefinedFunction *applyGlobalTLSRelocs;
224+
225+
// __wasm_init_tls
226+
// Function that allocates thread-local storage and initializes it.
227+
DefinedFunction *initTLS;
228+
229+
// Pointer to the function that is to be used in the start section.
230+
// (normally an alias of initMemory, or applyGlobalRelocs).
231+
DefinedFunction *startFunction;
232+
233+
// __dso_handle
234+
// Symbol used in calls to __cxa_atexit to determine current DLL
235+
DefinedData *dsoHandle;
236+
237+
// __table_base
238+
// Used in PIC code for offset of indirect function table
239+
UndefinedGlobal *tableBase;
240+
DefinedData *definedTableBase;
241+
242+
// __memory_base
243+
// Used in PIC code for offset of global data
244+
UndefinedGlobal *memoryBase;
245+
DefinedData *definedMemoryBase;
246+
247+
// __indirect_function_table
248+
// Used as an address space for function pointers, with each function that
249+
// is used as a function pointer being allocated a slot.
250+
TableSymbol *indirectFunctionTable;
251+
};
252+
WasmSym sym;
253+
144254
// True if we are creating position-independent code.
145255
bool isPic = false;
146256

lld/wasm/Driver.cpp

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ void Ctx::reset() {
7070
isPic = false;
7171
legacyFunctionTable = false;
7272
emitBssSegments = false;
73+
sym = WasmSym{};
7374
}
7475

7576
namespace {
@@ -945,14 +946,14 @@ static void createSyntheticSymbols() {
945946
true};
946947
static llvm::wasm::WasmGlobalType mutableGlobalTypeI64 = {WASM_TYPE_I64,
947948
true};
948-
WasmSym::callCtors = symtab->addSyntheticFunction(
949+
ctx.sym.callCtors = symtab->addSyntheticFunction(
949950
"__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
950951
make<SyntheticFunction>(nullSignature, "__wasm_call_ctors"));
951952

952953
bool is64 = ctx.arg.is64.value_or(false);
953954

954955
if (ctx.isPic) {
955-
WasmSym::stackPointer =
956+
ctx.sym.stackPointer =
956957
createUndefinedGlobal("__stack_pointer", ctx.arg.is64.value_or(false)
957958
? &mutableGlobalTypeI64
958959
: &mutableGlobalTypeI32);
@@ -962,51 +963,49 @@ static void createSyntheticSymbols() {
962963
// See:
963964
// https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md
964965
auto *globalType = is64 ? &globalTypeI64 : &globalTypeI32;
965-
WasmSym::memoryBase = createUndefinedGlobal("__memory_base", globalType);
966-
WasmSym::tableBase = createUndefinedGlobal("__table_base", globalType);
967-
WasmSym::memoryBase->markLive();
968-
WasmSym::tableBase->markLive();
966+
ctx.sym.memoryBase = createUndefinedGlobal("__memory_base", globalType);
967+
ctx.sym.tableBase = createUndefinedGlobal("__table_base", globalType);
968+
ctx.sym.memoryBase->markLive();
969+
ctx.sym.tableBase->markLive();
969970
} else {
970971
// For non-PIC code
971-
WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true);
972-
WasmSym::stackPointer->markLive();
972+
ctx.sym.stackPointer = createGlobalVariable("__stack_pointer", true);
973+
ctx.sym.stackPointer->markLive();
973974
}
974975

975976
if (ctx.arg.sharedMemory) {
976-
WasmSym::tlsBase = createGlobalVariable("__tls_base", true);
977-
WasmSym::tlsSize = createGlobalVariable("__tls_size", false);
978-
WasmSym::tlsAlign = createGlobalVariable("__tls_align", false);
979-
WasmSym::initTLS = symtab->addSyntheticFunction(
977+
ctx.sym.tlsBase = createGlobalVariable("__tls_base", true);
978+
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false);
979+
ctx.sym.tlsAlign = createGlobalVariable("__tls_align", false);
980+
ctx.sym.initTLS = symtab->addSyntheticFunction(
980981
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
981-
make<SyntheticFunction>(
982-
is64 ? i64ArgSignature : i32ArgSignature,
983-
"__wasm_init_tls"));
982+
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
983+
"__wasm_init_tls"));
984984
}
985985
}
986986

987987
static void createOptionalSymbols() {
988988
if (ctx.arg.relocatable)
989989
return;
990990

991-
WasmSym::dsoHandle = symtab->addOptionalDataSymbol("__dso_handle");
991+
ctx.sym.dsoHandle = symtab->addOptionalDataSymbol("__dso_handle");
992992

993993
if (!ctx.arg.shared)
994-
WasmSym::dataEnd = symtab->addOptionalDataSymbol("__data_end");
994+
ctx.sym.dataEnd = symtab->addOptionalDataSymbol("__data_end");
995995

996996
if (!ctx.isPic) {
997-
WasmSym::stackLow = symtab->addOptionalDataSymbol("__stack_low");
998-
WasmSym::stackHigh = symtab->addOptionalDataSymbol("__stack_high");
999-
WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base");
1000-
WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base");
1001-
WasmSym::heapEnd = symtab->addOptionalDataSymbol("__heap_end");
1002-
WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
1003-
WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
997+
ctx.sym.stackLow = symtab->addOptionalDataSymbol("__stack_low");
998+
ctx.sym.stackHigh = symtab->addOptionalDataSymbol("__stack_high");
999+
ctx.sym.globalBase = symtab->addOptionalDataSymbol("__global_base");
1000+
ctx.sym.heapBase = symtab->addOptionalDataSymbol("__heap_base");
1001+
ctx.sym.heapEnd = symtab->addOptionalDataSymbol("__heap_end");
1002+
ctx.sym.definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
1003+
ctx.sym.definedTableBase = symtab->addOptionalDataSymbol("__table_base");
10041004
}
10051005

1006-
WasmSym::firstPageEnd =
1007-
symtab->addOptionalDataSymbol("__wasm_first_page_end");
1008-
if (WasmSym::firstPageEnd)
1009-
WasmSym::firstPageEnd->setVA(ctx.arg.pageSize);
1006+
ctx.sym.firstPageEnd = symtab->addOptionalDataSymbol("__wasm_first_page_end");
1007+
if (ctx.sym.firstPageEnd)
1008+
ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);
10101009

10111010
// For non-shared memory programs we still need to define __tls_base since we
10121011
// allow object files built with TLS to be linked into single threaded
@@ -1018,7 +1017,7 @@ static void createOptionalSymbols() {
10181017
// __tls_size and __tls_align are not needed in this case since they are only
10191018
// needed for __wasm_init_tls (which we do not create in this case).
10201019
if (!ctx.arg.sharedMemory)
1021-
WasmSym::tlsBase = createOptionalGlobal("__tls_base", false);
1020+
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
10221021
}
10231022

10241023
static void processStubLibrariesPreLTO() {
@@ -1393,9 +1392,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
13931392
// by libc/etc., because destructors are registered dynamically with
13941393
// `__cxa_atexit` and friends.
13951394
if (!ctx.arg.relocatable && !ctx.arg.shared &&
1396-
!WasmSym::callCtors->isUsedInRegularObj &&
1397-
WasmSym::callCtors->getName() != ctx.arg.entry &&
1398-
!ctx.arg.exportedSymbols.count(WasmSym::callCtors->getName())) {
1395+
!ctx.sym.callCtors->isUsedInRegularObj &&
1396+
ctx.sym.callCtors->getName() != ctx.arg.entry &&
1397+
!ctx.arg.exportedSymbols.count(ctx.sym.callCtors->getName())) {
13991398
if (Symbol *callDtors =
14001399
handleUndefined("__wasm_call_dtors", "<internal>")) {
14011400
if (auto *callDtorsFunc = dyn_cast<DefinedFunction>(callDtors)) {
@@ -1404,7 +1403,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
14041403
!callDtorsFunc->signature->Returns.empty())) {
14051404
error("__wasm_call_dtors must have no argument or return values");
14061405
}
1407-
WasmSym::callDtors = callDtorsFunc;
1406+
ctx.sym.callDtors = callDtorsFunc;
14081407
} else {
14091408
error("__wasm_call_dtors must be a function");
14101409
}
@@ -1497,7 +1496,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
14971496
markLive();
14981497

14991498
// Provide the indirect function table if needed.
1500-
WasmSym::indirectFunctionTable =
1499+
ctx.sym.indirectFunctionTable =
15011500
symtab->resolveIndirectFunctionTable(/*required =*/false);
15021501

15031502
if (errorCount())

lld/wasm/InputChunks.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,9 @@ bool InputChunk::generateRelocationCode(raw_ostream &os) const {
411411
if (ctx.isPic) {
412412
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
413413
if (isTLS())
414-
writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "tls_base");
414+
writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "tls_base");
415415
else
416-
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
416+
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "memory_base");
417417
writeU8(os, opcode_ptr_add, "ADD");
418418
}
419419

@@ -436,12 +436,12 @@ bool InputChunk::generateRelocationCode(raw_ostream &os) const {
436436
}
437437
} else {
438438
assert(ctx.isPic);
439-
const GlobalSymbol* baseSymbol = WasmSym::memoryBase;
439+
const GlobalSymbol *baseSymbol = ctx.sym.memoryBase;
440440
if (rel.Type == R_WASM_TABLE_INDEX_I32 ||
441441
rel.Type == R_WASM_TABLE_INDEX_I64)
442-
baseSymbol = WasmSym::tableBase;
442+
baseSymbol = ctx.sym.tableBase;
443443
else if (sym->isTLS())
444-
baseSymbol = WasmSym::tlsBase;
444+
baseSymbol = ctx.sym.tlsBase;
445445
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
446446
writeUleb128(os, baseSymbol->getGlobalIndex(), "base");
447447
writeU8(os, opcode_reloc_const, "CONST");

lld/wasm/MarkLive.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ void MarkLive::run() {
114114
if (sym->isNoStrip() || sym->isExported())
115115
enqueue(sym);
116116

117-
if (WasmSym::callDtors)
118-
enqueue(WasmSym::callDtors);
117+
if (ctx.sym.callDtors)
118+
enqueue(ctx.sym.callDtors);
119119

120120
for (const ObjFile *obj : ctx.objectFiles)
121121
if (obj->isLive()) {
@@ -131,7 +131,7 @@ void MarkLive::run() {
131131
// If we have any non-discarded init functions, mark `__wasm_call_ctors` as
132132
// live so that we assign it an index and call it.
133133
if (isCallCtorsLive())
134-
WasmSym::callCtors->markLive();
134+
ctx.sym.callCtors->markLive();
135135
}
136136

137137
void MarkLive::mark() {

lld/wasm/OutputSections.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ void DataSection::finalizeContents() {
124124
if ((segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0) {
125125
if (ctx.isPic && ctx.arg.extendedConst) {
126126
writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get");
127-
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
127+
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(),
128128
"literal (global index)");
129129
if (segment->startVA) {
130130
writePtrConst(os, segment->startVA, is64, "offset");
@@ -137,7 +137,7 @@ void DataSection::finalizeContents() {
137137
if (ctx.isPic) {
138138
assert(segment->startVA == 0);
139139
initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET;
140-
initExpr.Inst.Value.Global = WasmSym::memoryBase->getGlobalIndex();
140+
initExpr.Inst.Value.Global = ctx.sym.memoryBase->getGlobalIndex();
141141
} else {
142142
initExpr = intConst(segment->startVA, is64);
143143
}

lld/wasm/Symbols.cpp

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -77,32 +77,6 @@ std::string toString(wasm::Symbol::Kind kind) {
7777
}
7878

7979
namespace wasm {
80-
DefinedFunction *WasmSym::callCtors;
81-
DefinedFunction *WasmSym::callDtors;
82-
DefinedFunction *WasmSym::initMemory;
83-
DefinedFunction *WasmSym::applyGlobalRelocs;
84-
DefinedFunction *WasmSym::applyTLSRelocs;
85-
DefinedFunction *WasmSym::applyGlobalTLSRelocs;
86-
DefinedFunction *WasmSym::initTLS;
87-
DefinedData *WasmSym::firstPageEnd;
88-
DefinedFunction *WasmSym::startFunction;
89-
DefinedData *WasmSym::dsoHandle;
90-
DefinedData *WasmSym::dataEnd;
91-
DefinedData *WasmSym::globalBase;
92-
DefinedData *WasmSym::heapBase;
93-
DefinedData *WasmSym::heapEnd;
94-
DefinedData *WasmSym::initMemoryFlag;
95-
GlobalSymbol *WasmSym::stackPointer;
96-
DefinedData *WasmSym::stackLow;
97-
DefinedData *WasmSym::stackHigh;
98-
GlobalSymbol *WasmSym::tlsBase;
99-
GlobalSymbol *WasmSym::tlsSize;
100-
GlobalSymbol *WasmSym::tlsAlign;
101-
UndefinedGlobal *WasmSym::tableBase;
102-
DefinedData *WasmSym::definedTableBase;
103-
UndefinedGlobal *WasmSym::memoryBase;
104-
DefinedData *WasmSym::definedMemoryBase;
105-
TableSymbol *WasmSym::indirectFunctionTable;
10680

10781
WasmSymbolType Symbol::getWasmType() const {
10882
if (isa<FunctionSymbol>(this))

0 commit comments

Comments
 (0)