| 1 | //! Support for "pseudo-dynamic", shared-everything linking of Wasm modules into a component. | 
| 2 | //! | 
|---|
| 3 | //! This implements [shared-everything | 
|---|
| 4 | //! linking](https://github.com/WebAssembly/component-model/blob/main/design/mvp/examples/SharedEverythingDynamicLinking.md), | 
|---|
| 5 | //! taking as input one or more [dynamic | 
|---|
| 6 | //! library](https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md) modules and producing a | 
|---|
| 7 | //! component whose type is the union of any `component-type*` custom sections found in the input modules. | 
|---|
| 8 | //! | 
|---|
| 9 | //! The entry point into this process is `Linker::encode`, which analyzes and topologically sorts the input | 
|---|
| 10 | //! modules, then sythesizes two additional modules: | 
|---|
| 11 | //! | 
|---|
| 12 | //! - `main` AKA `env`: hosts the component's single memory and function table and exports any functions needed to | 
|---|
| 13 | //! break dependency cycles discovered in the input modules. Those functions use `call.indirect` to invoke the real | 
|---|
| 14 | //! functions, references to which are placed in the table by the `init` module. | 
|---|
| 15 | //! | 
|---|
| 16 | //! - `init`: populates the function table as described above, initializes global variables per the dynamic linking | 
|---|
| 17 | //! tool convention, and calls any static constructors and/or link-time fixup functions | 
|---|
| 18 | //! | 
|---|
| 19 | //! `Linker` also supports synthesizing `dlopen`/`dlsym` lookup tables which allow symbols to be resolved at | 
|---|
| 20 | //! runtime.  Note that this is not true dynamic linking, since all the code is baked into the component ahead of | 
|---|
| 21 | //! time -- we simply allow runtime resolution of already-resident definitions.  This is sufficient to support | 
|---|
| 22 | //! dynamic language FFI features such as Python native extensions, provided the required libraries are linked | 
|---|
| 23 | //! ahead-of-time. | 
|---|
| 24 |  | 
|---|
| 25 | use { | 
|---|
| 26 | crate::encoding::{ComponentEncoder, Instance, Item, LibraryInfo, MainOrAdapter}, | 
|---|
| 27 | anyhow::{anyhow, bail, Context, Result}, | 
|---|
| 28 | indexmap::{map::Entry, IndexMap, IndexSet}, | 
|---|
| 29 | metadata::{Export, ExportKey, FunctionType, GlobalType, Metadata, Type, ValueType}, | 
|---|
| 30 | std::{ | 
|---|
| 31 | collections::{BTreeMap, HashMap, HashSet}, | 
|---|
| 32 | fmt::Debug, | 
|---|
| 33 | hash::Hash, | 
|---|
| 34 | iter, | 
|---|
| 35 | }, | 
|---|
| 36 | wasm_encoder::{ | 
|---|
| 37 | CodeSection, ConstExpr, DataSection, ElementSection, Elements, EntityType, ExportKind, | 
|---|
| 38 | ExportSection, Function, FunctionSection, GlobalSection, ImportSection, Instruction as Ins, | 
|---|
| 39 | MemArg, MemorySection, MemoryType, Module, RawCustomSection, RefType, StartSection, | 
|---|
| 40 | TableSection, TableType, TypeSection, ValType, | 
|---|
| 41 | }, | 
|---|
| 42 | wasmparser::SymbolFlags, | 
|---|
| 43 | }; | 
|---|
| 44 |  | 
|---|
| 45 | mod metadata; | 
|---|
| 46 |  | 
|---|
| 47 | const PAGE_SIZE_BYTES: u32 = 65536; | 
|---|
| 48 | // This matches the default stack size LLVM produces: | 
|---|
| 49 | pub const DEFAULT_STACK_SIZE_BYTES: u32 = 16 * PAGE_SIZE_BYTES; | 
|---|
| 50 | const HEAP_ALIGNMENT_BYTES: u32 = 16; | 
|---|
| 51 |  | 
|---|
| 52 | enum Address<'a> { | 
|---|
| 53 | Function(u32), | 
|---|
| 54 | Global(&'a str), | 
|---|
| 55 | } | 
|---|
| 56 |  | 
|---|
| 57 | /// Represents a `dlopen`/`dlsym` lookup table enabling runtime symbol resolution | 
|---|
| 58 | /// | 
|---|
| 59 | /// The top level of this table is a sorted list of library names and offsets, each pointing to a sorted list of | 
|---|
| 60 | /// symbol names and offsets.  See ../dl/src/lib.rs for how this is used at runtime. | 
|---|
| 61 | struct DlOpenables<'a> { | 
|---|
| 62 | /// Offset into the main module's table where function references will be stored | 
|---|
| 63 | table_base: u32, | 
|---|
| 64 |  | 
|---|
| 65 | /// Offset into the main module's memory where the lookup table will be stored | 
|---|
| 66 | memory_base: u32, | 
|---|
| 67 |  | 
|---|
| 68 | /// The lookup table itself | 
|---|
| 69 | buffer: Vec<u8>, | 
|---|
| 70 |  | 
|---|
| 71 | /// Linear memory addresses where global variable addresses will live | 
|---|
| 72 | /// | 
|---|
| 73 | /// The init module will fill in the correct values at insantiation time. | 
|---|
| 74 | global_addresses: Vec<(&'a str, &'a str, u32)>, | 
|---|
| 75 |  | 
|---|
| 76 | /// Number of function references to be stored in the main module's table | 
|---|
| 77 | function_count: u32, | 
|---|
| 78 |  | 
|---|
| 79 | /// Linear memory address where the root of the lookup table will reside | 
|---|
| 80 | /// | 
|---|
| 81 | /// This can be different from `memory_base` depending on how the tree of libraries and symbols is laid out in | 
|---|
| 82 | /// memory. | 
|---|
| 83 | libraries_address: u32, | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | impl<'a> DlOpenables<'a> { | 
|---|
| 87 | /// Construct a lookup table containing all "dlopen-able" libraries and their symbols using the specified table | 
|---|
| 88 | /// and memory offsets. | 
|---|
| 89 | fn new(table_base: u32, memory_base: u32, metadata: &'a [Metadata<'a>]) -> Self { | 
|---|
| 90 | let mut function_count = 0; | 
|---|
| 91 | let mut buffer = Vec::new(); | 
|---|
| 92 | let mut global_addresses = Vec::new(); | 
|---|
| 93 | let mut libraries = metadata | 
|---|
| 94 | .iter() | 
|---|
| 95 | .filter(|metadata| metadata.dl_openable) | 
|---|
| 96 | .map(|metadata| { | 
|---|
| 97 | let name_address = memory_base + u32::try_from(buffer.len()).unwrap(); | 
|---|
| 98 | write_bytes_padded(&mut buffer, metadata.name.as_bytes()); | 
|---|
| 99 |  | 
|---|
| 100 | let mut symbols = metadata | 
|---|
| 101 | .exports | 
|---|
| 102 | .iter() | 
|---|
| 103 | .map(|export| { | 
|---|
| 104 | let name_address = memory_base + u32::try_from(buffer.len()).unwrap(); | 
|---|
| 105 | write_bytes_padded(&mut buffer, export.key.name.as_bytes()); | 
|---|
| 106 |  | 
|---|
| 107 | let address = match &export.key.ty { | 
|---|
| 108 | Type::Function(_) => Address::Function( | 
|---|
| 109 | table_base + get_and_increment(&mut function_count), | 
|---|
| 110 | ), | 
|---|
| 111 | Type::Global(_) => Address::Global(export.key.name), | 
|---|
| 112 | }; | 
|---|
| 113 |  | 
|---|
| 114 | (export.key.name, name_address, address) | 
|---|
| 115 | }) | 
|---|
| 116 | .collect::<Vec<_>>(); | 
|---|
| 117 |  | 
|---|
| 118 | symbols.sort_by_key(|(name, ..)| *name); | 
|---|
| 119 |  | 
|---|
| 120 | let start = buffer.len(); | 
|---|
| 121 | for (name, name_address, address) in symbols { | 
|---|
| 122 | write_u32(&mut buffer, u32::try_from(name.len()).unwrap()); | 
|---|
| 123 | write_u32(&mut buffer, name_address); | 
|---|
| 124 | match address { | 
|---|
| 125 | Address::Function(address) => write_u32(&mut buffer, address), | 
|---|
| 126 | Address::Global(name) => { | 
|---|
| 127 | global_addresses.push(( | 
|---|
| 128 | metadata.name, | 
|---|
| 129 | name, | 
|---|
| 130 | memory_base + u32::try_from(buffer.len()).unwrap(), | 
|---|
| 131 | )); | 
|---|
| 132 |  | 
|---|
| 133 | write_u32(&mut buffer, 0); | 
|---|
| 134 | } | 
|---|
| 135 | } | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | ( | 
|---|
| 139 | metadata.name, | 
|---|
| 140 | name_address, | 
|---|
| 141 | metadata.exports.len(), | 
|---|
| 142 | memory_base + u32::try_from(start).unwrap(), | 
|---|
| 143 | ) | 
|---|
| 144 | }) | 
|---|
| 145 | .collect::<Vec<_>>(); | 
|---|
| 146 |  | 
|---|
| 147 | libraries.sort_by_key(|(name, ..)| *name); | 
|---|
| 148 |  | 
|---|
| 149 | let start = buffer.len(); | 
|---|
| 150 | for (name, name_address, count, symbols) in &libraries { | 
|---|
| 151 | write_u32(&mut buffer, u32::try_from(name.len()).unwrap()); | 
|---|
| 152 | write_u32(&mut buffer, *name_address); | 
|---|
| 153 | write_u32(&mut buffer, u32::try_from(*count).unwrap()); | 
|---|
| 154 | write_u32(&mut buffer, *symbols); | 
|---|
| 155 | } | 
|---|
| 156 |  | 
|---|
| 157 | let libraries_address = memory_base + u32::try_from(buffer.len()).unwrap(); | 
|---|
| 158 | write_u32(&mut buffer, u32::try_from(libraries.len()).unwrap()); | 
|---|
| 159 | write_u32(&mut buffer, memory_base + u32::try_from(start).unwrap()); | 
|---|
| 160 |  | 
|---|
| 161 | Self { | 
|---|
| 162 | table_base, | 
|---|
| 163 | memory_base, | 
|---|
| 164 | buffer, | 
|---|
| 165 | global_addresses, | 
|---|
| 166 | function_count, | 
|---|
| 167 | libraries_address, | 
|---|
| 168 | } | 
|---|
| 169 | } | 
|---|
| 170 | } | 
|---|
| 171 |  | 
|---|
| 172 | fn write_u32(buffer: &mut Vec<u8>, value: u32) { | 
|---|
| 173 | buffer.extend(iter:value.to_le_bytes()); | 
|---|
| 174 | } | 
|---|
| 175 |  | 
|---|
| 176 | fn write_bytes_padded(buffer: &mut Vec<u8>, bytes: &[u8]) { | 
|---|
| 177 | buffer.extend(iter:bytes); | 
|---|
| 178 |  | 
|---|
| 179 | let len: u32 = u32::try_from(bytes.len()).unwrap(); | 
|---|
| 180 | for _ in len..align(a:len, b:4) { | 
|---|
| 181 | buffer.push(0); | 
|---|
| 182 | } | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | fn align(a: u32, b: u32) -> u32 { | 
|---|
| 186 | assert!(b.is_power_of_two()); | 
|---|
| 187 | (a + (b - 1)) & !(b - 1) | 
|---|
| 188 | } | 
|---|
| 189 |  | 
|---|
| 190 | fn get_and_increment(n: &mut u32) -> u32 { | 
|---|
| 191 | let v: u32 = *n; | 
|---|
| 192 | *n += 1; | 
|---|
| 193 | v | 
|---|
| 194 | } | 
|---|
| 195 |  | 
|---|
| 196 | fn const_u32(a: u32) -> ConstExpr { | 
|---|
| 197 | ConstExpr::i32_const(a as i32) | 
|---|
| 198 | } | 
|---|
| 199 |  | 
|---|
| 200 | /// Helper trait for determining the size of a set or map | 
|---|
| 201 | trait Length { | 
|---|
| 202 | fn len(&self) -> usize; | 
|---|
| 203 | } | 
|---|
| 204 |  | 
|---|
| 205 | impl<T> Length for HashSet<T> { | 
|---|
| 206 | fn len(&self) -> usize { | 
|---|
| 207 | HashSet::len(self) | 
|---|
| 208 | } | 
|---|
| 209 | } | 
|---|
| 210 |  | 
|---|
| 211 | impl<K, V> Length for HashMap<K, V> { | 
|---|
| 212 | fn len(&self) -> usize { | 
|---|
| 213 | HashMap::len(self) | 
|---|
| 214 | } | 
|---|
| 215 | } | 
|---|
| 216 |  | 
|---|
| 217 | impl<T> Length for IndexSet<T> { | 
|---|
| 218 | fn len(&self) -> usize { | 
|---|
| 219 | IndexSet::len(self) | 
|---|
| 220 | } | 
|---|
| 221 | } | 
|---|
| 222 |  | 
|---|
| 223 | impl<K, V> Length for IndexMap<K, V> { | 
|---|
| 224 | fn len(&self) -> usize { | 
|---|
| 225 | IndexMap::len(self) | 
|---|
| 226 | } | 
|---|
| 227 | } | 
|---|
| 228 |  | 
|---|
| 229 | /// Extension trait for collecting into a set or map and asserting that there were no duplicate entries in the | 
|---|
| 230 | /// source iterator. | 
|---|
| 231 | trait CollectUnique: Iterator + Sized { | 
|---|
| 232 | fn collect_unique<T: FromIterator<Self::Item> + Length>(self) -> T { | 
|---|
| 233 | let tmp: Vec<::Item> = self.collect::<Vec<_>>(); | 
|---|
| 234 | let len: usize = tmp.len(); | 
|---|
| 235 | let result: T = tmp.into_iter().collect::<T>(); | 
|---|
| 236 | assert!( | 
|---|
| 237 | result.len() == len, | 
|---|
| 238 | "one or more duplicate items detected when collecting into set or map" | 
|---|
| 239 | ); | 
|---|
| 240 | result | 
|---|
| 241 | } | 
|---|
| 242 | } | 
|---|
| 243 |  | 
|---|
| 244 | impl<T: Iterator> CollectUnique for T {} | 
|---|
| 245 |  | 
|---|
| 246 | /// Extension trait for inserting into a map and asserting that an entry did not already exist for the key | 
|---|
| 247 | trait InsertUnique { | 
|---|
| 248 | type Key; | 
|---|
| 249 | type Value; | 
|---|
| 250 |  | 
|---|
| 251 | fn insert_unique(&mut self, k: Self::Key, v: Self::Value); | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | impl<K: Hash + Eq + PartialEq + Debug, V: Debug> InsertUnique for HashMap<K, V> { | 
|---|
| 255 | type Key = K; | 
|---|
| 256 | type Value = V; | 
|---|
| 257 |  | 
|---|
| 258 | fn insert_unique(&mut self, k: Self::Key, v: Self::Value) { | 
|---|
| 259 | if let Some(old_v: &V) = self.get(&k) { | 
|---|
| 260 | panic!( "duplicate item inserted into map for key {k:?}  (old value: {old_v:?} ; new value: {v:?} )"); | 
|---|
| 261 | } | 
|---|
| 262 | self.insert(k, v); | 
|---|
| 263 | } | 
|---|
| 264 | } | 
|---|
| 265 |  | 
|---|
| 266 | /// Synthesize the "main" module for the component, responsible for exporting functions which break cyclic | 
|---|
| 267 | /// dependencies, as well as hosting the memory and function table. | 
|---|
| 268 | fn make_env_module<'a>( | 
|---|
| 269 | metadata: &'a [Metadata<'a>], | 
|---|
| 270 | function_exports: &[(&str, &FunctionType, usize)], | 
|---|
| 271 | cabi_realloc_exporter: Option<&str>, | 
|---|
| 272 | stack_size_bytes: u32, | 
|---|
| 273 | ) -> (Vec<u8>, DlOpenables<'a>, u32) { | 
|---|
| 274 | // TODO: deduplicate types | 
|---|
| 275 | let mut types = TypeSection::new(); | 
|---|
| 276 | let mut imports = ImportSection::new(); | 
|---|
| 277 | let mut import_map = IndexMap::new(); | 
|---|
| 278 | let mut function_count = 0; | 
|---|
| 279 | let mut global_offset = 0; | 
|---|
| 280 | let mut wasi_start = None; | 
|---|
| 281 |  | 
|---|
| 282 | for metadata in metadata { | 
|---|
| 283 | for import in &metadata.imports { | 
|---|
| 284 | if let Entry::Vacant(entry) = import_map.entry(import) { | 
|---|
| 285 | imports.import( | 
|---|
| 286 | import.module, | 
|---|
| 287 | import.name, | 
|---|
| 288 | match &import.ty { | 
|---|
| 289 | Type::Function(ty) => { | 
|---|
| 290 | let index = get_and_increment(&mut function_count); | 
|---|
| 291 | entry.insert(index); | 
|---|
| 292 | types.ty().function( | 
|---|
| 293 | ty.parameters.iter().copied().map(ValType::from), | 
|---|
| 294 | ty.results.iter().copied().map(ValType::from), | 
|---|
| 295 | ); | 
|---|
| 296 | EntityType::Function(index) | 
|---|
| 297 | } | 
|---|
| 298 | Type::Global(ty) => { | 
|---|
| 299 | entry.insert(get_and_increment(&mut global_offset)); | 
|---|
| 300 | EntityType::Global(wasm_encoder::GlobalType { | 
|---|
| 301 | val_type: ty.ty.into(), | 
|---|
| 302 | mutable: ty.mutable, | 
|---|
| 303 | shared: ty.shared, | 
|---|
| 304 | }) | 
|---|
| 305 | } | 
|---|
| 306 | }, | 
|---|
| 307 | ); | 
|---|
| 308 | } | 
|---|
| 309 | } | 
|---|
| 310 |  | 
|---|
| 311 | if metadata.has_wasi_start { | 
|---|
| 312 | if wasi_start.is_some() { | 
|---|
| 313 | panic!( "multiple libraries export _start"); | 
|---|
| 314 | } | 
|---|
| 315 | let index = get_and_increment(&mut function_count); | 
|---|
| 316 |  | 
|---|
| 317 | types.ty().function(vec![], vec![]); | 
|---|
| 318 | imports.import(metadata.name, "_start", EntityType::Function(index)); | 
|---|
| 319 |  | 
|---|
| 320 | wasi_start = Some(index); | 
|---|
| 321 | } | 
|---|
| 322 | } | 
|---|
| 323 |  | 
|---|
| 324 | let mut memory_offset = stack_size_bytes; | 
|---|
| 325 |  | 
|---|
| 326 | // Table offset 0 is reserved for the null function pointer. | 
|---|
| 327 | // This convention follows wasm-ld's table layout: | 
|---|
| 328 | // https://github.com/llvm/llvm-project/blob/913622d012f72edb5ac3a501cef8639d0ebe471b/lld/wasm/Driver.cpp#L581-L584 | 
|---|
| 329 | let mut table_offset = 1; | 
|---|
| 330 | let mut globals = GlobalSection::new(); | 
|---|
| 331 | let mut exports = ExportSection::new(); | 
|---|
| 332 |  | 
|---|
| 333 | if let Some(exporter) = cabi_realloc_exporter { | 
|---|
| 334 | let index = get_and_increment(&mut function_count); | 
|---|
| 335 | types.ty().function([ValType::I32; 4], [ValType::I32]); | 
|---|
| 336 | imports.import(exporter, "cabi_realloc", EntityType::Function(index)); | 
|---|
| 337 | exports.export( "cabi_realloc", ExportKind::Func, index); | 
|---|
| 338 | } | 
|---|
| 339 |  | 
|---|
| 340 | let dl_openables = DlOpenables::new(table_offset, memory_offset, metadata); | 
|---|
| 341 |  | 
|---|
| 342 | table_offset += dl_openables.function_count; | 
|---|
| 343 | memory_offset += u32::try_from(dl_openables.buffer.len()).unwrap(); | 
|---|
| 344 |  | 
|---|
| 345 | let memory_size = { | 
|---|
| 346 | let mut add_global_export = |name: &str, value, mutable| { | 
|---|
| 347 | let index = globals.len(); | 
|---|
| 348 | globals.global( | 
|---|
| 349 | wasm_encoder::GlobalType { | 
|---|
| 350 | val_type: ValType::I32, | 
|---|
| 351 | mutable, | 
|---|
| 352 | shared: false, | 
|---|
| 353 | }, | 
|---|
| 354 | &const_u32(value), | 
|---|
| 355 | ); | 
|---|
| 356 | exports.export(name, ExportKind::Global, index); | 
|---|
| 357 | }; | 
|---|
| 358 |  | 
|---|
| 359 | add_global_export( "__stack_pointer", stack_size_bytes, true); | 
|---|
| 360 |  | 
|---|
| 361 | // Binaryen's Asyncify transform for shared everything linking requires these globals | 
|---|
| 362 | // to be provided from env module | 
|---|
| 363 | let has_asyncified_module = metadata.iter().any(|m| m.is_asyncified); | 
|---|
| 364 | if has_asyncified_module { | 
|---|
| 365 | add_global_export( "__asyncify_state", 0, true); | 
|---|
| 366 | add_global_export( "__asyncify_data", 0, true); | 
|---|
| 367 | } | 
|---|
| 368 |  | 
|---|
| 369 | for metadata in metadata { | 
|---|
| 370 | memory_offset = align(memory_offset, 1 << metadata.mem_info.memory_alignment); | 
|---|
| 371 | table_offset = align(table_offset, 1 << metadata.mem_info.table_alignment); | 
|---|
| 372 |  | 
|---|
| 373 | add_global_export( | 
|---|
| 374 | &format!( "{} :memory_base", metadata.name), | 
|---|
| 375 | memory_offset, | 
|---|
| 376 | false, | 
|---|
| 377 | ); | 
|---|
| 378 | add_global_export( | 
|---|
| 379 | &format!( "{} :table_base", metadata.name), | 
|---|
| 380 | table_offset, | 
|---|
| 381 | false, | 
|---|
| 382 | ); | 
|---|
| 383 |  | 
|---|
| 384 | memory_offset += metadata.mem_info.memory_size; | 
|---|
| 385 | table_offset += metadata.mem_info.table_size; | 
|---|
| 386 |  | 
|---|
| 387 | for import in &metadata.memory_address_imports { | 
|---|
| 388 | // Note that we initialize this to zero and let the init module compute the real value at | 
|---|
| 389 | // instantiation time. | 
|---|
| 390 | add_global_export(&format!( "{} :{import} ", metadata.name), 0, true); | 
|---|
| 391 | } | 
|---|
| 392 | } | 
|---|
| 393 |  | 
|---|
| 394 | { | 
|---|
| 395 | let offsets = function_exports | 
|---|
| 396 | .iter() | 
|---|
| 397 | .enumerate() | 
|---|
| 398 | .map(|(offset, (name, ..))| (*name, table_offset + u32::try_from(offset).unwrap())) | 
|---|
| 399 | .collect_unique::<HashMap<_, _>>(); | 
|---|
| 400 |  | 
|---|
| 401 | for metadata in metadata { | 
|---|
| 402 | for import in &metadata.table_address_imports { | 
|---|
| 403 | add_global_export( | 
|---|
| 404 | &format!( "{} :{import} ", metadata.name), | 
|---|
| 405 | *offsets.get(import).unwrap(), | 
|---|
| 406 | true, | 
|---|
| 407 | ); | 
|---|
| 408 | } | 
|---|
| 409 | } | 
|---|
| 410 | } | 
|---|
| 411 |  | 
|---|
| 412 | memory_offset = align(memory_offset, HEAP_ALIGNMENT_BYTES); | 
|---|
| 413 | add_global_export( "__heap_base", memory_offset, true); | 
|---|
| 414 |  | 
|---|
| 415 | let heap_end = align(memory_offset, PAGE_SIZE_BYTES); | 
|---|
| 416 | add_global_export( "__heap_end", heap_end, true); | 
|---|
| 417 | heap_end / PAGE_SIZE_BYTES | 
|---|
| 418 | }; | 
|---|
| 419 |  | 
|---|
| 420 | let indirection_table_base = table_offset; | 
|---|
| 421 |  | 
|---|
| 422 | let mut functions = FunctionSection::new(); | 
|---|
| 423 | let mut code = CodeSection::new(); | 
|---|
| 424 | for (name, ty, _) in function_exports { | 
|---|
| 425 | let index = get_and_increment(&mut function_count); | 
|---|
| 426 | types.ty().function( | 
|---|
| 427 | ty.parameters.iter().copied().map(ValType::from), | 
|---|
| 428 | ty.results.iter().copied().map(ValType::from), | 
|---|
| 429 | ); | 
|---|
| 430 | functions.function(u32::try_from(index).unwrap()); | 
|---|
| 431 | let mut function = Function::new([]); | 
|---|
| 432 | for local in 0..ty.parameters.len() { | 
|---|
| 433 | function.instruction(&Ins::LocalGet(u32::try_from(local).unwrap())); | 
|---|
| 434 | } | 
|---|
| 435 | function.instruction(&Ins::I32Const(i32::try_from(table_offset).unwrap())); | 
|---|
| 436 | function.instruction(&Ins::CallIndirect { | 
|---|
| 437 | type_index: u32::try_from(index).unwrap(), | 
|---|
| 438 | table_index: 0, | 
|---|
| 439 | }); | 
|---|
| 440 | function.instruction(&Ins::End); | 
|---|
| 441 | code.function(&function); | 
|---|
| 442 | exports.export(name, ExportKind::Func, index); | 
|---|
| 443 |  | 
|---|
| 444 | table_offset += 1; | 
|---|
| 445 | } | 
|---|
| 446 |  | 
|---|
| 447 | for (import, offset) in import_map { | 
|---|
| 448 | exports.export( | 
|---|
| 449 | &format!( "{} :{} ", import.module, import.name), | 
|---|
| 450 | ExportKind::from(&import.ty), | 
|---|
| 451 | offset, | 
|---|
| 452 | ); | 
|---|
| 453 | } | 
|---|
| 454 | if let Some(index) = wasi_start { | 
|---|
| 455 | exports.export( "_start", ExportKind::Func, index); | 
|---|
| 456 | } | 
|---|
| 457 |  | 
|---|
| 458 | let mut module = Module::new(); | 
|---|
| 459 |  | 
|---|
| 460 | module.section(&types); | 
|---|
| 461 | module.section(&imports); | 
|---|
| 462 | module.section(&functions); | 
|---|
| 463 |  | 
|---|
| 464 | { | 
|---|
| 465 | let mut tables = TableSection::new(); | 
|---|
| 466 | tables.table(TableType { | 
|---|
| 467 | element_type: RefType::FUNCREF, | 
|---|
| 468 | minimum: table_offset.into(), | 
|---|
| 469 | maximum: None, | 
|---|
| 470 | table64: false, | 
|---|
| 471 | shared: false, | 
|---|
| 472 | }); | 
|---|
| 473 | exports.export( "__indirect_function_table", ExportKind::Table, 0); | 
|---|
| 474 | module.section(&tables); | 
|---|
| 475 | } | 
|---|
| 476 |  | 
|---|
| 477 | { | 
|---|
| 478 | let mut memories = MemorySection::new(); | 
|---|
| 479 | memories.memory(MemoryType { | 
|---|
| 480 | minimum: u64::from(memory_size), | 
|---|
| 481 | maximum: None, | 
|---|
| 482 | memory64: false, | 
|---|
| 483 | shared: false, | 
|---|
| 484 | page_size_log2: None, | 
|---|
| 485 | }); | 
|---|
| 486 | exports.export( "memory", ExportKind::Memory, 0); | 
|---|
| 487 | module.section(&memories); | 
|---|
| 488 | } | 
|---|
| 489 |  | 
|---|
| 490 | module.section(&globals); | 
|---|
| 491 | module.section(&exports); | 
|---|
| 492 | module.section(&code); | 
|---|
| 493 | module.section(&RawCustomSection( | 
|---|
| 494 | &crate::base_producers().raw_custom_section(), | 
|---|
| 495 | )); | 
|---|
| 496 |  | 
|---|
| 497 | let module = module.finish(); | 
|---|
| 498 | wasmparser::validate(&module).unwrap(); | 
|---|
| 499 |  | 
|---|
| 500 | (module, dl_openables, indirection_table_base) | 
|---|
| 501 | } | 
|---|
| 502 |  | 
|---|
| 503 | /// Synthesize the "init" module, responsible for initializing global variables per the dynamic linking tool | 
|---|
| 504 | /// convention and calling any static constructors and/or link-time fixup functions. | 
|---|
| 505 | /// | 
|---|
| 506 | /// This module also contains the data segment for the `dlopen`/`dlsym` lookup table. | 
|---|
| 507 | fn make_init_module( | 
|---|
| 508 | metadata: &[Metadata], | 
|---|
| 509 | exporters: &IndexMap<&ExportKey, (&str, &Export)>, | 
|---|
| 510 | function_exports: &[(&str, &FunctionType, usize)], | 
|---|
| 511 | dl_openables: DlOpenables, | 
|---|
| 512 | indirection_table_base: u32, | 
|---|
| 513 | ) -> Result<Vec<u8>> { | 
|---|
| 514 | let mut module = Module::new(); | 
|---|
| 515 |  | 
|---|
| 516 | // TODO: deduplicate types | 
|---|
| 517 | let mut types = TypeSection::new(); | 
|---|
| 518 | types.ty().function([], []); | 
|---|
| 519 | let thunk_ty = 0; | 
|---|
| 520 | types.ty().function([ValType::I32], []); | 
|---|
| 521 | let one_i32_param_ty = 1; | 
|---|
| 522 | let mut type_offset = 2; | 
|---|
| 523 |  | 
|---|
| 524 | for metadata in metadata { | 
|---|
| 525 | if metadata.dl_openable { | 
|---|
| 526 | for export in &metadata.exports { | 
|---|
| 527 | if let Type::Function(ty) = &export.key.ty { | 
|---|
| 528 | types.ty().function( | 
|---|
| 529 | ty.parameters.iter().copied().map(ValType::from), | 
|---|
| 530 | ty.results.iter().copied().map(ValType::from), | 
|---|
| 531 | ); | 
|---|
| 532 | } | 
|---|
| 533 | } | 
|---|
| 534 | } | 
|---|
| 535 | } | 
|---|
| 536 | for (_, ty, _) in function_exports { | 
|---|
| 537 | types.ty().function( | 
|---|
| 538 | ty.parameters.iter().copied().map(ValType::from), | 
|---|
| 539 | ty.results.iter().copied().map(ValType::from), | 
|---|
| 540 | ); | 
|---|
| 541 | } | 
|---|
| 542 | module.section(&types); | 
|---|
| 543 |  | 
|---|
| 544 | let mut imports = ImportSection::new(); | 
|---|
| 545 | imports.import( | 
|---|
| 546 | "env", | 
|---|
| 547 | "memory", | 
|---|
| 548 | MemoryType { | 
|---|
| 549 | minimum: 0, | 
|---|
| 550 | maximum: None, | 
|---|
| 551 | memory64: false, | 
|---|
| 552 | shared: false, | 
|---|
| 553 | page_size_log2: None, | 
|---|
| 554 | }, | 
|---|
| 555 | ); | 
|---|
| 556 | imports.import( | 
|---|
| 557 | "env", | 
|---|
| 558 | "__indirect_function_table", | 
|---|
| 559 | TableType { | 
|---|
| 560 | element_type: RefType::FUNCREF, | 
|---|
| 561 | minimum: 0, | 
|---|
| 562 | maximum: None, | 
|---|
| 563 | table64: false, | 
|---|
| 564 | shared: false, | 
|---|
| 565 | }, | 
|---|
| 566 | ); | 
|---|
| 567 |  | 
|---|
| 568 | let mut global_count = 0; | 
|---|
| 569 | let mut global_map = HashMap::new(); | 
|---|
| 570 | let mut add_global_import = |imports: &mut ImportSection, module: &str, name: &str, mutable| { | 
|---|
| 571 | *global_map | 
|---|
| 572 | .entry((module.to_owned(), name.to_owned())) | 
|---|
| 573 | .or_insert_with(|| { | 
|---|
| 574 | imports.import( | 
|---|
| 575 | module, | 
|---|
| 576 | name, | 
|---|
| 577 | wasm_encoder::GlobalType { | 
|---|
| 578 | val_type: ValType::I32, | 
|---|
| 579 | mutable, | 
|---|
| 580 | shared: false, | 
|---|
| 581 | }, | 
|---|
| 582 | ); | 
|---|
| 583 | get_and_increment(&mut global_count) | 
|---|
| 584 | }) | 
|---|
| 585 | }; | 
|---|
| 586 |  | 
|---|
| 587 | let mut function_count = 0; | 
|---|
| 588 | let mut function_map = HashMap::new(); | 
|---|
| 589 | let mut add_function_import = |imports: &mut ImportSection, module: &str, name: &str, ty| { | 
|---|
| 590 | *function_map | 
|---|
| 591 | .entry((module.to_owned(), name.to_owned())) | 
|---|
| 592 | .or_insert_with(|| { | 
|---|
| 593 | imports.import(module, name, EntityType::Function(ty)); | 
|---|
| 594 | get_and_increment(&mut function_count) | 
|---|
| 595 | }) | 
|---|
| 596 | }; | 
|---|
| 597 |  | 
|---|
| 598 | let mut memory_address_inits = Vec::new(); | 
|---|
| 599 | let mut reloc_calls = Vec::new(); | 
|---|
| 600 | let mut ctor_calls = Vec::new(); | 
|---|
| 601 | let mut names = HashMap::new(); | 
|---|
| 602 |  | 
|---|
| 603 | for (exporter, export, address) in dl_openables.global_addresses.iter() { | 
|---|
| 604 | memory_address_inits.push(Ins::I32Const(i32::try_from(*address).unwrap())); | 
|---|
| 605 | memory_address_inits.push(Ins::GlobalGet(add_global_import( | 
|---|
| 606 | &mut imports, | 
|---|
| 607 | "env", | 
|---|
| 608 | &format!( "{exporter} :memory_base"), | 
|---|
| 609 | false, | 
|---|
| 610 | ))); | 
|---|
| 611 | memory_address_inits.push(Ins::GlobalGet(add_global_import( | 
|---|
| 612 | &mut imports, | 
|---|
| 613 | exporter, | 
|---|
| 614 | export, | 
|---|
| 615 | false, | 
|---|
| 616 | ))); | 
|---|
| 617 | memory_address_inits.push(Ins::I32Add); | 
|---|
| 618 | memory_address_inits.push(Ins::I32Store(MemArg { | 
|---|
| 619 | offset: 0, | 
|---|
| 620 | align: 2, | 
|---|
| 621 | memory_index: 0, | 
|---|
| 622 | })); | 
|---|
| 623 | } | 
|---|
| 624 |  | 
|---|
| 625 | for (index, metadata) in metadata.iter().enumerate() { | 
|---|
| 626 | names.insert_unique(index, metadata.name); | 
|---|
| 627 |  | 
|---|
| 628 | if metadata.has_data_relocs { | 
|---|
| 629 | reloc_calls.push(Ins::Call(add_function_import( | 
|---|
| 630 | &mut imports, | 
|---|
| 631 | metadata.name, | 
|---|
| 632 | "__wasm_apply_data_relocs", | 
|---|
| 633 | thunk_ty, | 
|---|
| 634 | ))); | 
|---|
| 635 | } | 
|---|
| 636 |  | 
|---|
| 637 | if metadata.has_ctors && metadata.has_initialize { | 
|---|
| 638 | bail!( | 
|---|
| 639 | "library {}  exports both `__wasm_call_ctors` and `_initialize`; \ | 
|---|
| 640 |                  expected at most one of the two", | 
|---|
| 641 | metadata.name | 
|---|
| 642 | ); | 
|---|
| 643 | } | 
|---|
| 644 |  | 
|---|
| 645 | if metadata.has_ctors { | 
|---|
| 646 | ctor_calls.push(Ins::Call(add_function_import( | 
|---|
| 647 | &mut imports, | 
|---|
| 648 | metadata.name, | 
|---|
| 649 | "__wasm_call_ctors", | 
|---|
| 650 | thunk_ty, | 
|---|
| 651 | ))); | 
|---|
| 652 | } | 
|---|
| 653 |  | 
|---|
| 654 | if metadata.has_initialize { | 
|---|
| 655 | ctor_calls.push(Ins::Call(add_function_import( | 
|---|
| 656 | &mut imports, | 
|---|
| 657 | metadata.name, | 
|---|
| 658 | "_initialize", | 
|---|
| 659 | thunk_ty, | 
|---|
| 660 | ))); | 
|---|
| 661 | } | 
|---|
| 662 |  | 
|---|
| 663 | if metadata.has_set_libraries { | 
|---|
| 664 | ctor_calls.push(Ins::I32Const( | 
|---|
| 665 | i32::try_from(dl_openables.libraries_address).unwrap(), | 
|---|
| 666 | )); | 
|---|
| 667 | ctor_calls.push(Ins::Call(add_function_import( | 
|---|
| 668 | &mut imports, | 
|---|
| 669 | metadata.name, | 
|---|
| 670 | "__wasm_set_libraries", | 
|---|
| 671 | one_i32_param_ty, | 
|---|
| 672 | ))); | 
|---|
| 673 | } | 
|---|
| 674 |  | 
|---|
| 675 | for import in &metadata.memory_address_imports { | 
|---|
| 676 | let (exporter, _) = find_offset_exporter(import, exporters)?; | 
|---|
| 677 |  | 
|---|
| 678 | memory_address_inits.push(Ins::GlobalGet(add_global_import( | 
|---|
| 679 | &mut imports, | 
|---|
| 680 | "env", | 
|---|
| 681 | &format!( "{exporter} :memory_base"), | 
|---|
| 682 | false, | 
|---|
| 683 | ))); | 
|---|
| 684 | memory_address_inits.push(Ins::GlobalGet(add_global_import( | 
|---|
| 685 | &mut imports, | 
|---|
| 686 | exporter, | 
|---|
| 687 | import, | 
|---|
| 688 | false, | 
|---|
| 689 | ))); | 
|---|
| 690 | memory_address_inits.push(Ins::I32Add); | 
|---|
| 691 | memory_address_inits.push(Ins::GlobalSet(add_global_import( | 
|---|
| 692 | &mut imports, | 
|---|
| 693 | "env", | 
|---|
| 694 | &format!( "{} :{import} ", metadata.name), | 
|---|
| 695 | true, | 
|---|
| 696 | ))); | 
|---|
| 697 | } | 
|---|
| 698 | } | 
|---|
| 699 |  | 
|---|
| 700 | let mut dl_openable_functions = Vec::new(); | 
|---|
| 701 | for metadata in metadata { | 
|---|
| 702 | if metadata.dl_openable { | 
|---|
| 703 | for export in &metadata.exports { | 
|---|
| 704 | if let Type::Function(_) = &export.key.ty { | 
|---|
| 705 | dl_openable_functions.push(add_function_import( | 
|---|
| 706 | &mut imports, | 
|---|
| 707 | metadata.name, | 
|---|
| 708 | export.key.name, | 
|---|
| 709 | get_and_increment(&mut type_offset), | 
|---|
| 710 | )); | 
|---|
| 711 | } | 
|---|
| 712 | } | 
|---|
| 713 | } | 
|---|
| 714 | } | 
|---|
| 715 |  | 
|---|
| 716 | let indirections = function_exports | 
|---|
| 717 | .iter() | 
|---|
| 718 | .map(|(name, _, index)| { | 
|---|
| 719 | add_function_import( | 
|---|
| 720 | &mut imports, | 
|---|
| 721 | names[index], | 
|---|
| 722 | name, | 
|---|
| 723 | get_and_increment(&mut type_offset), | 
|---|
| 724 | ) | 
|---|
| 725 | }) | 
|---|
| 726 | .collect::<Vec<_>>(); | 
|---|
| 727 |  | 
|---|
| 728 | module.section(&imports); | 
|---|
| 729 |  | 
|---|
| 730 | { | 
|---|
| 731 | let mut functions = FunctionSection::new(); | 
|---|
| 732 | functions.function(thunk_ty); | 
|---|
| 733 | module.section(&functions); | 
|---|
| 734 | } | 
|---|
| 735 |  | 
|---|
| 736 | module.section(&StartSection { | 
|---|
| 737 | function_index: function_count, | 
|---|
| 738 | }); | 
|---|
| 739 |  | 
|---|
| 740 | { | 
|---|
| 741 | let mut elements = ElementSection::new(); | 
|---|
| 742 | elements.active( | 
|---|
| 743 | None, | 
|---|
| 744 | &const_u32(dl_openables.table_base), | 
|---|
| 745 | Elements::Functions(dl_openable_functions.into()), | 
|---|
| 746 | ); | 
|---|
| 747 | elements.active( | 
|---|
| 748 | None, | 
|---|
| 749 | &const_u32(indirection_table_base), | 
|---|
| 750 | Elements::Functions(indirections.into()), | 
|---|
| 751 | ); | 
|---|
| 752 | module.section(&elements); | 
|---|
| 753 | } | 
|---|
| 754 |  | 
|---|
| 755 | { | 
|---|
| 756 | let mut code = CodeSection::new(); | 
|---|
| 757 | let mut function = Function::new([]); | 
|---|
| 758 | for ins in memory_address_inits | 
|---|
| 759 | .iter() | 
|---|
| 760 | .chain(&reloc_calls) | 
|---|
| 761 | .chain(&ctor_calls) | 
|---|
| 762 | { | 
|---|
| 763 | function.instruction(ins); | 
|---|
| 764 | } | 
|---|
| 765 | function.instruction(&Ins::End); | 
|---|
| 766 | code.function(&function); | 
|---|
| 767 | module.section(&code); | 
|---|
| 768 | } | 
|---|
| 769 |  | 
|---|
| 770 | let mut data = DataSection::new(); | 
|---|
| 771 | data.active(0, &const_u32(dl_openables.memory_base), dl_openables.buffer); | 
|---|
| 772 | module.section(&data); | 
|---|
| 773 |  | 
|---|
| 774 | module.section(&RawCustomSection( | 
|---|
| 775 | &crate::base_producers().raw_custom_section(), | 
|---|
| 776 | )); | 
|---|
| 777 |  | 
|---|
| 778 | let module = module.finish(); | 
|---|
| 779 | wasmparser::validate(&module)?; | 
|---|
| 780 |  | 
|---|
| 781 | Ok(module) | 
|---|
| 782 | } | 
|---|
| 783 |  | 
|---|
| 784 | /// Find the library which exports the specified function or global address. | 
|---|
| 785 | fn find_offset_exporter<'a>( | 
|---|
| 786 | name: &str, | 
|---|
| 787 | exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>, | 
|---|
| 788 | ) -> Result<(&'a str, &'a Export<'a>)> { | 
|---|
| 789 | let export: ExportKey<'_> = ExportKey { | 
|---|
| 790 | name, | 
|---|
| 791 | ty: Type::Global(GlobalType { | 
|---|
| 792 | ty: ValueType::I32, | 
|---|
| 793 | mutable: false, | 
|---|
| 794 | shared: false, | 
|---|
| 795 | }), | 
|---|
| 796 | }; | 
|---|
| 797 |  | 
|---|
| 798 | exporters | 
|---|
| 799 | .get(&export) | 
|---|
| 800 | .copied() | 
|---|
| 801 | .ok_or_else(|| anyhow!( "unable to find {export:?}  in any library")) | 
|---|
| 802 | } | 
|---|
| 803 |  | 
|---|
| 804 | /// Find the library which exports the specified function. | 
|---|
| 805 | fn find_function_exporter<'a>( | 
|---|
| 806 | name: &str, | 
|---|
| 807 | ty: &FunctionType, | 
|---|
| 808 | exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>, | 
|---|
| 809 | ) -> Result<(&'a str, &'a Export<'a>)> { | 
|---|
| 810 | let export: ExportKey<'_> = ExportKey { | 
|---|
| 811 | name, | 
|---|
| 812 | ty: Type::Function(ty.clone()), | 
|---|
| 813 | }; | 
|---|
| 814 |  | 
|---|
| 815 | exporters | 
|---|
| 816 | .get(&export) | 
|---|
| 817 | .copied() | 
|---|
| 818 | .ok_or_else(|| anyhow!( "unable to find {export:?}  in any library")) | 
|---|
| 819 | } | 
|---|
| 820 |  | 
|---|
| 821 | /// Analyze the specified library metadata, producing a symbol-to-library-name map of exports. | 
|---|
| 822 | fn resolve_exporters<'a>( | 
|---|
| 823 | metadata: &'a [Metadata<'a>], | 
|---|
| 824 | ) -> Result<IndexMap<&'a ExportKey<'a>, Vec<(&'a str, &'a Export<'a>)>>> { | 
|---|
| 825 | let mut exporters: IndexMap<&ExportKey<'_>, Vec<…>> = IndexMap::<_, Vec<_>>::new(); | 
|---|
| 826 | for metadata: &'a Metadata<'_> in metadata { | 
|---|
| 827 | for export: &Export<'_> in &metadata.exports { | 
|---|
| 828 | exporters&mut Vec<(&'a str, &Export<'_>)> | 
|---|
| 829 | .entry(&export.key) | 
|---|
| 830 | .or_default() | 
|---|
| 831 | .push((metadata.name, export)); | 
|---|
| 832 | } | 
|---|
| 833 | } | 
|---|
| 834 | Ok(exporters) | 
|---|
| 835 | } | 
|---|
| 836 |  | 
|---|
| 837 | /// Match up all imported symbols to their corresponding exports, reporting any missing or duplicate symbols. | 
|---|
| 838 | fn resolve_symbols<'a>( | 
|---|
| 839 | metadata: &'a [Metadata<'a>], | 
|---|
| 840 | exporters: &'a IndexMap<&'a ExportKey<'a>, Vec<(&'a str, &'a Export<'a>)>>, | 
|---|
| 841 | ) -> ( | 
|---|
| 842 | IndexMap<&'a ExportKey<'a>, (&'a str, &'a Export<'a>)>, | 
|---|
| 843 | Vec<(&'a str, Export<'a>)>, | 
|---|
| 844 | Vec<(&'a str, &'a ExportKey<'a>, &'a [(&'a str, &'a Export<'a>)])>, | 
|---|
| 845 | ) { | 
|---|
| 846 | let function_exporters = exporters | 
|---|
| 847 | .iter() | 
|---|
| 848 | .filter_map(|(export, exporters)| { | 
|---|
| 849 | if let Type::Function(_) = &export.ty { | 
|---|
| 850 | Some((export.name, (export, exporters))) | 
|---|
| 851 | } else { | 
|---|
| 852 | None | 
|---|
| 853 | } | 
|---|
| 854 | }) | 
|---|
| 855 | .collect_unique::<IndexMap<_, _>>(); | 
|---|
| 856 |  | 
|---|
| 857 | let mut resolved = IndexMap::new(); | 
|---|
| 858 | let mut missing = Vec::new(); | 
|---|
| 859 | let mut duplicates = Vec::new(); | 
|---|
| 860 |  | 
|---|
| 861 | let mut triage = |metadata: &'a Metadata, export: Export<'a>| { | 
|---|
| 862 | if let Some((key, value)) = exporters.get_key_value(&export.key) { | 
|---|
| 863 | match value.as_slice() { | 
|---|
| 864 | [] => unreachable!(), | 
|---|
| 865 | [exporter] => { | 
|---|
| 866 | // Note that we do not use `insert_unique` here since multiple libraries may import the same | 
|---|
| 867 | // symbol, in which case we may redundantly insert the same value. | 
|---|
| 868 | resolved.insert(*key, *exporter); | 
|---|
| 869 | } | 
|---|
| 870 | [exporter, ..] => { | 
|---|
| 871 | resolved.insert(*key, *exporter); | 
|---|
| 872 | duplicates.push((metadata.name, *key, value.as_slice())); | 
|---|
| 873 | } | 
|---|
| 874 | } | 
|---|
| 875 | } else { | 
|---|
| 876 | missing.push((metadata.name, export)); | 
|---|
| 877 | } | 
|---|
| 878 | }; | 
|---|
| 879 |  | 
|---|
| 880 | for metadata in metadata { | 
|---|
| 881 | for (name, (ty, flags)) in &metadata.env_imports { | 
|---|
| 882 | triage( | 
|---|
| 883 | metadata, | 
|---|
| 884 | Export { | 
|---|
| 885 | key: ExportKey { | 
|---|
| 886 | name, | 
|---|
| 887 | ty: Type::Function(ty.clone()), | 
|---|
| 888 | }, | 
|---|
| 889 | flags: *flags, | 
|---|
| 890 | }, | 
|---|
| 891 | ); | 
|---|
| 892 | } | 
|---|
| 893 |  | 
|---|
| 894 | for name in &metadata.memory_address_imports { | 
|---|
| 895 | triage( | 
|---|
| 896 | metadata, | 
|---|
| 897 | Export { | 
|---|
| 898 | key: ExportKey { | 
|---|
| 899 | name, | 
|---|
| 900 | ty: Type::Global(GlobalType { | 
|---|
| 901 | ty: ValueType::I32, | 
|---|
| 902 | mutable: false, | 
|---|
| 903 | shared: false, | 
|---|
| 904 | }), | 
|---|
| 905 | }, | 
|---|
| 906 | flags: SymbolFlags::empty(), | 
|---|
| 907 | }, | 
|---|
| 908 | ); | 
|---|
| 909 | } | 
|---|
| 910 | } | 
|---|
| 911 |  | 
|---|
| 912 | for metadata in metadata { | 
|---|
| 913 | for name in &metadata.table_address_imports { | 
|---|
| 914 | if let Some((key, value)) = function_exporters.get(name) { | 
|---|
| 915 | // Note that we do not use `insert_unique` here since multiple libraries may import the same | 
|---|
| 916 | // symbol, in which case we may redundantly insert the same value. | 
|---|
| 917 | match value.as_slice() { | 
|---|
| 918 | [] => unreachable!(), | 
|---|
| 919 | [exporter] => { | 
|---|
| 920 | resolved.insert(key, *exporter); | 
|---|
| 921 | } | 
|---|
| 922 | [exporter, ..] => { | 
|---|
| 923 | resolved.insert(key, *exporter); | 
|---|
| 924 | duplicates.push((metadata.name, *key, value.as_slice())); | 
|---|
| 925 | } | 
|---|
| 926 | } | 
|---|
| 927 | } else { | 
|---|
| 928 | missing.push(( | 
|---|
| 929 | metadata.name, | 
|---|
| 930 | Export { | 
|---|
| 931 | key: ExportKey { | 
|---|
| 932 | name, | 
|---|
| 933 | ty: Type::Function(FunctionType { | 
|---|
| 934 | parameters: Vec::new(), | 
|---|
| 935 | results: Vec::new(), | 
|---|
| 936 | }), | 
|---|
| 937 | }, | 
|---|
| 938 | flags: SymbolFlags::empty(), | 
|---|
| 939 | }, | 
|---|
| 940 | )); | 
|---|
| 941 | } | 
|---|
| 942 | } | 
|---|
| 943 | } | 
|---|
| 944 |  | 
|---|
| 945 | (resolved, missing, duplicates) | 
|---|
| 946 | } | 
|---|
| 947 |  | 
|---|
| 948 | /// Recursively add a library (represented by its offset) and its dependency to the specified set, maintaining | 
|---|
| 949 | /// topological order (modulo cycles). | 
|---|
| 950 | fn topo_add<'a>( | 
|---|
| 951 | sorted: &mut IndexSet<usize>, | 
|---|
| 952 | dependencies: &IndexMap<usize, IndexSet<usize>>, | 
|---|
| 953 | element: usize, | 
|---|
| 954 | ) { | 
|---|
| 955 | let empty: &IndexSet = &IndexSet::new(); | 
|---|
| 956 | let deps: &IndexSet = dependencies.get(&element).unwrap_or(default:empty); | 
|---|
| 957 |  | 
|---|
| 958 | // First, add any dependencies which do not depend on `element` | 
|---|
| 959 | for &dep: usize in deps { | 
|---|
| 960 | if !(sorted.contains(&dep) || dependencies.get(&dep).unwrap_or(default:empty).contains(&element)) { | 
|---|
| 961 | topo_add(sorted, dependencies, element:dep); | 
|---|
| 962 | } | 
|---|
| 963 | } | 
|---|
| 964 |  | 
|---|
| 965 | // Next, add the element | 
|---|
| 966 | sorted.insert(element); | 
|---|
| 967 |  | 
|---|
| 968 | // Finally, add any dependencies which depend on `element` | 
|---|
| 969 | for &dep: usize in deps { | 
|---|
| 970 | if !sorted.contains(&dep) && dependencies.get(&dep).unwrap_or(default:empty).contains(&element) { | 
|---|
| 971 | topo_add(sorted, dependencies, element:dep); | 
|---|
| 972 | } | 
|---|
| 973 | } | 
|---|
| 974 | } | 
|---|
| 975 |  | 
|---|
| 976 | /// Topologically sort a set of libraries (represented by their offsets) according to their dependencies, modulo | 
|---|
| 977 | /// cycles. | 
|---|
| 978 | fn topo_sort(count: usize, dependencies: &IndexMap<usize, IndexSet<usize>>) -> Result<Vec<usize>> { | 
|---|
| 979 | let mut sorted: IndexSet = IndexSet::new(); | 
|---|
| 980 | for index: usize in 0..count { | 
|---|
| 981 | topo_add(&mut sorted, &dependencies, element:index); | 
|---|
| 982 | } | 
|---|
| 983 |  | 
|---|
| 984 | Ok(sorted.into_iter().collect()) | 
|---|
| 985 | } | 
|---|
| 986 |  | 
|---|
| 987 | /// Analyze the specified library metadata, producing a map of transitive dependencies, where each library is | 
|---|
| 988 | /// represented by its offset in the original metadata slice. | 
|---|
| 989 | fn find_dependencies( | 
|---|
| 990 | metadata: &[Metadata], | 
|---|
| 991 | exporters: &IndexMap<&ExportKey, (&str, &Export)>, | 
|---|
| 992 | ) -> Result<IndexMap<usize, IndexSet<usize>>> { | 
|---|
| 993 | // First, generate a map of direct dependencies (i.e. depender to dependees) | 
|---|
| 994 | let mut dependencies = IndexMap::<_, IndexSet<_>>::new(); | 
|---|
| 995 | let mut indexes = HashMap::new(); | 
|---|
| 996 | for (index, metadata) in metadata.iter().enumerate() { | 
|---|
| 997 | indexes.insert_unique(metadata.name, index); | 
|---|
| 998 | for &needed in &metadata.needed_libs { | 
|---|
| 999 | dependencies | 
|---|
| 1000 | .entry(metadata.name) | 
|---|
| 1001 | .or_default() | 
|---|
| 1002 | .insert(needed); | 
|---|
| 1003 | } | 
|---|
| 1004 | for (import_name, (ty, _)) in &metadata.env_imports { | 
|---|
| 1005 | dependencies | 
|---|
| 1006 | .entry(metadata.name) | 
|---|
| 1007 | .or_default() | 
|---|
| 1008 | .insert(find_function_exporter(import_name, ty, exporters)?.0); | 
|---|
| 1009 | } | 
|---|
| 1010 | } | 
|---|
| 1011 |  | 
|---|
| 1012 | // Next, convert the map from names to offsets | 
|---|
| 1013 | let mut dependencies = dependencies | 
|---|
| 1014 | .into_iter() | 
|---|
| 1015 | .map(|(k, v)| { | 
|---|
| 1016 | ( | 
|---|
| 1017 | indexes[k], | 
|---|
| 1018 | v.into_iter() | 
|---|
| 1019 | .map(|v| indexes[v]) | 
|---|
| 1020 | .collect_unique::<IndexSet<_>>(), | 
|---|
| 1021 | ) | 
|---|
| 1022 | }) | 
|---|
| 1023 | .collect_unique::<IndexMap<_, _>>(); | 
|---|
| 1024 |  | 
|---|
| 1025 | // Finally, add all transitive dependencies to the map in a fixpoint loop, exiting when no new dependencies are | 
|---|
| 1026 | // discovered. | 
|---|
| 1027 | let empty = &IndexSet::new(); | 
|---|
| 1028 |  | 
|---|
| 1029 | loop { | 
|---|
| 1030 | let mut new = IndexMap::<_, IndexSet<_>>::new(); | 
|---|
| 1031 | for (index, exporters) in &dependencies { | 
|---|
| 1032 | for exporter in exporters { | 
|---|
| 1033 | for exporter in dependencies.get(exporter).unwrap_or(empty) { | 
|---|
| 1034 | if !exporters.contains(exporter) { | 
|---|
| 1035 | new.entry(*index).or_default().insert(*exporter); | 
|---|
| 1036 | } | 
|---|
| 1037 | } | 
|---|
| 1038 | } | 
|---|
| 1039 | } | 
|---|
| 1040 |  | 
|---|
| 1041 | if new.is_empty() { | 
|---|
| 1042 | break Ok(dependencies); | 
|---|
| 1043 | } else { | 
|---|
| 1044 | for (index, exporters) in new { | 
|---|
| 1045 | dependencies.entry(index).or_default().extend(exporters); | 
|---|
| 1046 | } | 
|---|
| 1047 | } | 
|---|
| 1048 | } | 
|---|
| 1049 | } | 
|---|
| 1050 |  | 
|---|
| 1051 | struct EnvFunctionExports<'a> { | 
|---|
| 1052 | exports: Vec<(&'a str, &'a FunctionType, usize)>, | 
|---|
| 1053 | reexport_cabi_realloc: bool, | 
|---|
| 1054 | } | 
|---|
| 1055 |  | 
|---|
| 1056 | /// Analyze the specified metadata and generate a list of functions which should be re-exported as a | 
|---|
| 1057 | /// `call.indirect`-based function by the main (AKA "env") module, including the offset of the library containing | 
|---|
| 1058 | /// the original export. | 
|---|
| 1059 | fn env_function_exports<'a>( | 
|---|
| 1060 | metadata: &'a [Metadata<'a>], | 
|---|
| 1061 | exporters: &'a IndexMap<&'a ExportKey, (&'a str, &Export)>, | 
|---|
| 1062 | topo_sorted: &[usize], | 
|---|
| 1063 | ) -> Result<EnvFunctionExports<'a>> { | 
|---|
| 1064 | let function_exporters = exporters | 
|---|
| 1065 | .iter() | 
|---|
| 1066 | .filter_map(|(export, exporter)| { | 
|---|
| 1067 | if let Type::Function(ty) = &export.ty { | 
|---|
| 1068 | Some((export.name, (ty, *exporter))) | 
|---|
| 1069 | } else { | 
|---|
| 1070 | None | 
|---|
| 1071 | } | 
|---|
| 1072 | }) | 
|---|
| 1073 | .collect_unique::<HashMap<_, _>>(); | 
|---|
| 1074 |  | 
|---|
| 1075 | let indexes = metadata | 
|---|
| 1076 | .iter() | 
|---|
| 1077 | .enumerate() | 
|---|
| 1078 | .map(|(index, metadata)| (metadata.name, index)) | 
|---|
| 1079 | .collect_unique::<HashMap<_, _>>(); | 
|---|
| 1080 |  | 
|---|
| 1081 | let mut result = Vec::new(); | 
|---|
| 1082 | let mut exported = HashSet::new(); | 
|---|
| 1083 | let mut seen = HashSet::new(); | 
|---|
| 1084 |  | 
|---|
| 1085 | for &index in topo_sorted { | 
|---|
| 1086 | let metadata = &metadata[index]; | 
|---|
| 1087 |  | 
|---|
| 1088 | for name in &metadata.table_address_imports { | 
|---|
| 1089 | if !exported.contains(name) { | 
|---|
| 1090 | let (ty, (exporter, _)) = function_exporters | 
|---|
| 1091 | .get(name) | 
|---|
| 1092 | .ok_or_else(|| anyhow!( "unable to find {name:?}  in any library"))?; | 
|---|
| 1093 |  | 
|---|
| 1094 | result.push((*name, *ty, indexes[exporter])); | 
|---|
| 1095 | exported.insert(*name); | 
|---|
| 1096 | } | 
|---|
| 1097 | } | 
|---|
| 1098 |  | 
|---|
| 1099 | for (import_name, (ty, _)) in &metadata.env_imports { | 
|---|
| 1100 | if !exported.contains(import_name) { | 
|---|
| 1101 | let exporter = indexes[find_function_exporter(import_name, ty, exporters) | 
|---|
| 1102 | .unwrap() | 
|---|
| 1103 | .0]; | 
|---|
| 1104 | if !seen.contains(&exporter) { | 
|---|
| 1105 | result.push((*import_name, ty, exporter)); | 
|---|
| 1106 | exported.insert(*import_name); | 
|---|
| 1107 | } | 
|---|
| 1108 | } | 
|---|
| 1109 | } | 
|---|
| 1110 | seen.insert(index); | 
|---|
| 1111 | } | 
|---|
| 1112 |  | 
|---|
| 1113 | let reexport_cabi_realloc = exported.contains( "cabi_realloc"); | 
|---|
| 1114 |  | 
|---|
| 1115 | Ok(EnvFunctionExports { | 
|---|
| 1116 | exports: result, | 
|---|
| 1117 | reexport_cabi_realloc, | 
|---|
| 1118 | }) | 
|---|
| 1119 | } | 
|---|
| 1120 |  | 
|---|
| 1121 | /// Synthesize a module which contains trapping stub exports for the specified functions. | 
|---|
| 1122 | fn make_stubs_module(missing: &[(&str, Export)]) -> Vec<u8> { | 
|---|
| 1123 | let mut types = TypeSection::new(); | 
|---|
| 1124 | let mut exports = ExportSection::new(); | 
|---|
| 1125 | let mut functions = FunctionSection::new(); | 
|---|
| 1126 | let mut code = CodeSection::new(); | 
|---|
| 1127 | for (offset, (_, export)) in missing.iter().enumerate() { | 
|---|
| 1128 | let offset = u32::try_from(offset).unwrap(); | 
|---|
| 1129 |  | 
|---|
| 1130 | let Export { | 
|---|
| 1131 | key: | 
|---|
| 1132 | ExportKey { | 
|---|
| 1133 | name, | 
|---|
| 1134 | ty: Type::Function(ty), | 
|---|
| 1135 | }, | 
|---|
| 1136 | .. | 
|---|
| 1137 | } = export | 
|---|
| 1138 | else { | 
|---|
| 1139 | unreachable!(); | 
|---|
| 1140 | }; | 
|---|
| 1141 |  | 
|---|
| 1142 | types.ty().function( | 
|---|
| 1143 | ty.parameters.iter().copied().map(ValType::from), | 
|---|
| 1144 | ty.results.iter().copied().map(ValType::from), | 
|---|
| 1145 | ); | 
|---|
| 1146 | functions.function(offset); | 
|---|
| 1147 | let mut function = Function::new([]); | 
|---|
| 1148 | function.instruction(&Ins::Unreachable); | 
|---|
| 1149 | function.instruction(&Ins::End); | 
|---|
| 1150 | code.function(&function); | 
|---|
| 1151 | exports.export(name, ExportKind::Func, offset); | 
|---|
| 1152 | } | 
|---|
| 1153 |  | 
|---|
| 1154 | let mut module = Module::new(); | 
|---|
| 1155 |  | 
|---|
| 1156 | module.section(&types); | 
|---|
| 1157 | module.section(&functions); | 
|---|
| 1158 | module.section(&exports); | 
|---|
| 1159 | module.section(&code); | 
|---|
| 1160 | module.section(&RawCustomSection( | 
|---|
| 1161 | &crate::base_producers().raw_custom_section(), | 
|---|
| 1162 | )); | 
|---|
| 1163 |  | 
|---|
| 1164 | let module = module.finish(); | 
|---|
| 1165 | wasmparser::validate(&module).unwrap(); | 
|---|
| 1166 |  | 
|---|
| 1167 | module | 
|---|
| 1168 | } | 
|---|
| 1169 |  | 
|---|
| 1170 | /// Determine which of the specified libraries are transitively reachable at runtime, i.e. reachable from a | 
|---|
| 1171 | /// component export or via `dlopen`. | 
|---|
| 1172 | fn find_reachable<'a>( | 
|---|
| 1173 | metadata: &'a [Metadata<'a>], | 
|---|
| 1174 | dependencies: &IndexMap<usize, IndexSet<usize>>, | 
|---|
| 1175 | ) -> IndexSet<&'a str> { | 
|---|
| 1176 | let reachable = metadata | 
|---|
| 1177 | .iter() | 
|---|
| 1178 | .enumerate() | 
|---|
| 1179 | .filter_map(|(index, metadata)| { | 
|---|
| 1180 | if metadata.has_component_exports || metadata.dl_openable || metadata.has_wasi_start { | 
|---|
| 1181 | Some(index) | 
|---|
| 1182 | } else { | 
|---|
| 1183 | None | 
|---|
| 1184 | } | 
|---|
| 1185 | }) | 
|---|
| 1186 | .collect_unique::<IndexSet<_>>(); | 
|---|
| 1187 |  | 
|---|
| 1188 | let empty = &IndexSet::new(); | 
|---|
| 1189 |  | 
|---|
| 1190 | reachable | 
|---|
| 1191 | .iter() | 
|---|
| 1192 | .chain( | 
|---|
| 1193 | reachable | 
|---|
| 1194 | .iter() | 
|---|
| 1195 | .flat_map(|index| dependencies.get(index).unwrap_or(empty)), | 
|---|
| 1196 | ) | 
|---|
| 1197 | .map(|&index| metadata[index].name) | 
|---|
| 1198 | .collect() | 
|---|
| 1199 | } | 
|---|
| 1200 |  | 
|---|
| 1201 | /// Builder type for composing dynamic library modules into a component | 
|---|
| 1202 | #[ derive(Default)] | 
|---|
| 1203 | pub struct Linker { | 
|---|
| 1204 | /// The `(name, module, dl_openable)` triple representing the libraries to be composed | 
|---|
| 1205 | /// | 
|---|
| 1206 | /// The order of this list determines priority in cases where more than one library exports the same symbol. | 
|---|
| 1207 | libraries: Vec<(String, Vec<u8>, bool)>, | 
|---|
| 1208 |  | 
|---|
| 1209 | /// The set of adapters to use when generating the component | 
|---|
| 1210 | adapters: Vec<(String, Vec<u8>)>, | 
|---|
| 1211 |  | 
|---|
| 1212 | /// Whether to validate the resulting component prior to returning it | 
|---|
| 1213 | validate: bool, | 
|---|
| 1214 |  | 
|---|
| 1215 | /// Whether to generate trapping stubs for any unresolved imports | 
|---|
| 1216 | stub_missing_functions: bool, | 
|---|
| 1217 |  | 
|---|
| 1218 | /// Whether to use a built-in implementation of `dlopen`/`dlsym`. | 
|---|
| 1219 | use_built_in_libdl: bool, | 
|---|
| 1220 |  | 
|---|
| 1221 | /// Size of stack (in bytes) to allocate in the synthesized main module | 
|---|
| 1222 | /// | 
|---|
| 1223 | /// If `None`, use `DEFAULT_STACK_SIZE_BYTES`. | 
|---|
| 1224 | stack_size: Option<u32>, | 
|---|
| 1225 |  | 
|---|
| 1226 | /// This affects how when to WIT worlds are merged together, for example | 
|---|
| 1227 | /// from two different libraries, whether their imports are unified when the | 
|---|
| 1228 | /// semver version ranges for interface allow it. | 
|---|
| 1229 | merge_imports_based_on_semver: Option<bool>, | 
|---|
| 1230 | } | 
|---|
| 1231 |  | 
|---|
| 1232 | impl Linker { | 
|---|
| 1233 | /// Add a dynamic library module to this linker. | 
|---|
| 1234 | /// | 
|---|
| 1235 | /// If `dl_openable` is true, all of the libraries exports will be added to the `dlopen`/`dlsym` lookup table | 
|---|
| 1236 | /// for runtime resolution. | 
|---|
| 1237 | pub fn library(mut self, name: &str, module: &[u8], dl_openable: bool) -> Result<Self> { | 
|---|
| 1238 | self.libraries | 
|---|
| 1239 | .push((name.to_owned(), module.to_vec(), dl_openable)); | 
|---|
| 1240 |  | 
|---|
| 1241 | Ok(self) | 
|---|
| 1242 | } | 
|---|
| 1243 |  | 
|---|
| 1244 | /// Add an adapter to this linker. | 
|---|
| 1245 | /// | 
|---|
| 1246 | /// See [crate::encoding::ComponentEncoder::adapter] for details. | 
|---|
| 1247 | pub fn adapter(mut self, name: &str, module: &[u8]) -> Result<Self> { | 
|---|
| 1248 | self.adapters.push((name.to_owned(), module.to_vec())); | 
|---|
| 1249 |  | 
|---|
| 1250 | Ok(self) | 
|---|
| 1251 | } | 
|---|
| 1252 |  | 
|---|
| 1253 | /// Specify whether to validate the resulting component prior to returning it | 
|---|
| 1254 | pub fn validate(mut self, validate: bool) -> Self { | 
|---|
| 1255 | self.validate = validate; | 
|---|
| 1256 | self | 
|---|
| 1257 | } | 
|---|
| 1258 |  | 
|---|
| 1259 | /// Specify size of stack to allocate in the synthesized main module | 
|---|
| 1260 | pub fn stack_size(mut self, stack_size: u32) -> Self { | 
|---|
| 1261 | self.stack_size = Some(stack_size); | 
|---|
| 1262 | self | 
|---|
| 1263 | } | 
|---|
| 1264 |  | 
|---|
| 1265 | /// Specify whether to generate trapping stubs for any unresolved imports | 
|---|
| 1266 | pub fn stub_missing_functions(mut self, stub_missing_functions: bool) -> Self { | 
|---|
| 1267 | self.stub_missing_functions = stub_missing_functions; | 
|---|
| 1268 | self | 
|---|
| 1269 | } | 
|---|
| 1270 |  | 
|---|
| 1271 | /// Specify whether to use a built-in implementation of `dlopen`/`dlsym`. | 
|---|
| 1272 | pub fn use_built_in_libdl(mut self, use_built_in_libdl: bool) -> Self { | 
|---|
| 1273 | self.use_built_in_libdl = use_built_in_libdl; | 
|---|
| 1274 | self | 
|---|
| 1275 | } | 
|---|
| 1276 |  | 
|---|
| 1277 | /// This affects how when to WIT worlds are merged together, for example | 
|---|
| 1278 | /// from two different libraries, whether their imports are unified when the | 
|---|
| 1279 | /// semver version ranges for interface allow it. | 
|---|
| 1280 | /// | 
|---|
| 1281 | /// This is enabled by default. | 
|---|
| 1282 | pub fn merge_imports_based_on_semver(mut self, merge: bool) -> Self { | 
|---|
| 1283 | self.merge_imports_based_on_semver = Some(merge); | 
|---|
| 1284 | self | 
|---|
| 1285 | } | 
|---|
| 1286 |  | 
|---|
| 1287 | /// Encode the component and return the bytes | 
|---|
| 1288 | pub fn encode(mut self) -> Result<Vec<u8>> { | 
|---|
| 1289 | if self.use_built_in_libdl { | 
|---|
| 1290 | self.use_built_in_libdl = false; | 
|---|
| 1291 | self = self.library( "libdl.so", include_bytes!( "../libdl.so"), false)?; | 
|---|
| 1292 | } | 
|---|
| 1293 |  | 
|---|
| 1294 | let adapter_names = self | 
|---|
| 1295 | .adapters | 
|---|
| 1296 | .iter() | 
|---|
| 1297 | .map(|(name, _)| name.as_str()) | 
|---|
| 1298 | .collect_unique::<HashSet<_>>(); | 
|---|
| 1299 |  | 
|---|
| 1300 | if adapter_names.len() != self.adapters.len() { | 
|---|
| 1301 | bail!( "duplicate adapter name"); | 
|---|
| 1302 | } | 
|---|
| 1303 |  | 
|---|
| 1304 | let metadata = self | 
|---|
| 1305 | .libraries | 
|---|
| 1306 | .iter() | 
|---|
| 1307 | .map(|(name, module, dl_openable)| { | 
|---|
| 1308 | Metadata::try_new(name, *dl_openable, module, &adapter_names) | 
|---|
| 1309 | .with_context(|| format!( "failed to extract linking metadata from {name} ")) | 
|---|
| 1310 | }) | 
|---|
| 1311 | .collect::<Result<Vec<_>>>()?; | 
|---|
| 1312 |  | 
|---|
| 1313 | { | 
|---|
| 1314 | let names = self | 
|---|
| 1315 | .libraries | 
|---|
| 1316 | .iter() | 
|---|
| 1317 | .map(|(name, ..)| name.as_str()) | 
|---|
| 1318 | .collect_unique::<HashSet<_>>(); | 
|---|
| 1319 |  | 
|---|
| 1320 | let missing = metadata | 
|---|
| 1321 | .iter() | 
|---|
| 1322 | .filter_map(|metadata| { | 
|---|
| 1323 | let missing = metadata | 
|---|
| 1324 | .needed_libs | 
|---|
| 1325 | .iter() | 
|---|
| 1326 | .copied() | 
|---|
| 1327 | .filter(|name| !names.contains(*name)) | 
|---|
| 1328 | .collect::<Vec<_>>(); | 
|---|
| 1329 |  | 
|---|
| 1330 | if missing.is_empty() { | 
|---|
| 1331 | None | 
|---|
| 1332 | } else { | 
|---|
| 1333 | Some((metadata.name, missing)) | 
|---|
| 1334 | } | 
|---|
| 1335 | }) | 
|---|
| 1336 | .collect::<Vec<_>>(); | 
|---|
| 1337 |  | 
|---|
| 1338 | if !missing.is_empty() { | 
|---|
| 1339 | bail!( | 
|---|
| 1340 | "missing libraries:\n{} ", | 
|---|
| 1341 | missing | 
|---|
| 1342 | .iter() | 
|---|
| 1343 | .map(|(needed_by, missing)| format!( | 
|---|
| 1344 | "\t{needed_by}  needs {} ", | 
|---|
| 1345 | missing.join( ", ") | 
|---|
| 1346 | )) | 
|---|
| 1347 | .collect::<Vec<_>>() | 
|---|
| 1348 | .join( "\n ") | 
|---|
| 1349 | ); | 
|---|
| 1350 | } | 
|---|
| 1351 | } | 
|---|
| 1352 |  | 
|---|
| 1353 | let exporters = resolve_exporters(&metadata)?; | 
|---|
| 1354 |  | 
|---|
| 1355 | let cabi_realloc_exporter = exporters | 
|---|
| 1356 | .get(&ExportKey { | 
|---|
| 1357 | name: "cabi_realloc", | 
|---|
| 1358 | ty: Type::Function(FunctionType { | 
|---|
| 1359 | parameters: vec![ValueType::I32; 4], | 
|---|
| 1360 | results: vec![ValueType::I32], | 
|---|
| 1361 | }), | 
|---|
| 1362 | }) | 
|---|
| 1363 | .map(|exporters| exporters.first().unwrap().0); | 
|---|
| 1364 |  | 
|---|
| 1365 | let (exporters, missing, _) = resolve_symbols(&metadata, &exporters); | 
|---|
| 1366 |  | 
|---|
| 1367 | if !missing.is_empty() { | 
|---|
| 1368 | if missing | 
|---|
| 1369 | .iter() | 
|---|
| 1370 | .all(|(_, export)| matches!(&export.key.ty, Type::Function(_))) | 
|---|
| 1371 | && (self.stub_missing_functions | 
|---|
| 1372 | || missing | 
|---|
| 1373 | .iter() | 
|---|
| 1374 | .all(|(_, export)| export.flags.contains(SymbolFlags::BINDING_WEAK))) | 
|---|
| 1375 | { | 
|---|
| 1376 | self.stub_missing_functions = false; | 
|---|
| 1377 | self.libraries.push(( | 
|---|
| 1378 | "wit-component:stubs".into(), | 
|---|
| 1379 | make_stubs_module(&missing), | 
|---|
| 1380 | false, | 
|---|
| 1381 | )); | 
|---|
| 1382 | return self.encode(); | 
|---|
| 1383 | } else { | 
|---|
| 1384 | bail!( | 
|---|
| 1385 | "unresolved symbol(s):\n{} ", | 
|---|
| 1386 | missing | 
|---|
| 1387 | .iter() | 
|---|
| 1388 | .filter(|(_, export)| !export.flags.contains(SymbolFlags::BINDING_WEAK)) | 
|---|
| 1389 | .map(|(importer, export)| { format!( "\t{importer}  needs {} ", export.key) }) | 
|---|
| 1390 | .collect::<Vec<_>>() | 
|---|
| 1391 | .join( "\n ") | 
|---|
| 1392 | ); | 
|---|
| 1393 | } | 
|---|
| 1394 | } | 
|---|
| 1395 |  | 
|---|
| 1396 | let dependencies = find_dependencies(&metadata, &exporters)?; | 
|---|
| 1397 |  | 
|---|
| 1398 | { | 
|---|
| 1399 | let reachable = find_reachable(&metadata, &dependencies); | 
|---|
| 1400 | let unreachable = self | 
|---|
| 1401 | .libraries | 
|---|
| 1402 | .iter() | 
|---|
| 1403 | .filter_map(|(name, ..)| (!reachable.contains(name.as_str())).then(|| name.clone())) | 
|---|
| 1404 | .collect_unique::<HashSet<_>>(); | 
|---|
| 1405 |  | 
|---|
| 1406 | if !unreachable.is_empty() { | 
|---|
| 1407 | self.libraries | 
|---|
| 1408 | .retain(|(name, ..)| !unreachable.contains(name)); | 
|---|
| 1409 | return self.encode(); | 
|---|
| 1410 | } | 
|---|
| 1411 | } | 
|---|
| 1412 |  | 
|---|
| 1413 | let topo_sorted = topo_sort(metadata.len(), &dependencies)?; | 
|---|
| 1414 |  | 
|---|
| 1415 | let EnvFunctionExports { | 
|---|
| 1416 | exports: env_function_exports, | 
|---|
| 1417 | reexport_cabi_realloc, | 
|---|
| 1418 | } = env_function_exports(&metadata, &exporters, &topo_sorted)?; | 
|---|
| 1419 |  | 
|---|
| 1420 | let (env_module, dl_openables, table_base) = make_env_module( | 
|---|
| 1421 | &metadata, | 
|---|
| 1422 | &env_function_exports, | 
|---|
| 1423 | if reexport_cabi_realloc { | 
|---|
| 1424 | // If "env" module already reexports "cabi_realloc", we don't need to | 
|---|
| 1425 | // reexport it again. | 
|---|
| 1426 | None | 
|---|
| 1427 | } else { | 
|---|
| 1428 | cabi_realloc_exporter | 
|---|
| 1429 | }, | 
|---|
| 1430 | self.stack_size.unwrap_or(DEFAULT_STACK_SIZE_BYTES), | 
|---|
| 1431 | ); | 
|---|
| 1432 |  | 
|---|
| 1433 | let mut encoder = ComponentEncoder::default().validate(self.validate); | 
|---|
| 1434 | if let Some(merge) = self.merge_imports_based_on_semver { | 
|---|
| 1435 | encoder = encoder.merge_imports_based_on_semver(merge); | 
|---|
| 1436 | }; | 
|---|
| 1437 | encoder = encoder.module(&env_module)?; | 
|---|
| 1438 |  | 
|---|
| 1439 | for (name, module) in &self.adapters { | 
|---|
| 1440 | encoder = encoder.adapter(name, module)?; | 
|---|
| 1441 | } | 
|---|
| 1442 |  | 
|---|
| 1443 | let default_env_items = [ | 
|---|
| 1444 | Item { | 
|---|
| 1445 | alias: "memory".into(), | 
|---|
| 1446 | kind: ExportKind::Memory, | 
|---|
| 1447 | which: MainOrAdapter::Main, | 
|---|
| 1448 | name: "memory".into(), | 
|---|
| 1449 | }, | 
|---|
| 1450 | Item { | 
|---|
| 1451 | alias: "__indirect_function_table".into(), | 
|---|
| 1452 | kind: ExportKind::Table, | 
|---|
| 1453 | which: MainOrAdapter::Main, | 
|---|
| 1454 | name: "__indirect_function_table".into(), | 
|---|
| 1455 | }, | 
|---|
| 1456 | Item { | 
|---|
| 1457 | alias: "__stack_pointer".into(), | 
|---|
| 1458 | kind: ExportKind::Global, | 
|---|
| 1459 | which: MainOrAdapter::Main, | 
|---|
| 1460 | name: "__stack_pointer".into(), | 
|---|
| 1461 | }, | 
|---|
| 1462 | ]; | 
|---|
| 1463 |  | 
|---|
| 1464 | let mut seen = HashSet::new(); | 
|---|
| 1465 | for index in topo_sorted { | 
|---|
| 1466 | let (name, module, _) = &self.libraries[index]; | 
|---|
| 1467 | let metadata = &metadata[index]; | 
|---|
| 1468 |  | 
|---|
| 1469 | let env_items = default_env_items | 
|---|
| 1470 | .iter() | 
|---|
| 1471 | .cloned() | 
|---|
| 1472 | .chain([ | 
|---|
| 1473 | Item { | 
|---|
| 1474 | alias: "__memory_base".into(), | 
|---|
| 1475 | kind: ExportKind::Global, | 
|---|
| 1476 | which: MainOrAdapter::Main, | 
|---|
| 1477 | name: format!( "{name} :memory_base"), | 
|---|
| 1478 | }, | 
|---|
| 1479 | Item { | 
|---|
| 1480 | alias: "__table_base".into(), | 
|---|
| 1481 | kind: ExportKind::Global, | 
|---|
| 1482 | which: MainOrAdapter::Main, | 
|---|
| 1483 | name: format!( "{name} :table_base"), | 
|---|
| 1484 | }, | 
|---|
| 1485 | ]) | 
|---|
| 1486 | .chain(metadata.env_imports.iter().map(|(name, (ty, _))| { | 
|---|
| 1487 | let (exporter, _) = find_function_exporter(name, ty, &exporters).unwrap(); | 
|---|
| 1488 |  | 
|---|
| 1489 | Item { | 
|---|
| 1490 | alias: (*name).into(), | 
|---|
| 1491 | kind: ExportKind::Func, | 
|---|
| 1492 | which: if seen.contains(exporter) { | 
|---|
| 1493 | MainOrAdapter::Adapter(exporter.to_owned()) | 
|---|
| 1494 | } else { | 
|---|
| 1495 | MainOrAdapter::Main | 
|---|
| 1496 | }, | 
|---|
| 1497 | name: (*name).into(), | 
|---|
| 1498 | } | 
|---|
| 1499 | })) | 
|---|
| 1500 | .chain(if metadata.is_asyncified { | 
|---|
| 1501 | vec![ | 
|---|
| 1502 | Item { | 
|---|
| 1503 | alias: "__asyncify_state".into(), | 
|---|
| 1504 | kind: ExportKind::Global, | 
|---|
| 1505 | which: MainOrAdapter::Main, | 
|---|
| 1506 | name: "__asyncify_state".into(), | 
|---|
| 1507 | }, | 
|---|
| 1508 | Item { | 
|---|
| 1509 | alias: "__asyncify_data".into(), | 
|---|
| 1510 | kind: ExportKind::Global, | 
|---|
| 1511 | which: MainOrAdapter::Main, | 
|---|
| 1512 | name: "__asyncify_data".into(), | 
|---|
| 1513 | }, | 
|---|
| 1514 | ] | 
|---|
| 1515 | } else { | 
|---|
| 1516 | vec![] | 
|---|
| 1517 | }) | 
|---|
| 1518 | .collect(); | 
|---|
| 1519 |  | 
|---|
| 1520 | let global_item = |address_name: &str| Item { | 
|---|
| 1521 | alias: address_name.into(), | 
|---|
| 1522 | kind: ExportKind::Global, | 
|---|
| 1523 | which: MainOrAdapter::Main, | 
|---|
| 1524 | name: format!( "{name} :{address_name} "), | 
|---|
| 1525 | }; | 
|---|
| 1526 |  | 
|---|
| 1527 | let mem_items = metadata | 
|---|
| 1528 | .memory_address_imports | 
|---|
| 1529 | .iter() | 
|---|
| 1530 | .copied() | 
|---|
| 1531 | .map(global_item) | 
|---|
| 1532 | .chain([ "__heap_base", "__heap_end"].into_iter().map(|name| Item { | 
|---|
| 1533 | alias: name.into(), | 
|---|
| 1534 | kind: ExportKind::Global, | 
|---|
| 1535 | which: MainOrAdapter::Main, | 
|---|
| 1536 | name: name.into(), | 
|---|
| 1537 | })) | 
|---|
| 1538 | .collect(); | 
|---|
| 1539 |  | 
|---|
| 1540 | let func_items = metadata | 
|---|
| 1541 | .table_address_imports | 
|---|
| 1542 | .iter() | 
|---|
| 1543 | .copied() | 
|---|
| 1544 | .map(global_item) | 
|---|
| 1545 | .collect(); | 
|---|
| 1546 |  | 
|---|
| 1547 | let mut import_items = BTreeMap::<_, Vec<_>>::new(); | 
|---|
| 1548 | for import in &metadata.imports { | 
|---|
| 1549 | import_items.entry(import.module).or_default().push(Item { | 
|---|
| 1550 | alias: import.name.into(), | 
|---|
| 1551 | kind: ExportKind::from(&import.ty), | 
|---|
| 1552 | which: MainOrAdapter::Main, | 
|---|
| 1553 | name: format!( "{} :{} ", import.module, import.name), | 
|---|
| 1554 | }); | 
|---|
| 1555 | } | 
|---|
| 1556 |  | 
|---|
| 1557 | encoder = encoder.library( | 
|---|
| 1558 | name, | 
|---|
| 1559 | module, | 
|---|
| 1560 | LibraryInfo { | 
|---|
| 1561 | instantiate_after_shims: false, | 
|---|
| 1562 | arguments: [ | 
|---|
| 1563 | ( "GOT.mem".into(), Instance::Items(mem_items)), | 
|---|
| 1564 | ( "GOT.func".into(), Instance::Items(func_items)), | 
|---|
| 1565 | ( "env".into(), Instance::Items(env_items)), | 
|---|
| 1566 | ] | 
|---|
| 1567 | .into_iter() | 
|---|
| 1568 | .chain( | 
|---|
| 1569 | import_items | 
|---|
| 1570 | .into_iter() | 
|---|
| 1571 | .map(|(k, v)| (k.into(), Instance::Items(v))), | 
|---|
| 1572 | ) | 
|---|
| 1573 | .collect(), | 
|---|
| 1574 | }, | 
|---|
| 1575 | )?; | 
|---|
| 1576 |  | 
|---|
| 1577 | seen.insert(name.as_str()); | 
|---|
| 1578 | } | 
|---|
| 1579 |  | 
|---|
| 1580 | encoder | 
|---|
| 1581 | .library( | 
|---|
| 1582 | "__init", | 
|---|
| 1583 | &make_init_module( | 
|---|
| 1584 | &metadata, | 
|---|
| 1585 | &exporters, | 
|---|
| 1586 | &env_function_exports, | 
|---|
| 1587 | dl_openables, | 
|---|
| 1588 | table_base, | 
|---|
| 1589 | )?, | 
|---|
| 1590 | LibraryInfo { | 
|---|
| 1591 | instantiate_after_shims: true, | 
|---|
| 1592 | arguments: iter::once(( | 
|---|
| 1593 | "env".into(), | 
|---|
| 1594 | Instance::MainOrAdapter(MainOrAdapter::Main), | 
|---|
| 1595 | )) | 
|---|
| 1596 | .chain(self.libraries.iter().map(|(name, ..)| { | 
|---|
| 1597 | ( | 
|---|
| 1598 | name.clone(), | 
|---|
| 1599 | Instance::MainOrAdapter(MainOrAdapter::Adapter(name.clone())), | 
|---|
| 1600 | ) | 
|---|
| 1601 | })) | 
|---|
| 1602 | .collect(), | 
|---|
| 1603 | }, | 
|---|
| 1604 | )? | 
|---|
| 1605 | .encode() | 
|---|
| 1606 | } | 
|---|
| 1607 | } | 
|---|
| 1608 |  | 
|---|