Skip to content

[wasm-ld] Refactor WasmSym from static globals to per-link context #134970

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 25, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions lld/wasm/Config.h
Original file line number Diff line number Diff line change
@@ -32,6 +32,11 @@ class InputTable;
class InputGlobal;
class InputFunction;
class Symbol;
class DefinedData;
class GlobalSymbol;
class DefinedFunction;
class UndefinedGlobal;
class TableSymbol;

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

// linker-generated symbols
struct WasmSym {
// __global_base
// Symbol marking the start of the global section.
DefinedData *globalBase;

// __stack_pointer/__stack_low/__stack_high
// Global that holds current value of stack pointer and data symbols marking
// the start and end of the stack region. stackPointer is initialized to
// stackHigh and grows downwards towards stackLow
GlobalSymbol *stackPointer;
DefinedData *stackLow;
DefinedData *stackHigh;

// __tls_base
// Global that holds the address of the base of the current thread's
// TLS block.
GlobalSymbol *tlsBase;

// __tls_size
// Symbol whose value is the size of the TLS block.
GlobalSymbol *tlsSize;

// __tls_size
// Symbol whose value is the alignment of the TLS block.
GlobalSymbol *tlsAlign;

// __data_end
// Symbol marking the end of the data and bss.
DefinedData *dataEnd;

// __heap_base/__heap_end
// Symbols marking the beginning and end of the "heap". It starts at the end
// of the data, bss and explicit stack, and extends to the end of the linear
// memory allocated by wasm-ld. This region of memory is not used by the
// linked code, so it may be used as a backing store for `sbrk` or `malloc`
// implementations.
DefinedData *heapBase;
DefinedData *heapEnd;

// __wasm_first_page_end
// A symbol whose address is the end of the first page in memory (if any).
DefinedData *firstPageEnd;

// __wasm_init_memory_flag
// Symbol whose contents are nonzero iff memory has already been
// initialized.
DefinedData *initMemoryFlag;

// __wasm_init_memory
// Function that initializes passive data segments during instantiation.
DefinedFunction *initMemory;

// __wasm_call_ctors
// Function that directly calls all ctors in priority order.
DefinedFunction *callCtors;

// __wasm_call_dtors
// Function that calls the libc/etc. cleanup function.
DefinedFunction *callDtors;

// __wasm_apply_global_relocs
// Function that applies relocations to wasm globals post-instantiation.
// Unlike __wasm_apply_data_relocs this needs to run on every thread.
DefinedFunction *applyGlobalRelocs;

// __wasm_apply_tls_relocs
// Like __wasm_apply_data_relocs but for TLS section. These must be
// delayed until __wasm_init_tls.
DefinedFunction *applyTLSRelocs;

// __wasm_apply_global_tls_relocs
// Like applyGlobalRelocs but for globals that hold TLS addresses. These
// must be delayed until __wasm_init_tls.
DefinedFunction *applyGlobalTLSRelocs;

// __wasm_init_tls
// Function that allocates thread-local storage and initializes it.
DefinedFunction *initTLS;

// Pointer to the function that is to be used in the start section.
// (normally an alias of initMemory, or applyGlobalRelocs).
DefinedFunction *startFunction;

// __dso_handle
// Symbol used in calls to __cxa_atexit to determine current DLL
DefinedData *dsoHandle;

// __table_base
// Used in PIC code for offset of indirect function table
UndefinedGlobal *tableBase;
DefinedData *definedTableBase;

// __memory_base
// Used in PIC code for offset of global data
UndefinedGlobal *memoryBase;
DefinedData *definedMemoryBase;

// __indirect_function_table
// Used as an address space for function pointers, with each function that
// is used as a function pointer being allocated a slot.
TableSymbol *indirectFunctionTable;
};
WasmSym sym;

// True if we are creating position-independent code.
bool isPic = false;

67 changes: 33 additions & 34 deletions lld/wasm/Driver.cpp
Original file line number Diff line number Diff line change
@@ -70,6 +70,7 @@ void Ctx::reset() {
isPic = false;
legacyFunctionTable = false;
emitBssSegments = false;
sym = WasmSym{};
}

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

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

if (ctx.isPic) {
WasmSym::stackPointer =
ctx.sym.stackPointer =
createUndefinedGlobal("__stack_pointer", ctx.arg.is64.value_or(false)
? &mutableGlobalTypeI64
: &mutableGlobalTypeI32);
@@ -962,51 +963,49 @@ static void createSyntheticSymbols() {
// See:
// https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md
auto *globalType = is64 ? &globalTypeI64 : &globalTypeI32;
WasmSym::memoryBase = createUndefinedGlobal("__memory_base", globalType);
WasmSym::tableBase = createUndefinedGlobal("__table_base", globalType);
WasmSym::memoryBase->markLive();
WasmSym::tableBase->markLive();
ctx.sym.memoryBase = createUndefinedGlobal("__memory_base", globalType);
ctx.sym.tableBase = createUndefinedGlobal("__table_base", globalType);
ctx.sym.memoryBase->markLive();
ctx.sym.tableBase->markLive();
} else {
// For non-PIC code
WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true);
WasmSym::stackPointer->markLive();
ctx.sym.stackPointer = createGlobalVariable("__stack_pointer", true);
ctx.sym.stackPointer->markLive();
}

