[lld] [lld][WebAssembly] Fix TLS-relative relocations when linking without shared memory (PR #116136)
Sam Clegg via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 13 16:59:18 PST 2024
https://github.com/sbc100 created https://github.com/llvm/llvm-project/pull/116136
TLS-relative relocation 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: https://github.com/emscripten-core/emscripten/issues/22880
>From 23d374deb75aa49d137fcb08fd18dab0f8d1d0e5 Mon Sep 17 00:00:00 2001
From: Sam Clegg <sbc at chromium.org>
Date: Wed, 13 Nov 2024 16:46:30 -0800
Subject: [PATCH] [lld][WebAssembly] Fix TLS-relative relocations when linking
without shared memory
TLS-relative relocation 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: https://github.com/emscripten-core/emscripten/issues/22880
---
lld/test/wasm/tls-non-shared-memory.s | 9 +++++++++
lld/wasm/Relocations.cpp | 12 ++++++------
lld/wasm/Symbols.cpp | 9 ++++-----
lld/wasm/Symbols.h | 4 +++-
lld/wasm/SyntheticSections.cpp | 5 ++++-
5 files changed, 26 insertions(+), 13 deletions(-)
diff --git a/lld/test/wasm/tls-non-shared-memory.s b/lld/test/wasm/tls-non-shared-memory.s
index 1754fd6254bb80..04fbb62228a7e2 100644
--- a/lld/test/wasm/tls-non-shared-memory.s
+++ b/lld/test/wasm/tls-non-shared-memory.s
@@ -44,6 +44,7 @@ tls1:
# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
+# RUN: llvm-objdump --disassemble-symbols=get_tls1 --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS
# RUN: wasm-ld --experimental-pic -shared -o %t.so %t.o
# RUN: obj2yaml %t.so | FileCheck %s --check-prefixes=SHARED,PIC
@@ -97,6 +98,14 @@ tls1:
# CHECK-NEXT: Content: 2A000000
# CHECK-NEXT: - Type: CUSTOM
+# The constant value here which we add to `__tls_base` should not be absolute
+# but relative to `__tls_base`, in this case zero rather than 1024.
+# DIS: <get_tls1>:
+# DIS-EMPTY:
+# DIS-NEXT: global.get 1
+# DIS-NEXT: i32.const 0
+# DIS-NEXT: i32.add
+# DIS-NEXT: end
# In PIC mode we expect TLS data and non-TLS data to be merged into
# a single segment which is initialized via the __memory_base import
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 45ad32701616a1..2776ede8be9034 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -122,16 +122,16 @@ void scanRelocations(InputChunk *chunk) {
" cannot be used against an undefined symbol `" + toString(*sym) +
"`");
}
+ if (!sym->isTLS()) {
+ error(toString(file) + ": relocation " +
+ relocTypeToString(reloc.Type) +
+ " cannot be used against non-TLS symbol `" + toString(*sym) +
+ "`");
+ }
// In single-threaded builds TLS is lowered away and TLS data can be
// merged with normal data and allowing TLS relocation in non-TLS
// segments.
if (config->sharedMemory) {
- if (!sym->isTLS()) {
- error(toString(file) + ": relocation " +
- relocTypeToString(reloc.Type) +
- " cannot be used against non-TLS symbol `" + toString(*sym) +
- "`");
- }
if (auto *D = dyn_cast<DefinedData>(sym)) {
if (!D->segment->outputSeg->isTLS()) {
error(toString(file) + ": relocation " +
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index b2bbd11c53ef23..8df850ce23032f 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -310,12 +310,11 @@ uint32_t DefinedFunction::getExportedFunctionIndex() const {
return function->getFunctionIndex();
}
-uint64_t DefinedData::getVA() const {
+uint64_t DefinedData::getVA(bool absolute) const {
LLVM_DEBUG(dbgs() << "getVA: " << getName() << "\n");
- // In the shared memory case, TLS symbols are relative to the start of the TLS
- // output segment (__tls_base). When building without shared memory, TLS
- // symbols absolute, just like non-TLS.
- if (isTLS() && config->sharedMemory)
+ // TLS symbols are relative to the start of the TLS output segment
+ // (__tls_base).
+ if (isTLS() && !absolute)
return getOutputSegmentOffset();
if (segment)
return segment->getVA(value);
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 5ce3ecbc4ab194..310842b89098f9 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -315,7 +315,9 @@ class DefinedData : public DataSymbol {
static bool classof(const Symbol *s) { return s->kind() == DefinedDataKind; }
// Returns the output virtual address of a defined data symbol.
- uint64_t getVA() const;
+ // For TLS symbols, by default (unless absolute us set), this returns an
+ // address relative the `__tls_base`.
+ uint64_t getVA(bool absolute = false) const;
void setVA(uint64_t va);
// Returns the offset of a defined data symbol within its OutputSegment.
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index a3bc90cfe759ca..1e8abe9bc7b246 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -514,7 +514,10 @@ void GlobalSection::writeBody() {
} else {
WasmInitExpr initExpr;
if (auto *d = dyn_cast<DefinedData>(sym))
- initExpr = intConst(d->getVA(), is64);
+ // In the sharedMemory case `__wasm_apply_global_tls_relocs` is used
+ // to set the final value of this globel, but in the non-shared case
+ // we know the absolute value at link time.
+ initExpr = intConst(d->getVA(/*absolute=*/!config->sharedMemory), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
More information about the llvm-commits
mailing list