From 5b84010e49f6fec754c219dbf8f5f49817408058 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 Aug 2026 16:07:01 -0700 Subject: [PATCH] Adjust TLS in wasip3 with threads This is a refinement of the previous implementation in #858 and is intended to be the dual of bytecodealliance/wasm-tools#2596 for wasi-libc. Notably the details of what exactly is exported from each shared object is now encapsulated and local to wasi-libc as opposed to needing toolchain integration which removes the need for changes to Clang or LLD. The main change here is that the previous inline array of `__wasilibc_library_tls_info`, which was constructed by `wasm-tools component link`, is now defined by wasi-libc itself. The representation here has changed slightly since TLS size/align isn't `const`-knowable at this time (now it's a function pointer to learn that), and the dynamic linker will now arrange for an array of these structures to be visible to libc and use. This extra level of indirection means that each shared object exports its own information and those are all woven into a single list for wasi-libc to read. --- CMakeLists.txt | 11 -------- .../crt/wasip3_symbol_references.h | 25 +++++++++++++++++++ .../headers/private/wasi/wasip3_tls.h | 10 +++++--- .../src/thread/coop-threads/pthread_create.c | 24 ++++++++++++------ test/CMakeLists.txt | 1 - 5 files changed, 48 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index deffbc62c..e3dcfd96a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -204,17 +204,6 @@ add_link_options( -resource-dir ${tmp_resource_dir} ) -if (HAVE_WASM_LIBCALL_THREAD_CONTEXT) - # TODO: shouldn't have to pass these as it means it's required by all end - # users as well. Ideally this would be part of the driver in rustc/clang, but - # that's left for a future change. - add_link_options( - -Wl,--export-if-defined=__wasm_init_tls - -Wl,--export-if-defined=__tls_size - -Wl,--export-if-defined=__tls_align - ) -endif() - # Expose the public headers to the implementation. We use `-isystem` for # purpose for two reasons: # diff --git a/libc-bottom-half/crt/wasip3_symbol_references.h b/libc-bottom-half/crt/wasip3_symbol_references.h index b047054c4..56e21d937 100644 --- a/libc-bottom-half/crt/wasip3_symbol_references.h +++ b/libc-bottom-half/crt/wasip3_symbol_references.h @@ -5,6 +5,7 @@ #ifdef __wasm_libcall_thread_context__ #include +#include // Force some symbols to be linked in for wasip3 extern void __wasm_init_task(void); @@ -15,6 +16,30 @@ __attribute__((used)) static void *__wasm_init_async_task_ref = __wasm_init_async_task; __attribute__((used)) static void *cabi_realloc_ref = cabi_realloc; +// Export a symbol from this object which describes the TLS information +// required for this object. This encapsulates the size/align information as +// well as an initialization function. +// +// Note that being part of `crt1-*.o` this is exported from all libraries, +// which is what we want because each library has a unique return value from +// `__builtin_wasm_tls_*` intrinsics and a separate `__wasm_init_tls` function. +// +// For more information on this see +// https://github.com/WebAssembly/wasi-libc/issues/857 which has since been +// adjusted with this scheme. +static size_t tls_size_and_align(size_t *align) { + *align = __builtin_wasm_tls_align(); + return __builtin_wasm_tls_size(); +} + +void __wasm_init_tls(void *base); + +__attribute__((visibility("default"))) +struct __wasilibc_library_tls_info __wasm_library_tls_info = { + .tls_size_and_align = tls_size_and_align, + .init_tls = __wasm_init_tls, +}; + // Force `__wasm_{g,s}et_{stack_pointer,tls_base}` to exist as defined symbols. // These end up as imported functions which `wit-component` recognizes, and what // exactly they're hooked up to will depend on `wit-component` when this is diff --git a/libc-top-half/headers/private/wasi/wasip3_tls.h b/libc-top-half/headers/private/wasi/wasip3_tls.h index 0e8fd0d70..4dfa04559 100644 --- a/libc-top-half/headers/private/wasi/wasip3_tls.h +++ b/libc-top-half/headers/private/wasi/wasip3_tls.h @@ -13,14 +13,18 @@ #ifdef __wasm_libcall_thread_context__ struct __wasilibc_library_tls_info { - size_t tls_size; - size_t tls_align; + // Stores this library's TLS alignment in `*align` and returns its TLS size. + // + // Note that a library with no thread-local storage at all reports a size of + // zero, but `*align` is always at least 1 so that callers can use it as a + // divisor/mask without special-casing. + size_t (*tls_size_and_align)(size_t *align); void (*init_tls)(void *); }; struct __wasilibc_program_tls_info { size_t num_libraries; - const struct __wasilibc_library_tls_info *library_info; + const struct __wasilibc_library_tls_info **library_info; void **main_thread_tls_base; }; diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_create.c b/libc-top-half/musl/src/thread/coop-threads/pthread_create.c index 848d7a680..185863bc8 100644 --- a/libc-top-half/musl/src/thread/coop-threads/pthread_create.c +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_create.c @@ -164,10 +164,14 @@ static size_t thread_tls_size(size_t *align) { size_t result_align = _Alignof(void *); for (size_t i = 0; i < info->num_libraries; i++) { - const struct __wasilibc_library_tls_info *library = &info->library_info[i]; - size = align_up(size, library->tls_align) + library->tls_size; - if (library->tls_align > result_align) - result_align = library->tls_align; + const struct __wasilibc_library_tls_info *library = info->library_info[i]; + size_t library_align; + size_t library_size = library->tls_size_and_align(&library_align); + if (library_size == 0) + continue; + size = align_up(size, library_align) + library_size; + if (library_align > result_align) + result_align = library_align; } *align = result_align; @@ -190,10 +194,14 @@ static void *layout_thread_tls(void *block) { void **bases = block; uintptr_t next = (uintptr_t)block + info->num_libraries * sizeof(void *); for (size_t i = 0; i < info->num_libraries; i++) { - const struct __wasilibc_library_tls_info *library = &info->library_info[i]; - next = align_up(next, library->tls_align); + const struct __wasilibc_library_tls_info *library = info->library_info[i]; + size_t library_align; + size_t library_size = library->tls_size_and_align(&library_align); + if (library_size == 0) + continue; + next = align_up(next, library_align); bases[i] = (void *)next; - next += library->tls_size; + next += library_size; } return bases; } @@ -258,7 +266,7 @@ hidden void __wasi_coop_thread_start_C(struct start_args *args) { void **bases = args->tls_layout; size_t num_libraries = info->num_libraries; for (size_t i = 0; i < num_libraries; i++) - info->library_info[i].init_tls(bases[i]); + info->library_info[i]->init_tls(bases[i]); } *__pthread_self() = self_copy; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2634f2598..5c1b1995f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -811,7 +811,6 @@ function(add_posix_test test_file) string(REPLACE "/" "_" test_name ${test_name}) set(test_name "open_posix_test_${test_name}") set(test_file "${CMAKE_CURRENT_SOURCE_DIR}/open-posix-test-suite/${test_file}") - set_source_files_properties(${test_file} PROPERTIES GENERATED TRUE) add_test_pair(${test_name} ${test_file} CFLAGS -Wno-unused-parameter -Wno-unused-variable -Wno-sign-compare -Wno-unused-but-set-global