diff --git a/lld/test/wasm/pic-static.s b/lld/test/wasm/pic-static.s index c6be02ddaacdf..0e3e7014647b4 100644 --- a/lld/test/wasm/pic-static.s +++ b/lld/test/wasm/pic-static.s @@ -124,7 +124,7 @@ ret32_ptr: # CHECK-NEXT: Mutable: false # CHECK-NEXT: InitExpr: # CHECK-NEXT: Opcode: I32_CONST -# CHECK-NEXT: Value: 0 +# CHECK-NEXT: Value: 65536 # GOT.func.internal.ret32 # CHECK-NEXT: - Index: 4 diff --git a/lld/test/wasm/tls-base-non-shared-memory.s b/lld/test/wasm/tls-base-non-shared-memory.s new file mode 100644 index 0000000000000..df027e520a649 --- /dev/null +++ b/lld/test/wasm/tls-base-non-shared-memory.s @@ -0,0 +1,37 @@ +# Test that linking without shared memory causes __tls_base to be +# internalized, and initialized to a non-zero value, even without any TLS data +# present. + +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s + +.globaltype __tls_base, i32 + +.globl _start +_start: + .functype _start () -> () + global.get __tls_base + drop + end_function + +# CHECK: - Type: GLOBAL +# 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: 65536 +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: false +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 65536 + +# CHECK: GlobalNames: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Name: __stack_pointer +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Name: __tls_base diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 128931513b215..038a8b3f5417d 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -381,6 +381,7 @@ void Writer::layoutMemory() { ctx.sym.dsoHandle->setVA(dataStart); out.dylinkSec->memAlign = 0; + uint64_t fixedTLSBase = memoryPtr; for (OutputSegment *seg : segments) { out.dylinkSec->memAlign = std::max(out.dylinkSec->memAlign, seg->alignment); memoryPtr = alignTo(memoryPtr, 1ULL << seg->alignment); @@ -397,10 +398,7 @@ void Writer::layoutMemory() { auto *tlsAlign = cast(ctx.sym.tlsAlign); setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment); } - if (!ctx.arg.sharedMemory && ctx.sym.tlsBase) { - auto *tlsBase = cast(ctx.sym.tlsBase); - setGlobalPtr(tlsBase, memoryPtr); - } + fixedTLSBase = memoryPtr; } if (ctx.sym.rodataStart && seg->name.starts_with(".rodata") && @@ -414,6 +412,15 @@ void Writer::layoutMemory() { ctx.sym.rodataEnd->setVA(memoryPtr); } + // In single-threaded builds we set __tls_base statically. + // Even in the absense of any actual TLS data, this symbol can still be + // referenced (for example by __builtin_thread_pointer, which should not + // return NULL). + if (!ctx.arg.sharedMemory && ctx.sym.tlsBase) { + auto *tlsBase = cast(ctx.sym.tlsBase); + setGlobalPtr(tlsBase, fixedTLSBase); + } + // Make space for the memory initialization flag if (ctx.arg.sharedMemory && hasPassiveInitializedSegments()) { memoryPtr = alignTo(memoryPtr, 4);