[lld][WebAssembly] Apply relocations to TLS data

This is only needed in the case of dynamic linking and pthreads.
Previously these relocations were simply not being applied.

Verified that this works using a more real world emscripten test:
https://github.com/emscripten-core/emscripten/pull/18641

Differential Revision: https://reviews.llvm.org/D143020
This commit is contained in:
Sam Clegg
2023-01-31 13:33:29 -08:00
parent 9f8fd57cb6
commit a0495e6b00
6 changed files with 157 additions and 14 deletions

View File

@@ -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

View File

@@ -133,7 +133,7 @@ tls3:
# 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

View File

@@ -384,14 +384,17 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
<< " 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 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
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");

View File

@@ -77,6 +77,7 @@ DefinedFunction *WasmSym::callDtors;
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 @@ uint64_t DefinedData::getVA() const {
// 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;

View File

@@ -578,6 +578,11 @@ struct WasmSym {
// 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.

View File

@@ -63,6 +63,7 @@ private:
void createStartFunction();
void createApplyDataRelocationsFunction();
void createApplyGlobalRelocationsFunction();
void createApplyTLSRelocationsFunction();
void createApplyGlobalTLSRelocationsFunction();
void createCallCtorsFunction();
void createInitTLSFunction();
@@ -1043,14 +1044,31 @@ void Writer::createSyntheticInitFunctions() {
}
}
if (config->sharedMemory && out.globalSec->needsTLSRelocations()) {
WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction(
"__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(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<SyntheticFunction>(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<SyntheticFunction>(nullSignature,
"__wasm_apply_tls_relocs"));
WasmSym::applyTLSRelocs->markLive();
}
}
if (config->isPic && out.globalSec->needsRelocations()) {
@@ -1332,8 +1350,9 @@ void Writer::createApplyDataRelocationsFunction() {
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 @@ void Writer::createApplyDataRelocationsFunction() {
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 @@ void Writer::createInitTLSFunction() {
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 @@ void Writer::run() {
createApplyDataRelocationsFunction();
if (WasmSym::applyGlobalRelocs)
createApplyGlobalRelocationsFunction();
if (WasmSym::applyTLSRelocs)
createApplyTLSRelocationsFunction();
if (WasmSym::applyGlobalTLSRelocs)
createApplyGlobalTLSRelocationsFunction();
if (WasmSym::initMemory)