if (ctx.arg.sharedMemory) {
WasmSym::tlsBase = createGlobalVariable("__tls_base", true);
WasmSym::tlsSize = createGlobalVariable("__tls_size", false);
WasmSym::tlsAlign = createGlobalVariable("__tls_align", false);
WasmSym::initTLS = symtab->addSyntheticFunction(
ctx.sym.tlsBase = createGlobalVariable("__tls_base", true);
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false);
ctx.sym.tlsAlign = createGlobalVariable("__tls_align", false);
ctx.sym.initTLS = symtab->addSyntheticFunction(
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(
is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
}
}

static void createOptionalSymbols() {
if (ctx.arg.relocatable)
return;

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

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

if (!ctx.isPic) {
WasmSym::stackLow = symtab->addOptionalDataSymbol("__stack_low");
WasmSym::stackHigh = symtab->addOptionalDataSymbol("__stack_high");
WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base");
WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base");
WasmSym::heapEnd = symtab->addOptionalDataSymbol("__heap_end");
WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
ctx.sym.stackLow = symtab->addOptionalDataSymbol("__stack_low");
ctx.sym.stackHigh = symtab->addOptionalDataSymbol("__stack_high");
ctx.sym.globalBase = symtab->addOptionalDataSymbol("__global_base");
ctx.sym.heapBase = symtab->addOptionalDataSymbol("__heap_base");
ctx.sym.heapEnd = symtab->addOptionalDataSymbol("__heap_end");
ctx.sym.definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
ctx.sym.definedTableBase = symtab->addOptionalDataSymbol("__table_base");
}

WasmSym::firstPageEnd =
symtab->addOptionalDataSymbol("__wasm_first_page_end");
if (WasmSym::firstPageEnd)
WasmSym::firstPageEnd->setVA(ctx.arg.pageSize);
ctx.sym.firstPageEnd = symtab->addOptionalDataSymbol("__wasm_first_page_end");
if (ctx.sym.firstPageEnd)
ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);

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

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

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

if (errorCount())
10 changes: 5 additions & 5 deletions lld/wasm/InputChunks.cpp
Original file line number Diff line number Diff line change
@@ -411,9 +411,9 @@ bool InputChunk::generateRelocationCode(raw_ostream &os) const {
if (ctx.isPic) {
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
if (isTLS())
writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "tls_base");
writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "tls_base");
else
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "memory_base");
writeU8(os, opcode_ptr_add, "ADD");
}

@@ -436,12 +436,12 @@ bool InputChunk::generateRelocationCode(raw_ostream &os) const {
}
} else {
assert(ctx.isPic);
const GlobalSymbol* baseSymbol = WasmSym::memoryBase;
const GlobalSymbol *baseSymbol = ctx.sym.memoryBase;
if (rel.Type == R_WASM_TABLE_INDEX_I32 ||
rel.Type == R_WASM_TABLE_INDEX_I64)
baseSymbol = WasmSym::tableBase;
baseSymbol = ctx.sym.tableBase;
else if (sym->isTLS())
baseSymbol = WasmSym::tlsBase;
baseSymbol = ctx.sym.tlsBase;
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, baseSymbol->getGlobalIndex(), "base");
writeU8(os, opcode_reloc_const, "CONST");
6 changes: 3 additions & 3 deletions lld/wasm/MarkLive.cpp
Original file line number Diff line number Diff line change
@@ -114,8 +114,8 @@ void MarkLive::run() {
if (sym->isNoStrip() || sym->isExported())
enqueue(sym);

if (WasmSym::callDtors)
enqueue(WasmSym::callDtors);
if (ctx.sym.callDtors)
enqueue(ctx.sym.callDtors);

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

void MarkLive::mark() {
4 changes: 2 additions & 2 deletions lld/wasm/OutputSections.cpp
Original file line number Diff line number Diff line change
@@ -124,7 +124,7 @@ void DataSection::finalizeContents() {
if ((segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0) {
if (ctx.isPic && ctx.arg.extendedConst) {
writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get");
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(),
"literal (global index)");
if (segment->startVA) {
writePtrConst(os, segment->startVA, is64, "offset");
@@ -137,7 +137,7 @@ void DataSection::finalizeContents() {
if (ctx.isPic) {
assert(segment->startVA == 0);
initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET;
initExpr.Inst.Value.Global = WasmSym::memoryBase->getGlobalIndex();
initExpr.Inst.Value.Global = ctx.sym.memoryBase->getGlobalIndex();
} else {
initExpr = intConst(segment->startVA, is64);
}
26 changes: 0 additions & 26 deletions lld/wasm/Symbols.cpp
Original file line number Diff line number Diff line change
@@ -77,32 +77,6 @@ std::string toString(wasm::Symbol::Kind kind) {
}

namespace wasm {
DefinedFunction *WasmSym::callCtors;
DefinedFunction *WasmSym::callDtors;
DefinedFunction *WasmSym::initMemory;
DefinedFunction *WasmSym::applyGlobalRelocs;
DefinedFunction *WasmSym::applyTLSRelocs;
DefinedFunction *WasmSym::applyGlobalTLSRelocs;
DefinedFunction *WasmSym::initTLS;
DefinedData *WasmSym::firstPageEnd;
DefinedFunction *WasmSym::startFunction;
DefinedData *WasmSym::dsoHandle;
DefinedData *WasmSym::dataEnd;
DefinedData *WasmSym::globalBase;
DefinedData *WasmSym::heapBase;
DefinedData *WasmSym::heapEnd;
DefinedData *WasmSym::initMemoryFlag;
GlobalSymbol *WasmSym::stackPointer;
DefinedData *WasmSym::stackLow;
DefinedData *WasmSym::stackHigh;
GlobalSymbol *WasmSym::tlsBase;
GlobalSymbol *WasmSym::tlsSize;
GlobalSymbol *WasmSym::tlsAlign;
UndefinedGlobal *WasmSym::tableBase;
DefinedData *WasmSym::definedTableBase;
UndefinedGlobal *WasmSym::memoryBase;
DefinedData *WasmSym::definedMemoryBase;
TableSymbol *WasmSym::indirectFunctionTable;

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