[lld] [WebAssembly] Generate a call to __wasm_apply_global_tls_relocs in __wasm_init_memory (PR #149832)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 21 08:11:05 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld-wasm
Author: None (Arshia001)
<details>
<summary>Changes</summary>
# Motivation
We recently implemented the WebAssembly exception handling proposal in Wasmer 6.0. As a result, we can now take advantage of clang's support for compiling SjLj and C++ exceptions to WASM EH. This PR fixes a wasm-ld issue that breaks the use of C++ exception handling in WASI(X) modules.
Note: I use WASI(X) to mean either [wasi preview 1](https://wasi.dev/) or [WASIX](https://wasix.org) modules.
# Error details
When compiling C++ code that uses exceptions, clang generates a `GOT.data.internal.__wasm_lpad_context` global, which points to the wasm landing pad context that's shared between compiler code and libunwind. This global is initialized in the `__wasm_apply_global_tls_relocs` function.
TLS initialization happens in two separate places; for the "main thread", `__wasm_init_memory` runs as the `(start)` function of the WASM module, initializing all memory segments (including TLS), while also initializing the main thread's `__tls_base` to the space reserved for it by the compiler, and signalling this fact to other threads via an atomic. Other threads need to run `__wasm_init_tls` after getting their respective `__tls_base` global initialized externally.
As it stands, `__wasm_apply_global_tls_relocs` is only called through `__wasm_init_tls`, meaning if code doesn't call `__wasm_init_tls`, any globals that are initialized in `__wasm_apply_global_tls_relocs` do not get initialized. This is the case for the main thread.
It is important to note that exception handling code generated by the compiler uses `GOT.data.internal.__wasm_lpad_context`, while the code in `_Unwind_CallPersonality` goes through `__tls_base + offset` directly. Because `GOT.data.internal.__wasm_lpad_context` is not initialized in the main thread, the compiler and `_Unwind_CallPersonality` do not agree on where the landing pad context is stored. This results in `scan_eh_tab` not getting the correct LSDA pointer. Exception handling is then completely broken; the catch-all block runs for every exception due to a lack of any type information at runtime.
This PR allows a call to `__wasm_apply_global_tls_relocs` to be generated in `__wasm_init_memory` if needed, which should fix the value of `GOT.data.internal.__wasm_lpad_context` in modules' main threads. Interestingly, through all of our recent work on dynamic linking and PIC modules, we never encountered `__wasm_apply_global_tls_relocs`, and I don't know if it's used for anything besides `GOT.data.internal.__wasm_lpad_context`.
## But how does emscripten work if this is broken?
Good question! Emscripten calls `__wasm_init_tls` redundantly for main threads, and thus initializes the TLS area twice. This has no observable effect besides being slower, and does indeed fix C++ exception handling.
This is a workaround that we can use in WASIX as well. However, as far as I understand, the current behavior is wasm-ld is broken, since `__wasm_init_memory` and `__wasm_init_tls` should behave similarly with respect to TLS initialization, but feel free to disagree with me here.
---
Full diff: https://github.com/llvm/llvm-project/pull/149832.diff
1 Files Affected:
- (modified) lld/wasm/Writer.cpp (+9)
``````````diff
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index b704677d36c93..3cd6a73fb1a31 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -1366,6 +1366,15 @@ void Writer::createInitMemoryFunction() {
writeUleb128(os, s->index, "segment index immediate");
writeU8(os, 0, "memory index immediate");
}
+
+ // After initializing the TLS segment, we also need to apply TLS
+ // relocations in the same way __wasm_init_tls does.
+ if (ctx.arg.sharedMemory && s->isTLS() &&
+ ctx.sym.applyGlobalTLSRelocs) {
+ writeU8(os, WASM_OPCODE_CALL, "CALL");
+ writeUleb128(os, ctx.sym.applyGlobalTLSRelocs->getFunctionIndex(),
+ "function index");
+ }
}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/149832
More information about the llvm-commits
mailing list