diff --git a/lld/test/wasm/no-tls.s b/lld/test/wasm/no-tls.s --- a/lld/test/wasm/no-tls.s +++ b/lld/test/wasm/no-tls.s @@ -52,4 +52,4 @@ # CHECK-NEXT: Mutable: false # CHECK-NEXT: InitExpr: # CHECK-NEXT: Opcode: I32_CONST -# CHECK-NEXT: Value: 1 +# CHECK-NEXT: Value: 0 diff --git a/lld/test/wasm/tls-no-shared.s b/lld/test/wasm/tls-no-shared.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/tls-no-shared.s @@ -0,0 +1,75 @@ +# Test that linking without shared memory causes __tls_base to be +# interlized + +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s + +.globaltype __tls_base, i32 + +.globl get_tls1 +get_tls1: + .functype get_tls1 () -> (i32) + global.get __tls_base + i32.const tls1 + i32.add + end_function + +.section .data.no_tls,"",@ +.globl no_tls +.p2align 2 +no_tls: + .int32 42 + .size no_tls, 4 + +.section .tdata.tls1,"",@ +.globl tls1 +.p2align 2 +tls1: + .int32 43 + .size tls1, 2 + +.section .custom_section.target_features,"",@ + .int8 2 + .int8 43 + .int8 7 + .ascii "atomics" + .int8 43 + .int8 11 + .ascii "bulk-memory" + +# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s + +# CHECK: - Type: GLOBAL +# __stack_pointer +# CHECK-NEXT: Globals: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: true +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 66576 +# __tls_base +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: false +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 1028 +# CHECK-NEXT: - Type: EXPORT + +# CHECK: - Type: DATA +# .data +# CHECK-NEXT: Segments: +# CHECK-NEXT: - SectionOffset: 7 +# CHECK-NEXT: InitFlags: 0 +# CHECK-NEXT: Offset: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 1024 +# CHECK-NEXT: Content: 2A000000 +# .tdata +# CHECK-NEXT: - SectionOffset: 17 +# CHECK-NEXT: InitFlags: 0 +# CHECK-NEXT: Offset: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 1028 +# CHECK-NEXT: Content: 2B000000 diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -541,21 +541,30 @@ return sym; } -static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable, - int value) { +static InputGlobal *createGlobal(StringRef name, bool isMutable) { llvm::wasm::WasmGlobal wasmGlobal; if (config->is64.getValueOr(false)) { wasmGlobal.Type = {WASM_TYPE_I64, isMutable}; - wasmGlobal.InitExpr.Value.Int64 = value; wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I64_CONST; + wasmGlobal.InitExpr.Value.Int64 = 0; } else { wasmGlobal.Type = {WASM_TYPE_I32, isMutable}; - wasmGlobal.InitExpr.Value.Int32 = value; wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST; + wasmGlobal.InitExpr.Value.Int32 = 0; } wasmGlobal.SymbolName = name; - return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN, - make(wasmGlobal, nullptr)); + return make(wasmGlobal, nullptr); +} + +static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable) { + InputGlobal *g = createGlobal(name, isMutable); + return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN, g); +} + +static GlobalSymbol *createOptionalGlobal(StringRef name, bool isMutable) { + InputGlobal *g = createGlobal(name, isMutable); + return symtab->addOptionalGlobalSymbols(name, WASM_SYMBOL_VISIBILITY_HIDDEN, + g); } // Create ABI-defined synthetic symbols @@ -602,7 +611,7 @@ WasmSym::tableBase->markLive(); } else { // For non-PIC code - WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true, 0); + WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true); WasmSym::stackPointer->markLive(); } @@ -616,9 +625,9 @@ WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol( "__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN); assert(WasmSym::initMemoryFlag); - WasmSym::tlsBase = createGlobalVariable("__tls_base", true, 0); - WasmSym::tlsSize = createGlobalVariable("__tls_size", false, 0); - WasmSym::tlsAlign = createGlobalVariable("__tls_align", false, 1); + WasmSym::tlsBase = createGlobalVariable("__tls_base", true); + WasmSym::tlsSize = createGlobalVariable("__tls_size", false); + WasmSym::tlsAlign = createGlobalVariable("__tls_align", false); WasmSym::initTLS = symtab->addSyntheticFunction( "__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN, make( @@ -642,6 +651,18 @@ WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base"); WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base"); } + + // 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 + // programs, and such object files can contains refernced to this symbol. + // + // However, in this case __tls_base is immutable and points directly to the + // start of the `.tdata` static segment. + // + // __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 (!config->sharedMemory) + WasmSym::tlsBase = createOptionalGlobal("__tls_base", false); } // Reconstructs command line arguments so that so that you can re-run diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -83,6 +83,8 @@ DefinedFunction *addSyntheticFunction(StringRef name, uint32_t flags, InputFunction *function); DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0); + DefinedGlobal *addOptionalGlobalSymbols(StringRef name, uint32_t flags, + InputGlobal *global); void handleSymbolVariants(); void handleWeakUndefines(); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -206,7 +206,7 @@ flags, nullptr, function); } -// Adds an optional, linker generated, data symbols. The symbol will only be +// Adds an optional, linker generated, data symbol. The symbol will only be // 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. @@ -241,6 +241,18 @@ nullptr, global); } +DefinedGlobal *SymbolTable::addOptionalGlobalSymbols(StringRef name, + uint32_t flags, + InputGlobal *global) { + LLVM_DEBUG(dbgs() << "addOptionalGlobalSymbols: " << name << " -> " << global + << "\n"); + Symbol *s = find(name); + if (!s || s->isDefined()) + return nullptr; + syntheticGlobals.emplace_back(global); + return replaceSymbol(s, name, flags, nullptr, global); +} + static bool shouldReplace(const Symbol *existing, InputFile *newFile, uint32_t newFlags) { // If existing symbol is undefined, replace it. diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -204,6 +204,16 @@ }); } +static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) { + if (config->is64.getValueOr(false)) { + assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I64_CONST); + g->global->global.InitExpr.Value.Int64 = memoryPtr; + } else { + assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST); + g->global->global.InitExpr.Value.Int32 = memoryPtr; + } +} + // Fix the memory layout of the output binary. This assigns memory offsets // to each of the input data sections as well as the explicit stack region. // The default memory layout is as follows, from low to high. @@ -267,18 +277,21 @@ seg->startVA = memoryPtr; log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name, memoryPtr, seg->size, seg->alignment)); - memoryPtr += seg->size; - if (WasmSym::tlsSize && seg->name == ".tdata") { - auto *tlsSize = cast(WasmSym::tlsSize); - assert(tlsSize->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST); - tlsSize->global->global.InitExpr.Value.Int32 = seg->size; + if (seg->name == ".tdata") { + if (config->sharedMemory) { + auto *tlsSize = cast(WasmSym::tlsSize); + setGlobalPtr(tlsSize, seg->size); - auto *tlsAlign = cast(WasmSym::tlsAlign); - assert(tlsAlign->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST); - tlsAlign->global->global.InitExpr.Value.Int32 = int64_t{1} - << seg->alignment; + auto *tlsAlign = cast(WasmSym::tlsAlign); + setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment); + } else { + auto *tlsBase = cast(WasmSym::tlsBase); + setGlobalPtr(tlsBase, memoryPtr); + } } + + memoryPtr += seg->size; } // Make space for the memory initialization flag @@ -768,7 +781,7 @@ if (s == nullptr) { LLVM_DEBUG(dbgs() << "new segment: " << name << "\n"); s = make(name); - if (config->sharedMemory || name == ".tdata") + if (config->sharedMemory) s->initFlags = WASM_SEGMENT_IS_PASSIVE; // Exported memories are guaranteed to be zero-initialized, so no need // to emit data segments for bss sections.