[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
Wed Oct 22 00:03:57 PDT 2025


Arshia001 wrote:

> What you are saying may be true of __wasm_apply_global_tls_relocs but it is not true of __wasm_apply_tls_relocs. __wasm_tls_init needs to be called after instantiation because of this latter function, even if technically the former could run during instantiation.

OK, now I see where the confusion came from. `__wasm_apply_tls_relocs` does need to be called after linking steps are performed, but in our linker implementation, the flow for the main thread is:

1. Allocate memory and table space for each module. This gives us a `__memory_base` and a `__tls_base`.
2. Construct the imports object. At this stage, we may not have access to every import from every other module anyway; some modules won't be instantiated at all since circular dependencies between modules are possible. What we do is, for `GOT.mem` and `GOT.func` imports, we just give the global a `0` value and keep track of them, whereas for function imports in `env`, we generate stubs that will resolve the correct function and call it later.
3. Instantiate each module; this runs the start function and, as a consequence, calls `__wasm_init_memory` which in turn calls `__wasm_apply_global_tls_relocs`.
4. After every module is instantiated, we go over all modules again, setting the pending globals from step 2 to the correct value. See code here: https://github.com/wasmerio/wasmer/blob/09ce060643a7761d3532d5c1f4c0aabc30855cc6/lib/wasix/src/state/linker.rs#L1657-L1700
5. We then call all the functions that couldn't be called before everything else was initialized; these are `__wasm_apply_data_relocs` and `__wasm_apply_tls_relocs`.
6. Once every module has the correct linking steps performed, the final step is to call `__wasm_call_ctors`; this runs arbitrary user code, and there are no guarantees about what it may or may not do.
  * One problem we still have (and I believe cannot be solved) is, if two modules interdepend on each other's ctors having run before their own, one module will not work correctly.

For worker threads, the flow is mostly the same. However, since worker threads don't have a statically-allocated TLS area from the compiler, we need to allocate that first within the module. We use a small guest-side function to allocate and initialize TLS ( [code here](https://github.com/wasix-org/wasix-libc/blob/main/libc-bottom-half/crt/scrt1.c) ). Everything else remains mostly the same.

In this way, the main thread doesn't need to call `__wasm_init_tls` anyway. I do agree this is not the cleanest thing ever, but it's sound AFAICT.

https://github.com/llvm/llvm-project/pull/149832


More information about the llvm-commits mailing list