diff --git a/lld/test/wasm/tls-relocations.s b/lld/test/wasm/tls-relocations.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/tls-relocations.s @@ -0,0 +1,88 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s + +.section data,"",@ + .int32 41 +data_sym: + .int32 42 + .size data_sym, 4 + +# TLS data section of size 16 with as relocations at offset 8 and 12 +.section tls_sec,"T",@ +.globl tls_sym +.p2align 2 + .int32 0x50 +tls_sym: + .int32 0x51 + .int32 data_sym + .int32 tls_sym + .size tls_sym, 4 + +.section .custom_section.target_features,"",@ + .int8 2 + .int8 43 + .int8 7 + .ascii "atomics" + .int8 43 + .int8 11 + .ascii "bulk-memory" + +# RUN: wasm-ld --experimental-pic -pie -no-gc-sections --shared-memory --no-entry -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s +# RUN: llvm-objdump -d --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck --check-prefix=ASM %s -- + +# CHECK: - Type: GLOBAL + +# __tls_base +# CHECK-NEXT: Globals: +# CHECK-NEXT: - Index: 3 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: true +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 0 + +# __tls_size +# CHECK-NEXT: - Index: 4 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: false +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 16 + +# __tls_align +# CHECK-NEXT: - Index: 5 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: false +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 4 + +# ASM: <__wasm_init_tls>: +# ASM-EMPTY: +# ASM-NEXT: local.get 0 +# ASM-NEXT: global.set 3 +# ASM-NEXT: local.get 0 +# ASM-NEXT: i32.const 0 +# ASM-NEXT: i32.const 16 +# ASM-NEXT: memory.init 0, 0 +# call to __wasm_apply_tls_relocs +# ASM-NEXT: call 4 +# ASM-NEXT: end + +# ASM: <__wasm_apply_tls_relocs>: +# ASM-EMPTY: +# ASM-NEXT: i32.const 8 +# ASM-NEXT: global.get 3 +# ASM-NEXT: i32.add +# ASM-NEXT: global.get 1 +# ASM-NEXT: i32.const 20 +# ASM-NEXT: i32.add +# ASM-NEXT: i32.store 0 +# ASM-NEXT: i32.const 12 +# ASM-NEXT: global.get 3 +# ASM-NEXT: i32.add +# ASM-NEXT: global.get 3 +# ASM-NEXT: i32.const 4 +# ASM-NEXT: i32.add +# ASM-NEXT: i32.store 0 +# ASM-NEXT: end diff --git a/lld/test/wasm/tls.s b/lld/test/wasm/tls.s --- a/lld/test/wasm/tls.s +++ b/lld/test/wasm/tls.s @@ -133,7 +133,7 @@ # ASM-NEXT: i32.const 0 # ASM-NEXT: i32.const 12 # ASM-NEXT: memory.init 0, 0 -# call to __wasm_apply_global_tls_relocs> +# call to __wasm_apply_global_tls_relocs # ASM-NEXT: call 3 # ASM-NEXT: end diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -384,14 +384,17 @@ << " addend=" << rel.Addend << " index=" << rel.Index << " output offset=" << offset << "\n"); - // Calculate the address at which to apply the relocations + // Calculate the address at which to apply the relocation writeU8(os, opcode_ptr_const, "CONST"); writeSleb128(os, offset, "offset"); // In PIC mode we need to add the __memory_base if (config->isPic) { writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); - writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base"); + if (isTLS()) + writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "tls_base"); + else + writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base"); writeU8(os, opcode_ptr_add, "ADD"); } @@ -418,6 +421,8 @@ if (rel.Type == R_WASM_TABLE_INDEX_I32 || rel.Type == R_WASM_TABLE_INDEX_I64) baseSymbol = WasmSym::tableBase; + else if (sym->isTLS()) + baseSymbol = WasmSym::tlsBase; writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); writeUleb128(os, baseSymbol->getGlobalIndex(), "base"); writeU8(os, opcode_reloc_const, "CONST"); diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -578,6 +578,11 @@ // Unlike __wasm_apply_data_relocs this needs to run on every thread. static DefinedFunction *applyGlobalRelocs; + // __wasm_apply_tls_relocs + // Like applyDataRelocs but for TLS section. These must be delayed until + // __wasm_init_tls. + static DefinedFunction *applyTLSRelocs; + // __wasm_apply_global_tls_relocs // Like applyGlobalRelocs but for globals that hold TLS addresses. These // must be delayed until __wasm_init_tls. diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -77,6 +77,7 @@ DefinedFunction *WasmSym::initMemory; DefinedFunction *WasmSym::applyDataRelocs; DefinedFunction *WasmSym::applyGlobalRelocs; +DefinedFunction *WasmSym::applyTLSRelocs; DefinedFunction *WasmSym::applyGlobalTLSRelocs; DefinedFunction *WasmSym::initTLS; DefinedFunction *WasmSym::startFunction; @@ -308,7 +309,7 @@ // output segment (__tls_base). When building without shared memory, TLS // symbols absolute, just like non-TLS. if (isTLS() && config->sharedMemory) - return getOutputSegmentOffset() + value; + return getOutputSegmentOffset(); if (segment) return segment->getVA(value); return value; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -63,6 +63,7 @@ void createStartFunction(); void createApplyDataRelocationsFunction(); void createApplyGlobalRelocationsFunction(); + void createApplyTLSRelocationsFunction(); void createApplyGlobalTLSRelocationsFunction(); void createCallCtorsFunction(); void createInitTLSFunction(); @@ -1043,14 +1044,31 @@ } } - if (config->sharedMemory && out.globalSec->needsTLSRelocations()) { - WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction( - "__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, - make(nullSignature, - "__wasm_apply_global_tls_relocs")); - WasmSym::applyGlobalTLSRelocs->markLive(); - // TLS relocations depend on the __tls_base symbols - WasmSym::tlsBase->markLive(); + if (config->sharedMemory) { + if (out.globalSec->needsTLSRelocations()) { + WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction( + "__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, + make(nullSignature, + "__wasm_apply_global_tls_relocs")); + WasmSym::applyGlobalTLSRelocs->markLive(); + // TLS relocations depend on the __tls_base symbols + WasmSym::tlsBase->markLive(); + } + + auto hasTLSRelocs = [](const OutputSegment *segment) { + if (segment->isTLS()) + for (const auto* is: segment->inputSegments) + if (is->getRelocations().size()) + return true; + return false; + }; + if (llvm::any_of(segments, hasTLSRelocs)) { + WasmSym::applyTLSRelocs = symtab->addSyntheticFunction( + "__wasm_apply_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, + make(nullSignature, + "__wasm_apply_tls_relocs")); + WasmSym::applyTLSRelocs->markLive(); + } } if (config->isPic && out.globalSec->needsRelocations()) { @@ -1332,8 +1350,9 @@ raw_string_ostream os(bodyContent); writeUleb128(os, 0, "num locals"); for (const OutputSegment *seg : segments) - for (const InputChunk *inSeg : seg->inputSegments) - inSeg->generateRelocationCode(os); + if (!config->sharedMemory || !seg->isTLS()) + for (const InputChunk *inSeg : seg->inputSegments) + inSeg->generateRelocationCode(os); writeU8(os, WASM_OPCODE_END, "END"); } @@ -1341,6 +1360,23 @@ createFunction(WasmSym::applyDataRelocs, bodyContent); } +void Writer::createApplyTLSRelocationsFunction() { + LLVM_DEBUG(dbgs() << "createApplyTLSRelocationsFunction\n"); + std::string bodyContent; + { + raw_string_ostream os(bodyContent); + writeUleb128(os, 0, "num locals"); + for (const OutputSegment *seg : segments) + if (seg->isTLS()) + for (const InputChunk *inSeg : seg->inputSegments) + inSeg->generateRelocationCode(os); + + writeU8(os, WASM_OPCODE_END, "END"); + } + + createFunction(WasmSym::applyTLSRelocs, bodyContent); +} + // Similar to createApplyDataRelocationsFunction but generates relocation code // for WebAssembly globals. Because these globals are not shared between threads // these relocation need to run on every thread. @@ -1476,6 +1512,12 @@ writeU8(os, 0, "memory index immediate"); } + if (WasmSym::applyTLSRelocs) { + writeU8(os, WASM_OPCODE_CALL, "CALL"); + writeUleb128(os, WasmSym::applyTLSRelocs->getFunctionIndex(), + "function index"); + } + if (WasmSym::applyGlobalTLSRelocs) { writeU8(os, WASM_OPCODE_CALL, "CALL"); writeUleb128(os, WasmSym::applyGlobalTLSRelocs->getFunctionIndex(), @@ -1619,6 +1661,8 @@ createApplyDataRelocationsFunction(); if (WasmSym::applyGlobalRelocs) createApplyGlobalRelocationsFunction(); + if (WasmSym::applyTLSRelocs) + createApplyTLSRelocationsFunction(); if (WasmSym::applyGlobalTLSRelocs) createApplyGlobalTLSRelocationsFunction(); if (WasmSym::initMemory)