From d98b5336eb8aa31fad02aee9cdeb408989e24eb5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 31 Jul 2026 14:09:25 -0700 Subject: [PATCH] Implement new handling of TLS base/symbols This commit is the wasm-tools half of the implementation of WebAssembly/wasi-libc#857. More information can be found in the writeup of that issue itself, but the general goal here is adjusting linking conventions for wasip3 modules, in particular around dynamic libraries, to ensure that TLS works everywhere. --- crates/wit-component/src/encoding.rs | 111 ++++ crates/wit-component/src/encoding/world.rs | 17 + crates/wit-component/src/linking.rs | 494 ++++++++++++++---- crates/wit-component/src/linking/metadata.rs | 128 ++++- crates/wit-component/src/validation.rs | 25 +- .../error-link-tls-without-init/error.txt | 1 + .../error-link-tls-without-init/lib-foo.wat | 20 + .../error-link-tls-without-init/lib-foo.wit | 9 + .../components/link-initialize/component.wat | 12 +- .../components/link-stub-wasip2/component.wat | 12 +- .../components/link-wasip3-abi/component.wat | 24 +- .../link-wasip3-tls-no-threads/component.wat | 182 +++++++ .../component.wit.print | 5 + .../link-wasip3-tls-no-threads/lib-c.wat | 26 + .../link-wasip3-tls-no-threads/lib-c.wit | 4 + .../link-wasip3-tls-no-threads/lib-foo.wat | 28 + .../link-wasip3-tls-no-threads/lib-foo.wit | 9 + .../components/link-wasip3-tls/component.wat | 294 +++++++++++ .../link-wasip3-tls/component.wit.print | 5 + .../components/link-wasip3-tls/lib-c.wat | 31 ++ .../components/link-wasip3-tls/lib-c.wit | 4 + .../components/link-wasip3-tls/lib-foo.wat | 33 ++ .../components/link-wasip3-tls/lib-foo.wit | 9 + .../components/link-wasip3-tls/lib-none.wat | 8 + .../components/link-wasip3-tls/lib-none.wit | 4 + .../tests/components/link/component.wat | 12 +- .../component.wat | 77 +++ .../component.wit.print | 4 + .../threading-wasm-ld-symbols-coop/module.wat | 8 + .../threading-wasm-ld-symbols-coop/module.wit | 5 + .../threading-wasm-ld-symbols/component.wat | 27 +- .../threading-wasm-ld-symbols64/component.wat | 27 +- 32 files changed, 1512 insertions(+), 143 deletions(-) create mode 100644 crates/wit-component/tests/components/error-link-tls-without-init/error.txt create mode 100644 crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wat create mode 100644 crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wit create mode 100644 crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wat create mode 100644 crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wit.print create mode 100644 crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wat create mode 100644 crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wit create mode 100644 crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wat create mode 100644 crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wit create mode 100644 crates/wit-component/tests/components/link-wasip3-tls/component.wat create mode 100644 crates/wit-component/tests/components/link-wasip3-tls/component.wit.print create mode 100644 crates/wit-component/tests/components/link-wasip3-tls/lib-c.wat create mode 100644 crates/wit-component/tests/components/link-wasip3-tls/lib-c.wit create mode 100644 crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wat create mode 100644 crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wit create mode 100644 crates/wit-component/tests/components/link-wasip3-tls/lib-none.wat create mode 100644 crates/wit-component/tests/components/link-wasip3-tls/lib-none.wit create mode 100644 crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wat create mode 100644 crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wit.print create mode 100644 crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wat create mode 100644 crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wit diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 726fc62268..5e29ced9eb 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -422,8 +422,21 @@ pub struct EncodingState<'a> { /// Maps from original export name to task initialization wrapper function index. /// Used to wrap exports with __wasm_init_(async_)task calls. export_task_initialization_wrappers: HashMap, + + /// The index of the instance of the synthesized module which stores the TLS + /// base pointer in a `global`. + /// + /// This is only used, and only created, when a module imports + /// `__wasm_{get,set}_tls_base` but the program doesn't use cooperative + /// threading. See `materialize_tls_base_import`. + tls_base_instance_index: Option<(u32, ValType)>, } +/// Name of the export of the synthesized TLS-base module which reads the base. +const TLS_BASE_GET: &str = "get"; +/// Name of the export of the synthesized TLS-base module which writes the base. +const TLS_BASE_SET: &str = "set"; + impl<'a> EncodingState<'a> { fn encode_core_modules(&mut self) { assert!(self.module_index.is_none()); @@ -1991,6 +2004,14 @@ impl<'a> EncodingState<'a> { let index = self.component.context_set((*ty).try_into()?, *slot); Ok((ExportKind::Func, index)) } + Import::TlsBaseGet { ty } => Ok(( + ExportKind::Func, + self.materialize_tls_base_import(false, (*ty).try_into()?), + )), + Import::TlsBaseSet { ty } => Ok(( + ExportKind::Func, + self.materialize_tls_base_import(true, (*ty).try_into()?), + )), Import::ExportedTaskCancel => { let index = self.component.task_cancel(); Ok((ExportKind::Func, index)) @@ -2037,6 +2058,93 @@ impl<'a> EncodingState<'a> { } } + /// Helper to satisfy `__wasm_{get,set}_tls_base` imports. + /// + /// For more information on this see WebAssembly/wasi-libc#857 + fn materialize_tls_base_import(&mut self, set: bool, ty: ValType) -> u32 { + if self.info.uses_cooperative_threading() { + return if set { + self.component.context_set(ty, 1) + } else { + self.component.context_get(ty, 1) + }; + } + + let instance = match self.tls_base_instance_index { + Some((index, prev_ty)) => { + assert_eq!(prev_ty, ty, "conflicting TLS base pointer types"); + index + } + None => { + let index = self.encode_tls_base_module(ty); + self.tls_base_instance_index = Some((index, ty)); + index + } + }; + let name = if set { TLS_BASE_SET } else { TLS_BASE_GET }; + self.core_alias_export( + Some(&format!("tls-base-{name}")), + instance, + name, + ExportKind::Func, + ) + } + + /// Synthesizes and instantiates a module which stores the TLS base pointer + /// in a mutable `global`, exporting accessors for it. + fn encode_tls_base_module(&mut self, ty: ValType) -> u32 { + let mut types = TypeSection::new(); + types.ty().function([], [ty]); + types.ty().function([ty], []); + + let mut globals = GlobalSection::new(); + globals.global( + wasm_encoder::GlobalType { + val_type: ty, + mutable: true, + shared: false, + }, + &match ty { + ValType::I64 => ConstExpr::i64_const(0), + ValType::I32 => ConstExpr::i32_const(0), + _ => unreachable!(), + }, + ); + + let mut functions = FunctionSection::new(); + let mut code = CodeSection::new(); + + functions.function(0); + let mut get = wasm_encoder::Function::new([]); + get.instruction(&Instruction::GlobalGet(0)); + get.instruction(&Instruction::End); + code.function(&get); + + functions.function(1); + let mut set = wasm_encoder::Function::new([]); + set.instruction(&Instruction::LocalGet(0)); + set.instruction(&Instruction::GlobalSet(0)); + set.instruction(&Instruction::End); + code.function(&set); + + let mut exports = ExportSection::new(); + exports.export(TLS_BASE_GET, ExportKind::Func, 0); + exports.export(TLS_BASE_SET, ExportKind::Func, 1); + + let mut module = Module::new(); + module.section(&types); + module.section(&functions); + module.section(&globals); + module.section(&exports); + module.section(&code); + + let module_index = self + .component + .core_module(Some("wit-component:tls-base"), &module); + self.component + .core_instantiate(Some("wit-component:tls-base"), module_index, []) + } + /// Helper for `materialize_import` above for materializing functions that /// are part of the "shim module" generated. fn materialize_shim_import(&mut self, shims: &Shims<'_>, kind: &ShimKind) -> (ExportKind, u32) { @@ -2686,6 +2794,8 @@ impl<'a> Shims<'a> { | Import::WaitableJoin | Import::ContextGet { .. } | Import::ContextSet { .. } + | Import::TlsBaseGet { .. } + | Import::TlsBaseSet { .. } | Import::ThreadIndex | Import::ThreadResumeLater | Import::ThreadSuspend { .. } @@ -3411,6 +3521,7 @@ impl ComponentEncoder { aliased_core_items: Default::default(), info: &world, export_task_initialization_wrappers: HashMap::new(), + tls_base_instance_index: None, }; state.encode_imports(&self.import_name_map)?; state.encode_core_modules(); diff --git a/crates/wit-component/src/encoding/world.rs b/crates/wit-component/src/encoding/world.rs index dfb56b2795..f5c6d32e69 100644 --- a/crates/wit-component/src/encoding/world.rs +++ b/crates/wit-component/src/encoding/world.rs @@ -85,6 +85,21 @@ impl<'a> ComponentWorld<'a> { Ok(ret) } + /// Returns whether any module in this component may spawn a thread, and + /// thus whether the program is using cooperative threading. + /// + /// This is a heuristic which should go away once component-model-threading + /// has been stable for awhile and the return value of this function should + /// be const-propagated as `true`. + pub fn uses_cooperative_threading(&self) -> bool { + let uses = |info: &ValidatedModule| { + info.imports + .imports() + .any(|(_, _, import)| matches!(import, Import::ThreadNewIndirect)) + }; + uses(&self.info) || self.adapters.values().any(|a| uses(&a.info)) + } + /// Process adapters which are required here. Iterate over all /// adapters and figure out what functions are required from the /// adapter itself, either because the functions are imported by the @@ -432,6 +447,8 @@ impl<'a> ComponentWorld<'a> { | Import::Item(_) | Import::ContextGet { .. } | Import::ContextSet { .. } + | Import::TlsBaseGet { .. } + | Import::TlsBaseSet { .. } | Import::BackpressureInc | Import::BackpressureDec | Import::WaitableSetNew diff --git a/crates/wit-component/src/linking.rs b/crates/wit-component/src/linking.rs index 0077d3711f..dda280eef7 100644 --- a/crates/wit-component/src/linking.rs +++ b/crates/wit-component/src/linking.rs @@ -28,6 +28,7 @@ use { indexmap::{IndexMap, IndexSet, map::Entry}, metadata::{Export, ExportKey, FunctionType, GlobalType, Metadata, Type, ValueType}, std::{ + cmp, collections::{BTreeMap, HashMap, HashSet}, fmt::Debug, hash::Hash, @@ -35,9 +36,9 @@ use { }, wasm_encoder::{ CodeSection, ConstExpr, DataSection, ElementSection, Elements, EntityType, ExportKind, - ExportSection, Function, FunctionSection, GlobalSection, ImportSection, Instruction as Ins, - MemArg, MemorySection, MemoryType, Module, RawCustomSection, RefType, StartSection, - TableSection, TableType, TypeSection, ValType, + ExportSection, Function, FunctionSection, GlobalSection, ImportSection, MemArg, + MemorySection, MemoryType, Module, RawCustomSection, RefType, StartSection, TableSection, + TableType, TypeSection, ValType, }, wasmparser::SymbolFlags, }; @@ -183,6 +184,134 @@ impl<'a> DlOpenables<'a> { } } +/// The layout of the whole program's thread-local storage bookkeeping. +/// +/// This generates a C structure that matches this layout: +/// +/// ```c +/// struct { +/// size_t num_libraries; +/// struct { +/// size_t __tls_size; +/// size_t __tls_align; +/// void (*__wasm_init_tls)(void*); +/// } *library_info; +/// void **main_thread_tls_base; +/// } __wasm_program_tls_info; +/// ``` +/// +/// where `main_thread_tls_base` is placed first, then `library_info`, then this +/// structure itself. +#[derive(Default)] +struct TlsLayout { + /// Address of the `main_thread_tls_base` array. + /// + /// This is left zero-initialized; no data segment covers it. + main_thread_tls_base: u32, + + /// Address of the `library_info` array. + library_info: u32, + + /// Address of the `__wasm_program_tls_info` struct itself. + program_info: u32, + + /// The libraries which have thread-local storage, in the order they appear + /// in `library_info`, paired with the table index reserved for each one's + /// `__wasm_init_tls`. + init_tls_functions: Vec<(usize, u32)>, + + /// For each library, the slot it uses in the array of TLS base pointers, or + /// `None` if it has no thread-local storage of its own. + slots: Vec>, + + /// Static contents of the `library_info` array and the + /// `__wasm_program_tls_info` struct, which live contiguously starting at + /// `library_info`. + buffer: Vec, +} + +impl TlsLayout { + /// Reserve linear memory and table space for the layout described above, + /// advancing `memory_offset` and `table_offset` past what's used. + /// + /// Nothing is reserved for a program which doesn't use thread-local storage + /// at all, and the `__wasm_program_tls_info` half is skipped unless some + /// library actually asks for it, which is only the case when the program + /// might spawn a thread. + fn new(metadata: &[Metadata], memory_offset: &mut u32, table_offset: &mut u32) -> Self { + let needs_tls_base = metadata + .iter() + .any(|m| m.needs_get_tls_base || m.needs_set_tls_base); + let needs_program_info = metadata.iter().any(|m| m.needs_program_tls_info); + if !needs_tls_base && !needs_program_info { + return Self::default(); + } + + // Filter out libraries that don't have TLS, and then sort this by + // biggest alignment first to help minimize the size of TLS blocks + // allocated. + let mut libraries = metadata + .iter() + .enumerate() + .filter(|(_, metadata)| metadata.has_tls_info) + .map(|(index, _)| index) + .collect::>(); + libraries.sort_by_key(|&index| cmp::Reverse(metadata[index].tls_align)); + + let mut slots = vec![None; metadata.len()]; + for (slot, index) in libraries.iter().enumerate() { + slots[*index] = Some(u32::try_from(slot).unwrap()); + } + let count = u32::try_from(libraries.len()).unwrap(); + + // Allocate space for `main_thread_tls_base` + *memory_offset = align(*memory_offset, 4); + let main_thread_tls_base = *memory_offset; + *memory_offset += count * 4; + + let mut library_info = 0; + let mut program_info = 0; + let mut init_tls_functions = Vec::new(); + let mut buffer = Vec::new(); + if needs_program_info { + // Allocate space for `library_info` + library_info = *memory_offset; + *memory_offset += count * 12; + // Allocate space for `__wasm_program_tls_info` + program_info = *memory_offset; + *memory_offset += 12; + + init_tls_functions = libraries + .iter() + .map(|&index| (index, get_and_increment(table_offset))) + .collect::>(); + + for &(index, table_index) in &init_tls_functions { + write_u32(&mut buffer, metadata[index].tls_size); + write_u32(&mut buffer, metadata[index].tls_align); + write_u32(&mut buffer, table_index); + } + write_u32(&mut buffer, count); + write_u32(&mut buffer, library_info); + write_u32(&mut buffer, main_thread_tls_base); + } + + Self { + main_thread_tls_base, + library_info, + program_info, + init_tls_functions, + slots, + buffer, + } + } + + /// The slot library `index` uses in the array of TLS base pointers. + fn slot(&self, index: usize) -> Option { + self.slots.get(index).copied().flatten() + } +} + fn write_u32(buffer: &mut Vec, value: u32) { buffer.extend(value.to_le_bytes()); } @@ -286,7 +415,7 @@ fn make_env_module<'a>( env_exports: &[EnvExport<'_>], cabi_realloc_exporter: Option<&str>, stack_size_bytes: u32, -) -> (Vec, DlOpenables<'a>, u32) { +) -> (Vec, DlOpenables<'a>, TlsLayout, u32) { // TODO: deduplicate types let mut types = TypeSection::new(); let mut imports = ImportSection::new(); @@ -354,6 +483,25 @@ fn make_env_module<'a>( exports.export(CABI_REALLOC, ExportKind::Func, index); } + // If tls base shims are being generated, and something might spawn a + // thread, then the shims generated will need access to `context.get 1`. + let indirect_tls_base = metadata + .iter() + .any(|m| m.needs_get_tls_base || m.needs_set_tls_base) + && metadata.iter().any(|m| m.uses_thread_new_indirect); + let tls_context_get = if indirect_tls_base { + let index = get_and_increment(&mut function_count); + types.ty().function([], [ValType::I32]); + imports.import( + metadata::ROOT, + metadata::CONTEXT_GET_1, + EntityType::Function(index), + ); + Some(index) + } else { + None + }; + let mut add_global_export = |name: &str, value, mutable| { let index = globals.len(); globals.global( @@ -380,6 +528,12 @@ fn make_env_module<'a>( table_offset += dl_openables.function_count; memory_offset += u32::try_from(dl_openables.buffer.len()).unwrap(); + let tls = TlsLayout::new(metadata, &mut memory_offset, &mut table_offset); + + if metadata.iter().any(|m| m.needs_program_tls_info) { + add_global_export(metadata::PROGRAM_TLS_INFO, tls.program_info, true); + } + let memory_size = { if metadata.iter().any(|m| m.needs_stack_pointer) { add_global_export(metadata::STACK_POINTER, stack_size_bytes, true); @@ -486,20 +640,141 @@ fn make_env_module<'a>( functions.function(u32::try_from(index).unwrap()); let mut function = Function::new([]); for local in 0..export.ty.parameters.len() { - function.instruction(&Ins::LocalGet(u32::try_from(local).unwrap())); + function + .instructions() + .local_get(u32::try_from(local).unwrap()); } - function.instruction(&Ins::I32Const(i32::try_from(table_offset).unwrap())); - function.instruction(&Ins::CallIndirect { - type_index: u32::try_from(index).unwrap(), - table_index: 0, - }); - function.instruction(&Ins::End); + function + .instructions() + .i32_const(i32::try_from(table_offset).unwrap()) + .call_indirect(0, u32::try_from(index).unwrap()) + .end(); code.function(&function); exports.export(export.name, ExportKind::Func, index); table_offset += 1; } + // Define a distinct `__wasm_{get,set}_tls_base` pair for each library that + // needs one. Each pair reads and writes that library's slot of the array of + // pointers described by `TlsLayout`. + for (index, metadata) in metadata.iter().enumerate() { + // A library with no thread-local storage of its own has no slot in the + // array. If it still imports these then synthesize functions that trap + // since they shouldn't ever be called. + let Some(slot) = tls.slot(index) else { + for (needed, name, params, results) in [ + ( + metadata.needs_get_tls_base, + metadata::GET_TLS_BASE, + &[][..], + &[ValType::I32][..], + ), + ( + metadata.needs_set_tls_base, + metadata::SET_TLS_BASE, + &[ValType::I32][..], + &[][..], + ), + ] { + if !needed { + continue; + } + let func = get_and_increment(&mut function_count); + types + .ty() + .function(params.iter().copied(), results.iter().copied()); + functions.function(func); + let mut function = Function::new([]); + function.instructions().unreachable().end(); + code.function(&function); + exports.export(&format!("{}:{name}", metadata.name), ExportKind::Func, func); + } + continue; + }; + + let mem_arg = MemArg { + offset: u64::from(slot * 4), + align: 2, + memory_index: 0, + }; + + if metadata.needs_get_tls_base { + let func = get_and_increment(&mut function_count); + types.ty().function([], [ValType::I32]); + functions.function(func); + let mut function = Function::new([]); + // With coop threads the base pointer is in `context.get 1`. Without + // coop threads the base pointer is `main_thread_tls_base` itself. + match tls_context_get { + Some(get) => { + function.instructions().call(get); + } + None => { + function + .instructions() + .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap()); + } + } + function.instructions().i32_load(mem_arg).end(); + code.function(&function); + exports.export( + &format!("{}:{}", metadata.name, metadata::GET_TLS_BASE), + ExportKind::Func, + func, + ); + } + + if metadata.needs_set_tls_base { + let func = get_and_increment(&mut function_count); + types.ty().function([ValType::I32], []); + functions.function(func); + let mut function = Function::new_with_locals_types(if tls_context_get.is_some() { + vec![ValType::I32] + } else { + vec![] + }); + // With coop threads this intrinsic conditionally initializes + // `main_thread_tls_base` based on `context.get 1`. Otherwise it + // writes through to it if it's set. + // + // Without coop threads this is updating `main_thread_tls_base`. + match tls_context_get { + Some(get) => { + function + .instructions() + .call(get) + .local_tee(1) + .i32_eqz() + .if_(wasm_encoder::BlockType::Empty) + .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap()) + .local_get(0) + .i32_store(mem_arg) + .else_() + .local_get(1) + .local_get(0) + .i32_store(mem_arg) + .end() + .end(); + } + None => { + function + .instructions() + .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap()) + .local_get(0) + .i32_store(mem_arg) + .end(); + } + } + code.function(&function); + exports.export( + &format!("{}:{}", metadata.name, metadata::SET_TLS_BASE), + ExportKind::Func, + func, + ); + } + } + for (import, offset) in import_map { exports.export( &format!("{}:{}", import.module, import.name), @@ -553,7 +828,7 @@ fn make_env_module<'a>( let module = module.finish(); wasmparser::validate(&module).unwrap(); - (module, dl_openables, indirection_table_base) + (module, dl_openables, tls, indirection_table_base) } /// Synthesize the "init" module, responsible for initializing global variables per the dynamic linking tool @@ -565,6 +840,7 @@ fn make_init_module( exporters: &IndexMap<&ExportKey, (&str, &Export)>, env_exports: &[EnvExport<'_>], dl_openables: DlOpenables, + tls: TlsLayout, indirection_table_base: u32, ) -> Result> { let mut module = Module::new(); @@ -574,6 +850,7 @@ fn make_init_module( types.ty().function([], []); let thunk_ty = 0; types.ty().function([ValType::I32], []); + let init_tls_ty = 1; let mut type_offset = 2; for metadata in metadata { @@ -650,52 +927,80 @@ fn make_init_module( }) }; - let mut memory_address_inits = Vec::new(); - let mut reloc_calls = Vec::new(); - let mut ctor_calls = Vec::new(); + let mut start = Function::new([]); + let mut names = HashMap::new(); + for (index, metadata) in metadata.iter().enumerate() { + names.insert_unique(index, metadata.name); + } for (exporter, export, address) in dl_openables.global_addresses.iter() { - memory_address_inits.push(Ins::I32Const(i32::try_from(*address).unwrap())); - memory_address_inits.push(Ins::GlobalGet(add_global_import( + let memory_base = add_global_import( &mut imports, metadata::ENV, &format!("{exporter}:memory_base"), false, - ))); - memory_address_inits.push(Ins::GlobalGet(add_global_import( - &mut imports, - exporter, - export, - false, - ))); - memory_address_inits.push(Ins::I32Add); - memory_address_inits.push(Ins::I32Store(MemArg { - offset: 0, - align: 2, - memory_index: 0, - })); + ); + let export = add_global_import(&mut imports, exporter, export, false); + start + .instructions() + .i32_const(i32::try_from(*address).unwrap()) + .global_get(memory_base) + .global_get(export) + .i32_add() + .i32_store(MemArg { + offset: 0, + align: 2, + memory_index: 0, + }); } - let mut init_task_exporter = exporters - .get(&ExportKey { - name: metadata::INIT_TASK, - ty: Type::Function(EMPTY_FUNCTION_TYPE.clone()), - }) - .map(|(name, _)| name); + for metadata in metadata { + for import in &metadata.memory_address_imports { + let (exporter, _) = find_offset_exporter(import, exporters)?; - for (index, metadata) in metadata.iter().enumerate() { - names.insert_unique(index, metadata.name); + let memory_base = add_global_import( + &mut imports, + metadata::ENV, + &format!("{exporter}:memory_base"), + false, + ); + let offset = add_global_import(&mut imports, exporter, import, false); + let address = add_global_import( + &mut imports, + metadata::ENV, + &format!("{}:{import}", metadata.name), + true, + ); + start + .instructions() + .global_get(memory_base) + .global_get(offset) + .i32_add() + .global_set(address); + } + } + for metadata in metadata { if metadata.has_data_relocs { - reloc_calls.push(Ins::Call(add_function_import( + let func = add_function_import( &mut imports, metadata.name, metadata::APPLY_DATA_RELOCS, thunk_ty, - ))); + ); + start.instructions().call(func); } + } + + let mut init_task_exporter = exporters + .get(&ExportKey { + name: metadata::INIT_TASK, + ty: Type::Function(EMPTY_FUNCTION_TYPE.clone()), + }) + .map(|(name, _)| name); + for metadata in metadata { if metadata.has_ctors && metadata.has_initialize { bail!( "library {} exports both `{}` and `{}`; \ @@ -717,54 +1022,20 @@ fn make_init_module( // `init_task_exporter` to `None` to avoid calling it again: init_task_exporter = None; - ctor_calls.push(Ins::Call(add_function_import( - &mut imports, - exporter, - metadata::INIT_TASK, - thunk_ty, - ))); + let func = add_function_import(&mut imports, exporter, metadata::INIT_TASK, thunk_ty); + start.instructions().call(func); } if metadata.has_ctors { - ctor_calls.push(Ins::Call(add_function_import( - &mut imports, - metadata.name, - metadata::CALL_CTORS, - thunk_ty, - ))); + let func = + add_function_import(&mut imports, metadata.name, metadata::CALL_CTORS, thunk_ty); + start.instructions().call(func); } if metadata.has_initialize { - ctor_calls.push(Ins::Call(add_function_import( - &mut imports, - metadata.name, - metadata::INITIALIZE, - thunk_ty, - ))); - } - - for import in &metadata.memory_address_imports { - let (exporter, _) = find_offset_exporter(import, exporters)?; - - memory_address_inits.push(Ins::GlobalGet(add_global_import( - &mut imports, - metadata::ENV, - &format!("{exporter}:memory_base"), - false, - ))); - memory_address_inits.push(Ins::GlobalGet(add_global_import( - &mut imports, - exporter, - import, - false, - ))); - memory_address_inits.push(Ins::I32Add); - memory_address_inits.push(Ins::GlobalSet(add_global_import( - &mut imports, - metadata::ENV, - &format!("{}:{import}", metadata.name), - true, - ))); + let func = + add_function_import(&mut imports, metadata.name, metadata::INITIALIZE, thunk_ty); + start.instructions().call(func); } } @@ -796,6 +1067,22 @@ fn make_init_module( }) .collect::>(); + // Each library's `__wasm_init_tls` needs a table slot so that wasi-libc + // can call it through the function pointer in `library_info`. Everything + // else about the layout is known statically and lives in a data segment. + let init_tls_functions = tls + .init_tls_functions + .iter() + .map(|&(index, _)| { + add_function_import( + &mut imports, + metadata[index].name, + metadata::INIT_TLS, + init_tls_ty, + ) + }) + .collect::>(); + module.section(&imports); { @@ -820,26 +1107,28 @@ fn make_init_module( &const_u32(indirection_table_base), Elements::Functions(indirections.into()), ); + if let Some((_, table_base)) = tls.init_tls_functions.first() { + elements.active( + None, + &const_u32(*table_base), + Elements::Functions(init_tls_functions.into()), + ); + } module.section(&elements); } { let mut code = CodeSection::new(); - let mut function = Function::new([]); - for ins in memory_address_inits - .iter() - .chain(&reloc_calls) - .chain(&ctor_calls) - { - function.instruction(ins); - } - function.instruction(&Ins::End); - code.function(&function); + start.instructions().end(); + code.function(&start); module.section(&code); } let mut data = DataSection::new(); data.active(0, &const_u32(dl_openables.memory_base), dl_openables.buffer); + if !tls.buffer.is_empty() { + data.active(0, &const_u32(tls.library_info), tls.buffer); + } module.section(&data); module.section(&RawCustomSection( @@ -1313,8 +1602,7 @@ fn make_stubs_module(missing: &[(&str, Export)]) -> Vec { ); functions.function(offset); let mut function = Function::new([]); - function.instruction(&Ins::Unreachable); - function.instruction(&Ins::End); + function.instructions().unreachable().end(); code.function(&function); exports.export(name, ExportKind::Func, offset); } @@ -1591,7 +1879,7 @@ impl Linker { reexport_cabi_realloc, } = env_exports(&metadata, &exporters, &topo_sorted)?; - let (env_module, dl_openables, table_base) = make_env_module( + let (env_module, dl_openables, tls, table_base) = make_env_module( &metadata, &env_exports, if reexport_cabi_realloc { @@ -1665,6 +1953,20 @@ impl Linker { name: format!("{name}:table_base"), }, ]) + .chain( + [ + (metadata.needs_get_tls_base, metadata::GET_TLS_BASE), + (metadata.needs_set_tls_base, metadata::SET_TLS_BASE), + ] + .into_iter() + .filter(|(needed, _)| *needed) + .map(|(_, intrinsic)| Item { + alias: intrinsic.into(), + kind: ExportKind::Func, + which: MainOrAdapter::Main, + name: format!("{name}:{intrinsic}"), + }), + ) .chain(metadata.env_imports.iter().map(|(name, (ty, _))| { let (exporter, _) = find_function_exporter(name, ty, &exporters).unwrap(); @@ -1752,6 +2054,7 @@ impl Linker { metadata::STACK_HIGH, metadata::STACK_LOW, metadata::LIBDL_LIBRARIES, + metadata::PROGRAM_TLS_INFO, ] .into_iter() .map(|name| Item { @@ -1811,6 +2114,7 @@ impl Linker { &exporters, &env_exports, dl_openables, + tls, table_base, )?, LibraryInfo { diff --git a/crates/wit-component/src/linking/metadata.rs b/crates/wit-component/src/linking/metadata.rs index 4dcfcb6ae4..de687ba47a 100644 --- a/crates/wit-component/src/linking/metadata.rs +++ b/crates/wit-component/src/linking/metadata.rs @@ -8,8 +8,8 @@ use { fmt, }, wasmparser::{ - Dylink0Subsection, ExternalKind, FuncType, KnownCustom, MemInfo, Parser, Payload, RefType, - SymbolFlags, TableType, TagKind, TagType, TypeRef, ValType, + Dylink0Subsection, ExternalKind, FuncType, KnownCustom, MemInfo, Operator, Parser, Payload, + RefType, SymbolFlags, TableType, TagKind, TagType, TypeRef, ValType, }, }; @@ -35,6 +35,17 @@ pub const START: &str = "_start"; pub const LIBDL_LIBRARIES: &str = "__wasm_libdl_libraries"; pub const INIT_TASK: &str = "__wasm_init_task"; pub const INIT_ASYNC_TASK: &str = "__wasm_init_async_task"; +pub const ROOT: &str = "$root"; +pub const THREAD_NEW_INDIRECT: &str = "[thread-new-indirect-v0]"; +pub const CONTEXT_GET_1: &str = "[context-get-1]"; +pub const GET_STACK_POINTER: &str = "__wasm_get_stack_pointer"; +pub const SET_STACK_POINTER: &str = "__wasm_set_stack_pointer"; +pub const GET_TLS_BASE: &str = "__wasm_get_tls_base"; +pub const SET_TLS_BASE: &str = "__wasm_set_tls_base"; +pub const TLS_SIZE: &str = "__tls_size"; +pub const TLS_ALIGN: &str = "__tls_align"; +pub const INIT_TLS: &str = "__wasm_init_tls"; +pub const PROGRAM_TLS_INFO: &str = "__wasm_program_tls_info"; /// Represents a core Wasm value type (not including V128 or reference types, which are not yet supported) #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -241,6 +252,36 @@ pub struct Metadata<'a> { /// Whether this module imports `__stack_low` pub needs_stack_low: bool, + /// Whether this module imports `env::__wasm_get_tls_base` + pub needs_get_tls_base: bool, + + /// Whether this module imports `env::__wasm_set_tls_base` + pub needs_set_tls_base: bool, + + /// Whether this module imports the address of `__wasm_program_tls_info` + pub needs_program_tls_info: bool, + + /// Whether this module imports `$root::[thread-new-indirect-v0]`, meaning + /// the program may spawn threads and is thus using cooperative threading. + pub uses_thread_new_indirect: bool, + + /// Whether this module has thread-local storage of its own which needs + /// allocating and initializing on a freshly spawned thread. + /// + /// This requires the module to export all of `__tls_size`, `__tls_align`, + /// and `__wasm_init_tls`, and to have a non-zero `tls_size`. + pub has_tls_info: bool, + + /// The size of this module's thread-local storage, i.e. the value of the + /// `__tls_size` global that `wasm-ld` synthesized for it. + /// + /// Zero if the module has no thread-local storage at all. + pub tls_size: u32, + + /// The alignment of this module's thread-local storage, i.e. the value of + /// the `__tls_align` global. At least one whenever `has_tls_info` is set. + pub tls_align: u32, + /// The functions imported from the `env` module, if any pub env_imports: BTreeSet<(&'a str, (FunctionType, SymbolFlags))>, @@ -296,6 +337,13 @@ impl<'a> Metadata<'a> { needs_heap_end: false, needs_stack_high: false, needs_stack_low: false, + needs_get_tls_base: false, + needs_set_tls_base: false, + needs_program_tls_info: false, + uses_thread_new_indirect: false, + has_tls_info: false, + tls_size: 0, + tls_align: 0, env_imports: BTreeSet::new(), memory_address_imports: BTreeSet::new(), table_address_imports: BTreeSet::new(), @@ -306,6 +354,10 @@ impl<'a> Metadata<'a> { let mut types = Vec::new(); let mut function_types = Vec::new(); let mut global_types = Vec::new(); + let mut global_values = Vec::new(); + let mut tls_size = None; + let mut tls_align = None; + let mut has_init_tls = false; let mut tag_types = Vec::new(); let mut import_info = HashMap::new(); let mut export_info = HashMap::new(); @@ -353,7 +405,10 @@ impl<'a> Metadata<'a> { match import.ty { TypeRef::Func(ty) => function_types.push(usize::try_from(ty).unwrap()), - TypeRef::Global(ty) => global_types.push(ty), + TypeRef::Global(ty) => { + global_types.push(ty); + global_values.push(None); + } TypeRef::Tag(ty) => tag_types.push(ty), _ => (), } @@ -424,6 +479,22 @@ impl<'a> Metadata<'a> { return type_error(); } } + ( + self::ENV, + name @ (self::GET_STACK_POINTER + | self::SET_STACK_POINTER + | self::GET_TLS_BASE + | self::SET_TLS_BASE), + ) => { + if !matches!(import.ty, TypeRef::Func(_)) { + return type_error(); + } + match name { + self::GET_TLS_BASE => result.needs_get_tls_base = true, + self::SET_TLS_BASE => result.needs_set_tls_base = true, + _ => {} + } + } (self::ENV, name) => match import.ty { TypeRef::Func(ty) => { result.env_imports.insert(( @@ -466,6 +537,9 @@ impl<'a> Metadata<'a> { self::LIBDL_LIBRARIES => { result.needs_libdl_libraries = true; } + self::PROGRAM_TLS_INFO => { + result.needs_program_tls_info = true; + } _ => { result.memory_address_imports.insert(name); @@ -486,6 +560,9 @@ impl<'a> Metadata<'a> { return type_error(); } } + (self::ROOT, self::THREAD_NEW_INDIRECT) => { + result.uses_thread_new_indirect = true; + } (module, name) if adapter_names.contains(module) => { let ty = match import.ty { TypeRef::Global(wasmparser::GlobalType { @@ -532,7 +609,15 @@ impl<'a> Metadata<'a> { Payload::GlobalSection(reader) => { for global in reader { - global_types.push(global?.ty); + let global = global?; + global_types.push(global.ty); + let mut ops = global.init_expr.get_operators_reader(); + global_values.push(match (ops.read(), ops.read()) { + (Ok(Operator::I32Const { value }), Ok(Operator::End)) => { + Some(value as u32) + } + _ => None, + }); } } @@ -546,11 +631,20 @@ impl<'a> Metadata<'a> { for export in reader { let export = export?; + let global_value = || { + global_values + .get(usize::try_from(export.index).unwrap()) + .copied() + .flatten() + }; match export.name { self::APPLY_DATA_RELOCS => result.has_data_relocs = true, self::CALL_CTORS => result.has_ctors = true, self::INITIALIZE => result.has_initialize = true, self::START => result.has_wasi_start = true, + self::TLS_SIZE => tls_size = global_value(), + self::TLS_ALIGN => tls_align = global_value(), + self::INIT_TLS => has_init_tls = true, _ => { if export.name == self::INIT_TASK { result.has_init_task = true; @@ -601,6 +695,32 @@ impl<'a> Metadata<'a> { } } + // A module only participates in whole-program TLS setup if the linker + // driver arranged for these to be exported and it actually has some + // thread-local storage. Anything else (a library built without those + // exports, or the synthesized stubs module) is left out of + // `__wasm_program_tls_info`: it never reads a TLS base. + result.tls_size = tls_size.unwrap_or(0); + result.tls_align = tls_align.unwrap_or(0); + result.has_tls_info = result.tls_size > 0; + if result.has_tls_info { + if !has_init_tls { + bail!( + "{} has {} bytes of thread-local storage but does not export `{}`", + result.name, + result.tls_size, + self::INIT_TLS, + ); + } + if result.tls_align == 0 { + bail!( + "{} has {} bytes of thread-local storage but no alignment", + result.name, + result.tls_size + ); + } + } + Ok(result) } } diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs index 898343d48b..eeddc597dc 100644 --- a/crates/wit-component/src/validation.rs +++ b/crates/wit-component/src/validation.rs @@ -290,6 +290,24 @@ pub enum Import { slot: u32, }, + /// The `__wasm_get_tls_base` function that LLVM emits to read the base + /// pointer of this module's thread-local storage. + /// + /// Unlike [`Import::ContextGet`] this is not tied to a particular storage + /// mechanism: how it's satisfied depends on whether the program uses + /// cooperative threading. See + /// `EncodingState::materialize_tls_base_import` for the details. + TlsBaseGet { + /// The type of the base pointer (`i32` or `i64`). + ty: ValType, + }, + + /// The `__wasm_set_tls_base` counterpart to [`Import::TlsBaseGet`]. + TlsBaseSet { + /// The type of the base pointer (`i32` or `i64`). + ty: ValType, + }, + /// A `canon backpressure.inc` intrinsic. BackpressureInc, @@ -2608,13 +2626,16 @@ impl NameMangling for Legacy { let ty = *ty.params().get(0)?; Some(Import::ContextSet { ty, slot: 0 }) } + // TLS handling is slightly different than above to handle + // coop-threading-vs-not, so the exact resolution of this import is + // deferred to later. "__wasm_get_tls_base" => { let ty = *ty.results().get(0)?; - Some(Import::ContextGet { ty, slot: 1 }) + Some(Import::TlsBaseGet { ty }) } "__wasm_set_tls_base" => { let ty = *ty.params().get(0)?; - Some(Import::ContextSet { ty, slot: 1 }) + Some(Import::TlsBaseSet { ty }) } _ => None, } diff --git a/crates/wit-component/tests/components/error-link-tls-without-init/error.txt b/crates/wit-component/tests/components/error-link-tls-without-init/error.txt new file mode 100644 index 0000000000..49630a8de8 --- /dev/null +++ b/crates/wit-component/tests/components/error-link-tls-without-init/error.txt @@ -0,0 +1 @@ +failed to extract linking metadata from foo: foo has 32 bytes of thread-local storage but does not export `__wasm_init_tls` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wat b/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wat new file mode 100644 index 0000000000..73ad41a810 --- /dev/null +++ b/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wat @@ -0,0 +1,20 @@ +;; A library with thread-local storage which doesn't export the +;; `__wasm_init_tls` that initializes it. Nothing could set this module's TLS up +;; on a spawned thread, so linking has to fail rather than silently leave it +;; uninitialized. +(module + (@dylink.0 + (mem-info (memory 8 4)) + ) + (type $void (func)) + (type $get (func (result i32))) + + (global $__tls_size i32 i32.const 32) + (global $__tls_align i32 i32.const 16) + + (func $foo (type $get) i32.const 7) + + (export "__tls_size" (global $__tls_size)) + (export "__tls_align" (global $__tls_align)) + (export "test:test/test#foo" (func $foo)) +) diff --git a/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wit b/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wit new file mode 100644 index 0000000000..be962ede03 --- /dev/null +++ b/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wit @@ -0,0 +1,9 @@ +package test:test; + +interface test { + foo: func() -> s32; +} + +world lib-foo { + export test; +} diff --git a/crates/wit-component/tests/components/link-initialize/component.wat b/crates/wit-component/tests/components/link-initialize/component.wat index 31abefee85..48def58f7f 100644 --- a/crates/wit-component/tests/components/link-initialize/component.wat +++ b/crates/wit-component/tests/components/link-initialize/component.wat @@ -148,16 +148,16 @@ (type (;1;) (func (param i32))) (import "env" "memory" (memory (;0;) 0)) (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) - (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) - (import "bar" "_initialize" (func (;1;) (type 0))) (import "env" "foo:memory_base" (global (;0;) i32)) (import "foo" "well" (global (;1;) i32)) (import "env" "bar:well" (global (;2;) (mut i32))) - (import "foo" "__wasm_apply_data_relocs" (func (;2;) (type 0))) - (import "foo" "_initialize" (func (;3;) (type 0))) (import "env" "bar:memory_base" (global (;3;) i32)) (import "bar" "um" (global (;4;) i32)) (import "env" "foo:um" (global (;5;) (mut i32))) + (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) + (import "foo" "__wasm_apply_data_relocs" (func (;1;) (type 0))) + (import "bar" "_initialize" (func (;2;) (type 0))) + (import "foo" "_initialize" (func (;3;) (type 0))) (start 4) (elem (;0;) (i32.const 1) func) (elem (;1;) (i32.const 1) func) @@ -171,8 +171,8 @@ i32.add global.set 5 call 0 - call 2 call 1 + call 2 call 3 ) (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") @@ -243,8 +243,8 @@ ) (core instance $__init (;10;) (instantiate $__init (with "env" (instance $main)) - (with "bar" (instance $bar)) (with "foo" (instance $foo)) + (with "bar" (instance $bar)) ) ) (type (;1;) (func (param "v" s32) (result s32))) diff --git a/crates/wit-component/tests/components/link-stub-wasip2/component.wat b/crates/wit-component/tests/components/link-stub-wasip2/component.wat index 47436ff025..49c9c2b44f 100644 --- a/crates/wit-component/tests/components/link-stub-wasip2/component.wat +++ b/crates/wit-component/tests/components/link-stub-wasip2/component.wat @@ -160,16 +160,16 @@ (type (;1;) (func (param i32))) (import "env" "memory" (memory (;0;) 0)) (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) - (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) - (import "bar" "__wasm_call_ctors" (func (;1;) (type 0))) (import "env" "foo:memory_base" (global (;0;) i32)) (import "foo" "well" (global (;1;) i32)) (import "env" "bar:well" (global (;2;) (mut i32))) - (import "foo" "__wasm_apply_data_relocs" (func (;2;) (type 0))) - (import "foo" "__wasm_call_ctors" (func (;3;) (type 0))) (import "env" "bar:memory_base" (global (;3;) i32)) (import "bar" "um" (global (;4;) i32)) (import "env" "foo:um" (global (;5;) (mut i32))) + (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) + (import "foo" "__wasm_apply_data_relocs" (func (;1;) (type 0))) + (import "bar" "__wasm_call_ctors" (func (;2;) (type 0))) + (import "foo" "__wasm_call_ctors" (func (;3;) (type 0))) (start 4) (elem (;0;) (i32.const 1) func) (elem (;1;) (i32.const 1) func) @@ -183,8 +183,8 @@ i32.add global.set 5 call 0 - call 2 call 1 + call 2 call 3 ) (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") @@ -302,8 +302,8 @@ ) (core instance $__init (;16;) (instantiate $__init (with "env" (instance $main)) - (with "bar" (instance $bar)) (with "foo" (instance $foo)) + (with "bar" (instance $bar)) ) ) (type (;1;) (func (param "v" s32) (result s32))) diff --git a/crates/wit-component/tests/components/link-wasip3-abi/component.wat b/crates/wit-component/tests/components/link-wasip3-abi/component.wat index d0ee5128eb..f0eb6f6c2c 100644 --- a/crates/wit-component/tests/components/link-wasip3-abi/component.wat +++ b/crates/wit-component/tests/components/link-wasip3-abi/component.wat @@ -178,21 +178,21 @@ (type (;3;) (func)) (import "env" "memory" (memory (;0;) 0)) (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) - (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) - (import "c" "__wasm_init_task" (func (;1;) (type 0))) - (import "bar" "__wasm_call_ctors" (func (;2;) (type 0))) (import "env" "foo:memory_base" (global (;0;) i32)) (import "foo" "well" (global (;1;) i32)) (import "env" "bar:well" (global (;2;) (mut i32))) - (import "foo" "__wasm_apply_data_relocs" (func (;3;) (type 0))) - (import "foo" "__wasm_call_ctors" (func (;4;) (type 0))) (import "env" "bar:memory_base" (global (;3;) i32)) (import "bar" "um" (global (;4;) i32)) (import "env" "foo:um" (global (;5;) (mut i32))) + (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) + (import "foo" "__wasm_apply_data_relocs" (func (;1;) (type 0))) + (import "c" "__wasm_init_task" (func (;2;) (type 0))) + (import "bar" "__wasm_call_ctors" (func (;3;) (type 0))) + (import "foo" "__wasm_call_ctors" (func (;4;) (type 0))) (import "c" "__wasm_init_async_task" (func (;5;) (type 3))) (start 6) (elem (;0;) (i32.const 1) func) - (elem (;1;) (i32.const 1) func 1 5) + (elem (;1;) (i32.const 1) func 2 5) (func (;6;) (type 0) global.get 0 global.get 1 @@ -203,9 +203,9 @@ i32.add global.set 5 call 0 - call 3 call 1 call 2 + call 3 call 4 ) (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") @@ -228,8 +228,8 @@ (alias core export $main "__indirect_function_table" (core table $__indirect_function_table (;0;))) (alias core export $main "foo:memory_base" (core global $foo:memory_base (;2;))) (alias core export $main "foo:table_base" (core global $foo:table_base (;3;))) - (alias core export $c "__wasm_get_stack_pointer" (core func $__wasm_get_stack_pointer (;0;))) - (alias core export $c "__wasm_set_stack_pointer" (core func $__wasm_set_stack_pointer (;1;))) + (core func $"context.get 0" (;0;) (canon context.get i32 0)) + (core func $"context.set 0" (;1;) (canon context.set i32 0)) (alias core export $c "malloc" (core func $malloc (;2;))) (alias core export $c "abort" (core func $abort (;3;))) (core instance $env (;3;) @@ -237,8 +237,8 @@ (export "__indirect_function_table" (table $__indirect_function_table)) (export "__memory_base" (global $foo:memory_base)) (export "__table_base" (global $foo:table_base)) - (export "__wasm_get_stack_pointer" (func $__wasm_get_stack_pointer)) - (export "__wasm_set_stack_pointer" (func $__wasm_set_stack_pointer)) + (export "__wasm_get_stack_pointer" (func $"context.get 0")) + (export "__wasm_set_stack_pointer" (func $"context.set 0")) (export "malloc" (func $malloc)) (export "abort" (func $abort)) ) @@ -278,9 +278,9 @@ ) (core instance $__init (;10;) (instantiate $__init (with "env" (instance $main)) + (with "foo" (instance $foo)) (with "bar" (instance $bar)) (with "c" (instance $c)) - (with "foo" (instance $foo)) ) ) (core module $init-task-wrappers (;5;) diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wat b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wat new file mode 100644 index 0000000000..1fcffc47cc --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wat @@ -0,0 +1,182 @@ +(component + (core module $main (;0;) + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (type (;2;) (func (result i32))) + (type (;3;) (func (param i32))) + (table (;0;) 1 funcref) + (memory (;0;) 17) + (global (;0;) i32 i32.const 1048592) + (global (;1;) i32 i32.const 1) + (global (;2;) i32 i32.const 1048608) + (global (;3;) i32 i32.const 1) + (export "c:memory_base" (global 0)) + (export "c:table_base" (global 1)) + (export "foo:memory_base" (global 2)) + (export "foo:table_base" (global 3)) + (export "c:__wasm_get_tls_base" (func 0)) + (export "c:__wasm_set_tls_base" (func 1)) + (export "foo:__wasm_get_tls_base" (func 2)) + (export "foo:__wasm_set_tls_base" (func 3)) + (export "__indirect_function_table" (table 0)) + (export "memory" (memory 0)) + (func (;0;) (type 0) (result i32) + i32.const 1048584 + i32.load offset=4 + ) + (func (;1;) (type 1) (param i32) + i32.const 1048584 + local.get 0 + i32.store offset=4 + ) + (func (;2;) (type 2) (result i32) + i32.const 1048584 + i32.load + ) + (func (;3;) (type 3) (param i32) + i32.const 1048584 + local.get 0 + i32.store + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $c (;1;) + (@dylink.0 + (mem-info (memory 16 4)) + ) + (type $void (;0;) (func)) + (type $get (;1;) (func (result i32))) + (type $set (;2;) (func (param i32))) + (type (;3;) (func (param i32) (result i32))) + (import "env" "memory" (memory (;0;) 1)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "env" "__memory_base" (global $__memory_base (;0;) i32)) + (import "env" "__table_base" (global $__table_base (;1;) i32)) + (import "env" "__wasm_get_stack_pointer" (func $get_sp (;0;) (type $get))) + (import "env" "__wasm_set_stack_pointer" (func $set_sp (;1;) (type $set))) + (import "env" "__wasm_get_tls_base" (func $get_tls (;2;) (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (;3;) (type $set))) + (global (;2;) i32 i32.const 148) + (global (;3;) i32 i32.const 4) + (export "__tls_size" (global 2)) + (export "__tls_align" (global 3)) + (export "__wasm_init_tls" (func 4)) + (export "abort" (func $abort)) + (func (;4;) (type $set) (param i32) + unreachable + ) + (func $malloc (;5;) (type 3) (param i32) (result i32) + unreachable + ) + (func $abort (;6;) (type $void) + unreachable + ) + ) + (core module $foo (;2;) + (@dylink.0 + (mem-info (memory 8 4)) + (needed "c") + ) + (type $void (;0;) (func)) + (type $get (;1;) (func (result i32))) + (type $set (;2;) (func (param i32))) + (import "env" "memory" (memory (;0;) 1)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "env" "__memory_base" (global $__memory_base (;0;) i32)) + (import "env" "__table_base" (global $__table_base (;1;) i32)) + (import "env" "__wasm_get_tls_base" (func $get_tls (;0;) (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (;1;) (type $set))) + (import "env" "abort" (func $abort (;2;) (type $void))) + (global (;2;) i32 i32.const 32) + (global (;3;) i32 i32.const 16) + (export "__tls_size" (global 2)) + (export "__tls_align" (global 3)) + (export "__wasm_init_tls" (func 3)) + (export "test:test/test#foo" (func $foo)) + (func (;3;) (type $set) (param i32) + unreachable + ) + (func $foo (;4;) (type $get) (result i32) + call $get_tls + i32.load + ) + ) + (core module $__init (;3;) + (type (;0;) (func)) + (type (;1;) (func (param i32))) + (import "env" "memory" (memory (;0;) 0)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (start 0) + (elem (;0;) (i32.const 1) func) + (elem (;1;) (i32.const 1) func) + (func (;0;) (type 0)) + (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core instance $main (;0;) (instantiate $main)) + (alias core export $main "memory" (core memory $memory (;0;))) + (alias core export $main "__indirect_function_table" (core table $__indirect_function_table (;0;))) + (alias core export $main "c:memory_base" (core global $c:memory_base (;0;))) + (alias core export $main "c:table_base" (core global $c:table_base (;1;))) + (core func $"context.get 0" (;0;) (canon context.get i32 0)) + (core func $"context.set 0" (;1;) (canon context.set i32 0)) + (alias core export $main "c:__wasm_get_tls_base" (core func $c:__wasm_get_tls_base (;2;))) + (alias core export $main "c:__wasm_set_tls_base" (core func $c:__wasm_set_tls_base (;3;))) + (core instance $env (;1;) + (export "memory" (memory $memory)) + (export "__indirect_function_table" (table $__indirect_function_table)) + (export "__memory_base" (global $c:memory_base)) + (export "__table_base" (global $c:table_base)) + (export "__wasm_get_stack_pointer" (func $"context.get 0")) + (export "__wasm_set_stack_pointer" (func $"context.set 0")) + (export "__wasm_get_tls_base" (func $c:__wasm_get_tls_base)) + (export "__wasm_set_tls_base" (func $c:__wasm_set_tls_base)) + ) + (core instance $c (;2;) (instantiate $c + (with "env" (instance $env)) + ) + ) + (alias core export $main "foo:memory_base" (core global $foo:memory_base (;2;))) + (alias core export $main "foo:table_base" (core global $foo:table_base (;3;))) + (alias core export $main "foo:__wasm_get_tls_base" (core func $foo:__wasm_get_tls_base (;4;))) + (alias core export $main "foo:__wasm_set_tls_base" (core func $foo:__wasm_set_tls_base (;5;))) + (alias core export $c "abort" (core func $abort (;6;))) + (core instance $"#core-instance3 env" (@name "env") (;3;) + (export "memory" (memory $memory)) + (export "__indirect_function_table" (table $__indirect_function_table)) + (export "__memory_base" (global $foo:memory_base)) + (export "__table_base" (global $foo:table_base)) + (export "__wasm_get_tls_base" (func $foo:__wasm_get_tls_base)) + (export "__wasm_set_tls_base" (func $foo:__wasm_set_tls_base)) + (export "abort" (func $abort)) + ) + (core instance $foo (;4;) (instantiate $foo + (with "env" (instance $"#core-instance3 env")) + ) + ) + (core instance $__init (;5;) (instantiate $__init + (with "env" (instance $main)) + ) + ) + (type (;0;) (func (result s32))) + (alias core export $foo "test:test/test#foo" (core func $test:test/test#foo (;7;))) + (func $foo (;0;) (type 0) (canon lift (core func $test:test/test#foo))) + (component $test:test/test-shim-component (;0;) + (type (;0;) (func (result s32))) + (import "import-func-foo" (func (;0;) (type 0))) + (type (;1;) (func (result s32))) + (export (;1;) "foo" (func 0) (func (type 1))) + ) + (instance $test:test/test-shim-instance (;0;) (instantiate $test:test/test-shim-component + (with "import-func-foo" (func $foo)) + ) + ) + (export $test:test/test (;1;) "test:test/test" (instance $test:test/test-shim-instance)) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wit.print b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wit.print new file mode 100644 index 0000000000..ad829fd0b8 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wit.print @@ -0,0 +1,5 @@ +package root:component; + +world root { + export test:test/test; +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wat b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wat new file mode 100644 index 0000000000..7ac22ca3b2 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wat @@ -0,0 +1,26 @@ +(module + (@dylink.0 + (mem-info (memory 16 4)) + ) + (type $void (func)) + (type $get (func (result i32))) + (type $set (func (param i32))) + + (import "env" "memory" (memory 1)) + (import "env" "__indirect_function_table" (table 0 funcref)) + (import "env" "__memory_base" (global $__memory_base i32)) + (import "env" "__table_base" (global $__table_base i32)) + (import "env" "__wasm_get_stack_pointer" (func $get_sp (type $get))) + (import "env" "__wasm_set_stack_pointer" (func $set_sp (type $set))) + (import "env" "__wasm_get_tls_base" (func $get_tls (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (type $set))) + + (global (export "__tls_size") i32 i32.const 148) + (global (export "__tls_align") i32 i32.const 4) + (func (export "__wasm_init_tls") (param i32) unreachable) + + (func $malloc (param i32) (result i32) unreachable) + (func $abort unreachable) + + (export "abort" (func $abort)) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wit b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wit new file mode 100644 index 0000000000..65ff1f36b2 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wit @@ -0,0 +1,4 @@ +package test:test; + +world lib-c { +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wat b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wat new file mode 100644 index 0000000000..c58fbaaa1a --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wat @@ -0,0 +1,28 @@ +(module + (@dylink.0 + (mem-info (memory 8 4)) + (needed "c") + ) + (type $void (func)) + (type $get (func (result i32))) + (type $set (func (param i32))) + + (import "env" "memory" (memory 1)) + (import "env" "__indirect_function_table" (table 0 funcref)) + (import "env" "__memory_base" (global $__memory_base i32)) + (import "env" "__table_base" (global $__table_base i32)) + (import "env" "__wasm_get_tls_base" (func $get_tls (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (type $set))) + (import "env" "abort" (func $abort (type $void))) + + (global (export "__tls_size") i32 i32.const 32) + (global (export "__tls_align") i32 i32.const 16) + (func (export "__wasm_init_tls") (param i32) unreachable) + + (func $foo (result i32) + call $get_tls + i32.load + ) + + (export "test:test/test#foo" (func $foo)) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wit b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wit new file mode 100644 index 0000000000..be962ede03 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wit @@ -0,0 +1,9 @@ +package test:test; + +interface test { + foo: func() -> s32; +} + +world lib-foo { + export test; +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls/component.wat b/crates/wit-component/tests/components/link-wasip3-tls/component.wat new file mode 100644 index 0000000000..a2bb4bcd70 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/component.wat @@ -0,0 +1,294 @@ +(component + (core module $main (;0;) + (type (;0;) (func (result i32))) + (type (;1;) (func (result i32))) + (type (;2;) (func (param i32))) + (type (;3;) (func (result i32))) + (type (;4;) (func (param i32))) + (import "$root" "[context-get-1]" (func (;0;) (type 0))) + (table (;0;) 3 funcref) + (memory (;0;) 17) + (global (;0;) (mut i32) i32.const 1048616) + (global (;1;) i32 i32.const 1048640) + (global (;2;) i32 i32.const 3) + (global (;3;) i32 i32.const 1048656) + (global (;4;) i32 i32.const 3) + (global (;5;) i32 i32.const 1048672) + (global (;6;) i32 i32.const 3) + (export "__wasm_program_tls_info" (global 0)) + (export "c:memory_base" (global 1)) + (export "c:table_base" (global 2)) + (export "foo:memory_base" (global 3)) + (export "foo:table_base" (global 4)) + (export "none:memory_base" (global 5)) + (export "none:table_base" (global 6)) + (export "c:__wasm_get_tls_base" (func 1)) + (export "c:__wasm_set_tls_base" (func 2)) + (export "foo:__wasm_get_tls_base" (func 3)) + (export "foo:__wasm_set_tls_base" (func 4)) + (export "__indirect_function_table" (table 0)) + (export "memory" (memory 0)) + (func (;1;) (type 1) (result i32) + call 0 + i32.load offset=4 + ) + (func (;2;) (type 2) (param i32) + (local i32) + call 0 + local.tee 1 + i32.eqz + if ;; label = @1 + i32.const 1048584 + local.get 0 + i32.store offset=4 + else + local.get 1 + local.get 0 + i32.store offset=4 + end + ) + (func (;3;) (type 3) (result i32) + call 0 + i32.load + ) + (func (;4;) (type 4) (param i32) + (local i32) + call 0 + local.tee 1 + i32.eqz + if ;; label = @1 + i32.const 1048584 + local.get 0 + i32.store + else + local.get 1 + local.get 0 + i32.store + end + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $c (;1;) + (@dylink.0 + (mem-info (memory 16 4)) + ) + (type $void (;0;) (func)) + (type $get (;1;) (func (result i32))) + (type $set (;2;) (func (param i32))) + (type $spawn (;3;) (func (param i32 i32) (result i32))) + (import "env" "memory" (memory (;0;) 1)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "env" "__memory_base" (global $__memory_base (;0;) i32)) + (import "env" "__table_base" (global $__table_base (;1;) i32)) + (import "env" "__wasm_get_stack_pointer" (func $get_sp (;0;) (type $get))) + (import "env" "__wasm_set_stack_pointer" (func $set_sp (;1;) (type $set))) + (import "env" "__wasm_get_tls_base" (func $get_tls (;2;) (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (;3;) (type $set))) + (import "$root" "[context-get-1]" (func $get_ctx (;4;) (type $get))) + (import "$root" "[context-set-1]" (func $set_ctx (;5;) (type $set))) + (import "GOT.mem" "__wasm_program_tls_info" (global $tls_info (;2;) (mut i32))) + (import "$root" "[thread-new-indirect-v0]" (func $spawn (;6;) (type $spawn))) + (global (;3;) i32 i32.const 148) + (global (;4;) i32 i32.const 4) + (export "__tls_size" (global 3)) + (export "__tls_align" (global 4)) + (export "__wasm_init_tls" (func 7)) + (export "abort" (func 8)) + (func (;7;) (type $set) (param i32) + local.get 0 + call $set_tls + ) + (func (;8;) (type $void) + unreachable + ) + ) + (core module $none (;2;) + (@dylink.0 + (mem-info (memory 4 4)) + (needed "c") + ) + (type (;0;) (func (result i32))) + (export "none_helper" (func 0)) + (func (;0;) (type 0) (result i32) + i32.const 7 + ) + ) + (core module $foo (;3;) + (@dylink.0 + (mem-info (memory 8 4)) + (needed "c") + (needed "none") + ) + (type $void (;0;) (func)) + (type $get (;1;) (func (result i32))) + (type $set (;2;) (func (param i32))) + (import "env" "memory" (memory (;0;) 1)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "env" "__memory_base" (global $__memory_base (;0;) i32)) + (import "env" "__table_base" (global $__table_base (;1;) i32)) + (import "env" "__wasm_get_tls_base" (func $get_tls (;0;) (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (;1;) (type $set))) + (import "env" "abort" (func $abort (;2;) (type $void))) + (import "env" "none_helper" (func $none_helper (;3;) (type $get))) + (global (;2;) i32 i32.const 32) + (global (;3;) i32 i32.const 16) + (export "__tls_size" (global 2)) + (export "__tls_align" (global 3)) + (export "__wasm_init_tls" (func 4)) + (export "test:test/test#foo" (func 5)) + (func (;4;) (type $set) (param i32) + local.get 0 + call $set_tls + ) + (func (;5;) (type $get) (result i32) + call $get_tls + i32.load + call $none_helper + i32.add + ) + ) + (core module $__init (;4;) + (type (;0;) (func)) + (type (;1;) (func (param i32))) + (import "env" "memory" (memory (;0;) 0)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "foo" "__wasm_init_tls" (func (;0;) (type 1))) + (import "c" "__wasm_init_tls" (func (;1;) (type 1))) + (start 2) + (elem (;0;) (i32.const 1) func) + (elem (;1;) (i32.const 3) func) + (elem (;2;) (i32.const 1) func 0 1) + (func (;2;) (type 0)) + (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") + (data (;1;) (i32.const 1048592) " \00\00\00\10\00\00\00\01\00\00\00\94\00\00\00\04\00\00\00\02\00\00\00\02\00\00\00\10\00\10\00\08\00\10\00") + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $wit-component-shim-module (;5;) + (type (;0;) (func (param i32 i32) (result i32))) + (table (;0;) 1 1 funcref) + (export "0" (func $thread.new-indirect)) + (export "$imports" (table 0)) + (func $thread.new-indirect (;0;) (type 0) (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.const 0 + call_indirect (type 0) + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $wit-component-fixup (;6;) + (type (;0;) (func (param i32 i32) (result i32))) + (import "" "0" (func (;0;) (type 0))) + (import "" "$imports" (table (;0;) 1 1 funcref)) + (elem (;0;) (i32.const 0) func 0) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module)) + (core func $"context.get 1" (;0;) (canon context.get i32 1)) + (core instance $$root (;1;) + (export "[context-get-1]" (func $"context.get 1")) + ) + (core instance $main (;2;) (instantiate $main + (with "$root" (instance $$root)) + ) + ) + (alias core export $main "memory" (core memory $memory (;0;))) + (alias core export $main "__indirect_function_table" (core table $__indirect_function_table (;0;))) + (alias core export $main "c:memory_base" (core global $c:memory_base (;0;))) + (alias core export $main "c:table_base" (core global $c:table_base (;1;))) + (core func $"context.get 0" (;1;) (canon context.get i32 0)) + (core func $"context.set 0" (;2;) (canon context.set i32 0)) + (alias core export $main "c:__wasm_get_tls_base" (core func $c:__wasm_get_tls_base (;3;))) + (alias core export $main "c:__wasm_set_tls_base" (core func $c:__wasm_set_tls_base (;4;))) + (core instance $env (;3;) + (export "memory" (memory $memory)) + (export "__indirect_function_table" (table $__indirect_function_table)) + (export "__memory_base" (global $c:memory_base)) + (export "__table_base" (global $c:table_base)) + (export "__wasm_get_stack_pointer" (func $"context.get 0")) + (export "__wasm_set_stack_pointer" (func $"context.set 0")) + (export "__wasm_get_tls_base" (func $c:__wasm_get_tls_base)) + (export "__wasm_set_tls_base" (func $c:__wasm_set_tls_base)) + ) + (core func $"#core-func5 context.get 1" (@name "context.get 1") (;5;) (canon context.get i32 1)) + (core func $"context.set 1" (;6;) (canon context.set i32 1)) + (alias core export $wit-component-shim-instance "0" (core func $thread.new-indirect (;7;))) + (core instance $"#core-instance4 $root" (@name "$root") (;4;) + (export "[context-get-1]" (func $"#core-func5 context.get 1")) + (export "[context-set-1]" (func $"context.set 1")) + (export "[thread-new-indirect-v0]" (func $thread.new-indirect)) + ) + (alias core export $main "__wasm_program_tls_info" (core global $__wasm_program_tls_info (;2;))) + (core instance $GOT.mem (;5;) + (export "__wasm_program_tls_info" (global $__wasm_program_tls_info)) + ) + (core instance $c (;6;) (instantiate $c + (with "env" (instance $env)) + (with "$root" (instance $"#core-instance4 $root")) + (with "GOT.mem" (instance $GOT.mem)) + ) + ) + (core instance $none (;7;) (instantiate $none)) + (alias core export $main "foo:memory_base" (core global $foo:memory_base (;3;))) + (alias core export $main "foo:table_base" (core global $foo:table_base (;4;))) + (alias core export $main "foo:__wasm_get_tls_base" (core func $foo:__wasm_get_tls_base (;8;))) + (alias core export $main "foo:__wasm_set_tls_base" (core func $foo:__wasm_set_tls_base (;9;))) + (alias core export $c "abort" (core func $abort (;10;))) + (alias core export $none "none_helper" (core func $none_helper (;11;))) + (core instance $"#core-instance8 env" (@name "env") (;8;) + (export "memory" (memory $memory)) + (export "__indirect_function_table" (table $__indirect_function_table)) + (export "__memory_base" (global $foo:memory_base)) + (export "__table_base" (global $foo:table_base)) + (export "__wasm_get_tls_base" (func $foo:__wasm_get_tls_base)) + (export "__wasm_set_tls_base" (func $foo:__wasm_set_tls_base)) + (export "abort" (func $abort)) + (export "none_helper" (func $none_helper)) + ) + (core instance $foo (;9;) (instantiate $foo + (with "env" (instance $"#core-instance8 env")) + ) + ) + (alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;1;))) + (core type $thread-start (;0;) (func (param i32))) + (core func $"#core-func12 thread.new-indirect" (@name "thread.new-indirect") (;12;) (canon thread.new-indirect $thread-start $__indirect_function_table)) + (core instance $fixup-args (;10;) + (export "$imports" (table $"shim table")) + (export "0" (func $"#core-func12 thread.new-indirect")) + ) + (core instance $fixup (;11;) (instantiate $wit-component-fixup + (with "" (instance $fixup-args)) + ) + ) + (core instance $__init (;12;) (instantiate $__init + (with "env" (instance $main)) + (with "foo" (instance $foo)) + (with "c" (instance $c)) + ) + ) + (type (;0;) (func (result s32))) + (alias core export $foo "test:test/test#foo" (core func $test:test/test#foo (;13;))) + (func $foo (;0;) (type 0) (canon lift (core func $test:test/test#foo))) + (component $test:test/test-shim-component (;0;) + (type (;0;) (func (result s32))) + (import "import-func-foo" (func (;0;) (type 0))) + (type (;1;) (func (result s32))) + (export (;1;) "foo" (func 0) (func (type 1))) + ) + (instance $test:test/test-shim-instance (;0;) (instantiate $test:test/test-shim-component + (with "import-func-foo" (func $foo)) + ) + ) + (export $test:test/test (;1;) "test:test/test" (instance $test:test/test-shim-instance)) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls/component.wit.print b/crates/wit-component/tests/components/link-wasip3-tls/component.wit.print new file mode 100644 index 0000000000..ad829fd0b8 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/component.wit.print @@ -0,0 +1,5 @@ +package root:component; + +world root { + export test:test/test; +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wat b/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wat new file mode 100644 index 0000000000..07e7070df0 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wat @@ -0,0 +1,31 @@ +(module + (@dylink.0 + (mem-info (memory 16 4)) + ) + (type $void (func)) + (type $get (func (result i32))) + (type $set (func (param i32))) + (type $spawn (func (param i32 i32) (result i32))) + + (import "env" "memory" (memory 1)) + (import "env" "__indirect_function_table" (table 0 funcref)) + (import "env" "__memory_base" (global $__memory_base i32)) + (import "env" "__table_base" (global $__table_base i32)) + (import "env" "__wasm_get_stack_pointer" (func $get_sp (type $get))) + (import "env" "__wasm_set_stack_pointer" (func $set_sp (type $set))) + (import "env" "__wasm_get_tls_base" (func $get_tls (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (type $set))) + (import "$root" "[context-get-1]" (func $get_ctx (type $get))) + (import "$root" "[context-set-1]" (func $set_ctx (type $set))) + (import "GOT.mem" "__wasm_program_tls_info" (global $tls_info (mut i32))) + (import "$root" "[thread-new-indirect-v0]" (func $spawn (type $spawn))) + + (global (export "__tls_size") i32 i32.const 148) + (global (export "__tls_align") i32 i32.const 4) + (func (export "__wasm_init_tls") (param i32) + local.get 0 + call $set_tls + ) + + (func (export "abort") unreachable) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wit b/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wit new file mode 100644 index 0000000000..65ff1f36b2 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wit @@ -0,0 +1,4 @@ +package test:test; + +world lib-c { +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wat b/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wat new file mode 100644 index 0000000000..bd1ec391c8 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wat @@ -0,0 +1,33 @@ +(module + (@dylink.0 + (mem-info (memory 8 4)) + (needed "c") + (needed "none") + ) + (type $void (func)) + (type $get (func (result i32))) + (type $set (func (param i32))) + + (import "env" "memory" (memory 1)) + (import "env" "__indirect_function_table" (table 0 funcref)) + (import "env" "__memory_base" (global $__memory_base i32)) + (import "env" "__table_base" (global $__table_base i32)) + (import "env" "__wasm_get_tls_base" (func $get_tls (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (type $set))) + (import "env" "abort" (func $abort (type $void))) + (import "env" "none_helper" (func $none_helper (type $get))) + + (global (export "__tls_size") i32 i32.const 32) + (global (export "__tls_align") i32 i32.const 16) + (func (export "__wasm_init_tls") (param i32) + local.get 0 + call $set_tls + ) + + (func (export "test:test/test#foo") (result i32) + call $get_tls + i32.load + call $none_helper + i32.add + ) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wit b/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wit new file mode 100644 index 0000000000..be962ede03 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wit @@ -0,0 +1,9 @@ +package test:test; + +interface test { + foo: func() -> s32; +} + +world lib-foo { + export test; +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wat b/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wat new file mode 100644 index 0000000000..3f3813b6a1 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wat @@ -0,0 +1,8 @@ +(module + (@dylink.0 + (mem-info (memory 4 4)) + (needed "c") + ) + + (func (export "none_helper") (result i32) i32.const 7) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wit b/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wit new file mode 100644 index 0000000000..1f34c7a78e --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wit @@ -0,0 +1,4 @@ +package test:test; + +world lib-none { +} diff --git a/crates/wit-component/tests/components/link/component.wat b/crates/wit-component/tests/components/link/component.wat index 9cbc709107..5a66d8ab1d 100644 --- a/crates/wit-component/tests/components/link/component.wat +++ b/crates/wit-component/tests/components/link/component.wat @@ -148,16 +148,16 @@ (type (;1;) (func (param i32))) (import "env" "memory" (memory (;0;) 0)) (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) - (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) - (import "bar" "__wasm_call_ctors" (func (;1;) (type 0))) (import "env" "foo:memory_base" (global (;0;) i32)) (import "foo" "well" (global (;1;) i32)) (import "env" "bar:well" (global (;2;) (mut i32))) - (import "foo" "__wasm_apply_data_relocs" (func (;2;) (type 0))) - (import "foo" "__wasm_call_ctors" (func (;3;) (type 0))) (import "env" "bar:memory_base" (global (;3;) i32)) (import "bar" "um" (global (;4;) i32)) (import "env" "foo:um" (global (;5;) (mut i32))) + (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) + (import "foo" "__wasm_apply_data_relocs" (func (;1;) (type 0))) + (import "bar" "__wasm_call_ctors" (func (;2;) (type 0))) + (import "foo" "__wasm_call_ctors" (func (;3;) (type 0))) (start 4) (elem (;0;) (i32.const 1) func) (elem (;1;) (i32.const 1) func) @@ -171,8 +171,8 @@ i32.add global.set 5 call 0 - call 2 call 1 + call 2 call 3 ) (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") @@ -243,8 +243,8 @@ ) (core instance $__init (;10;) (instantiate $__init (with "env" (instance $main)) - (with "bar" (instance $bar)) (with "foo" (instance $foo)) + (with "bar" (instance $bar)) ) ) (type (;1;) (func (param "v" s32) (result s32))) diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wat b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wat new file mode 100644 index 0000000000..d188eb4733 --- /dev/null +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wat @@ -0,0 +1,77 @@ +(component + (core module $main (;0;) + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (type (;2;) (func (param i32 i32) (result i32))) + (import "env" "__wasm_get_stack_pointer" (func (;0;) (type 0))) + (import "env" "__wasm_set_stack_pointer" (func (;1;) (type 1))) + (import "env" "__wasm_get_tls_base" (func (;2;) (type 0))) + (import "env" "__wasm_set_tls_base" (func (;3;) (type 1))) + (import "$root" "[thread-new-indirect-v0]" (func (;4;) (type 2))) + (table (;0;) 1 funcref) + (export "__indirect_function_table" (table 0)) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + (processed-by "my-fake-bindgen" "123.45") + ) + ) + (core module $wit-component-shim-module (;1;) + (type (;0;) (func (param i32 i32) (result i32))) + (table (;0;) 1 1 funcref) + (export "0" (func $thread.new-indirect)) + (export "$imports" (table 0)) + (func $thread.new-indirect (;0;) (type 0) (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.const 0 + call_indirect (type 0) + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $wit-component-fixup (;2;) + (type (;0;) (func (param i32 i32) (result i32))) + (import "" "0" (func (;0;) (type 0))) + (import "" "$imports" (table (;0;) 1 1 funcref)) + (elem (;0;) (i32.const 0) func 0) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module)) + (core func $"context.get 0" (;0;) (canon context.get i32 0)) + (core func $"context.set 0" (;1;) (canon context.set i32 0)) + (core func $"context.get 1" (;2;) (canon context.get i32 1)) + (core func $"context.set 1" (;3;) (canon context.set i32 1)) + (core instance $env (;1;) + (export "__wasm_get_stack_pointer" (func $"context.get 0")) + (export "__wasm_set_stack_pointer" (func $"context.set 0")) + (export "__wasm_get_tls_base" (func $"context.get 1")) + (export "__wasm_set_tls_base" (func $"context.set 1")) + ) + (alias core export $wit-component-shim-instance "0" (core func $thread.new-indirect (;4;))) + (core instance $$root (;2;) + (export "[thread-new-indirect-v0]" (func $thread.new-indirect)) + ) + (core instance $main (;3;) (instantiate $main + (with "env" (instance $env)) + (with "$root" (instance $$root)) + ) + ) + (alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;))) + (core type $thread-start (;0;) (func (param i32))) + (alias core export $main "__indirect_function_table" (core table $indirect-function-table (;1;))) + (core func $"#core-func5 thread.new-indirect" (@name "thread.new-indirect") (;5;) (canon thread.new-indirect $thread-start $indirect-function-table)) + (core instance $fixup-args (;4;) + (export "$imports" (table $"shim table")) + (export "0" (func $"#core-func5 thread.new-indirect")) + ) + (core instance $fixup (;5;) (instantiate $wit-component-fixup + (with "" (instance $fixup-args)) + ) + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wit.print b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wit.print new file mode 100644 index 0000000000..b4548abf3d --- /dev/null +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wit.print @@ -0,0 +1,4 @@ +package root:component; + +world root { +} diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wat b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wat new file mode 100644 index 0000000000..0ad52eb8ea --- /dev/null +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wat @@ -0,0 +1,8 @@ +(module + (import "env" "__wasm_get_stack_pointer" (func (result i32))) + (import "env" "__wasm_set_stack_pointer" (func (param i32))) + (import "env" "__wasm_get_tls_base" (func (result i32))) + (import "env" "__wasm_set_tls_base" (func (param i32))) + (import "$root" "[thread-new-indirect-v0]" (func (param i32 i32) (result i32))) + (table (export "__indirect_function_table") 1 funcref) +) diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wit b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wit new file mode 100644 index 0000000000..065a2959f4 --- /dev/null +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wit @@ -0,0 +1,5 @@ +package foo:foo; + + +world module { +} diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols/component.wat b/crates/wit-component/tests/components/threading-wasm-ld-symbols/component.wat index 6bcc4c5674..a9b2c43544 100644 --- a/crates/wit-component/tests/components/threading-wasm-ld-symbols/component.wat +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols/component.wat @@ -13,15 +13,30 @@ ) (core func $"context.get 0" (;0;) (canon context.get i32 0)) (core func $"context.set 0" (;1;) (canon context.set i32 0)) - (core func $"context.get 1" (;2;) (canon context.get i32 1)) - (core func $"context.set 1" (;3;) (canon context.set i32 1)) - (core instance $env (;0;) + (core module $wit-component:tls-base (;1;) + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (global (;0;) (mut i32) i32.const 0) + (export "get" (func 0)) + (export "set" (func 1)) + (func (;0;) (type 0) (result i32) + global.get 0 + ) + (func (;1;) (type 1) (param i32) + local.get 0 + global.set 0 + ) + ) + (core instance $wit-component:tls-base (;0;) (instantiate $wit-component:tls-base)) + (alias core export $wit-component:tls-base "get" (core func $tls-base-get (;2;))) + (alias core export $wit-component:tls-base "set" (core func $tls-base-set (;3;))) + (core instance $env (;1;) (export "__wasm_get_stack_pointer" (func $"context.get 0")) (export "__wasm_set_stack_pointer" (func $"context.set 0")) - (export "__wasm_get_tls_base" (func $"context.get 1")) - (export "__wasm_set_tls_base" (func $"context.set 1")) + (export "__wasm_get_tls_base" (func $tls-base-get)) + (export "__wasm_set_tls_base" (func $tls-base-set)) ) - (core instance $main (;1;) (instantiate $main + (core instance $main (;2;) (instantiate $main (with "env" (instance $env)) ) ) diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols64/component.wat b/crates/wit-component/tests/components/threading-wasm-ld-symbols64/component.wat index dd5690ae86..069f45b88b 100644 --- a/crates/wit-component/tests/components/threading-wasm-ld-symbols64/component.wat +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols64/component.wat @@ -13,15 +13,30 @@ ) (core func $"context.get 0" (;0;) (canon context.get i64 0)) (core func $"context.set 0" (;1;) (canon context.set i64 0)) - (core func $"context.get 1" (;2;) (canon context.get i64 1)) - (core func $"context.set 1" (;3;) (canon context.set i64 1)) - (core instance $env (;0;) + (core module $wit-component:tls-base (;1;) + (type (;0;) (func (result i64))) + (type (;1;) (func (param i64))) + (global (;0;) (mut i64) i64.const 0) + (export "get" (func 0)) + (export "set" (func 1)) + (func (;0;) (type 0) (result i64) + global.get 0 + ) + (func (;1;) (type 1) (param i64) + local.get 0 + global.set 0 + ) + ) + (core instance $wit-component:tls-base (;0;) (instantiate $wit-component:tls-base)) + (alias core export $wit-component:tls-base "get" (core func $tls-base-get (;2;))) + (alias core export $wit-component:tls-base "set" (core func $tls-base-set (;3;))) + (core instance $env (;1;) (export "__wasm_get_stack_pointer" (func $"context.get 0")) (export "__wasm_set_stack_pointer" (func $"context.set 0")) - (export "__wasm_get_tls_base" (func $"context.get 1")) - (export "__wasm_set_tls_base" (func $"context.set 1")) + (export "__wasm_get_tls_base" (func $tls-base-get)) + (export "__wasm_set_tls_base" (func $tls-base-set)) ) - (core instance $main (;1;) (instantiate $main + (core instance $main (;2;) (instantiate $main (with "env" (instance $env)) ) )