Skip to content

Commit 1c1fbf5

Browse files
authored
[lld][WebAssembly] Fix TLS-relative relocations when linking without shared memory (#116136)
TLS-relative relocations always need to be relative the TLS section since they get added to `__tls_base` at runtime. Without this change the tls base address was effectively being added to the final value twice in this case. This only effects code the is built with `-pthread` but linked without shared memory (i.e. without threads). Fixes: emscripten-core/emscripten#22880
1 parent 1073e90 commit 1c1fbf5

File tree

4 files changed

+20
-7
lines changed

4 files changed

+20
-7
lines changed

lld/test/wasm/tls-non-shared-memory.s

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ tls1:
4444

4545
# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o
4646
# RUN: obj2yaml %t.wasm | FileCheck %s
47+
# RUN: llvm-objdump --disassemble-symbols=get_tls1 --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS
4748

4849
# RUN: wasm-ld --experimental-pic -shared -o %t.so %t.o
4950
# RUN: obj2yaml %t.so | FileCheck %s --check-prefixes=SHARED,PIC
@@ -97,6 +98,14 @@ tls1:
9798
# CHECK-NEXT: Content: 2A000000
9899
# CHECK-NEXT: - Type: CUSTOM
99100

101+
# The constant value here which we add to `__tls_base` should not be absolute
102+
# but relative to `__tls_base`, in this case zero rather than 1024.
103+
# DIS: <get_tls1>:
104+
# DIS-EMPTY:
105+
# DIS-NEXT: global.get 1
106+
# DIS-NEXT: i32.const 0
107+
# DIS-NEXT: i32.add
108+
# DIS-NEXT: end
100109

101110
# In PIC mode we expect TLS data and non-TLS data to be merged into
102111
# a single segment which is initialized via the __memory_base import

lld/wasm/Symbols.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,12 +310,11 @@ uint32_t DefinedFunction::getExportedFunctionIndex() const {
310310
return function->getFunctionIndex();
311311
}
312312

313-
uint64_t DefinedData::getVA() const {
313+
uint64_t DefinedData::getVA(bool absolute) const {
314314
LLVM_DEBUG(dbgs() << "getVA: " << getName() << "\n");
315-
// In the shared memory case, TLS symbols are relative to the start of the TLS
316-
// output segment (__tls_base). When building without shared memory, TLS
317-
// symbols absolute, just like non-TLS.
318-
if (isTLS() && config->sharedMemory)
315+
// TLS symbols (by default) are relative to the start of the TLS output
316+
// segment (__tls_base).
317+
if (isTLS() && !absolute)
319318
return getOutputSegmentOffset();
320319
if (segment)
321320
return segment->getVA(value);

lld/wasm/Symbols.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,9 @@ class DefinedData : public DataSymbol {
315315
static bool classof(const Symbol *s) { return s->kind() == DefinedDataKind; }
316316

317317
// Returns the output virtual address of a defined data symbol.
318-
uint64_t getVA() const;
318+
// For TLS symbols, by default (unless absolute is set), this returns an
319+
// address relative the `__tls_base`.
320+
uint64_t getVA(bool absolute = false) const;
319321
void setVA(uint64_t va);
320322

321323
// Returns the offset of a defined data symbol within its OutputSegment.

lld/wasm/SyntheticSections.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,10 @@ void GlobalSection::writeBody() {
514514
} else {
515515
WasmInitExpr initExpr;
516516
if (auto *d = dyn_cast<DefinedData>(sym))
517-
initExpr = intConst(d->getVA(), is64);
517+
// In the sharedMemory case TLS globals are set during
518+
// `__wasm_apply_global_tls_relocs`, but in the non-shared case
519+
// we know the absolute value at link time.
520+
initExpr = intConst(d->getVA(/*absolute=*/!config->sharedMemory), is64);
518521
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
519522
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
520523
else {

0 commit comments

Comments
 (0)