/src/wasm-tools/crates/wit-component/src/linking/metadata.rs
Line | Count | Source |
1 | | //! Support for parsing and analyzing [dynamic |
2 | | //! library](https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md) modules. |
3 | | |
4 | | use { |
5 | | anyhow::{Context, Error, Result, bail}, |
6 | | std::{ |
7 | | collections::{BTreeSet, HashMap, HashSet}, |
8 | | fmt, |
9 | | }, |
10 | | wasmparser::{ |
11 | | Dylink0Subsection, ExternalKind, FuncType, KnownCustom, MemInfo, Parser, Payload, RefType, |
12 | | SymbolFlags, TableType, TagKind, TagType, TypeRef, ValType, |
13 | | }, |
14 | | }; |
15 | | |
16 | | pub const ENV: &str = "env"; |
17 | | pub const GOT_MEM: &str = "GOT.mem"; |
18 | | pub const GOT_FUNC: &str = "GOT.func"; |
19 | | pub const MEMORY: &str = "memory"; |
20 | | pub const MEMORY_BASE: &str = "__memory_base"; |
21 | | pub const TABLE_BASE: &str = "__table_base"; |
22 | | pub const STACK_POINTER: &str = "__stack_pointer"; |
23 | | pub const INIT_STACK_POINTER: &str = "__init_stack_pointer"; |
24 | | pub const ASYNCIFY_DATA: &str = "__asyncify_data"; |
25 | | pub const ASYNCIFY_STATE: &str = "__asyncify_state"; |
26 | | pub const INDIRECT_FUNCTION_TABLE: &str = "__indirect_function_table"; |
27 | | pub const HEAP_BASE: &str = "__heap_base"; |
28 | | pub const HEAP_END: &str = "__heap_end"; |
29 | | pub const STACK_HIGH: &str = "__stack_high"; |
30 | | pub const STACK_LOW: &str = "__stack_low"; |
31 | | pub const APPLY_DATA_RELOCS: &str = "__wasm_apply_data_relocs"; |
32 | | pub const CALL_CTORS: &str = "__wasm_call_ctors"; |
33 | | pub const INITIALIZE: &str = "_initialize"; |
34 | | pub const START: &str = "_start"; |
35 | | pub const LIBDL_LIBRARIES: &str = "__wasm_libdl_libraries"; |
36 | | pub const TASK_HOOK: &str = "__wasm_task_hook"; |
37 | | pub const ROOT: &str = "$root"; |
38 | | pub const THREAD_NEW_INDIRECT: &str = "[thread-new-indirect-v0]"; |
39 | | pub const CONTEXT_GET_1: &str = "[context-get-1]"; |
40 | | pub const GET_STACK_POINTER: &str = "__wasm_get_stack_pointer"; |
41 | | pub const SET_STACK_POINTER: &str = "__wasm_set_stack_pointer"; |
42 | | pub const GET_TLS_BASE: &str = "__wasm_get_tls_base"; |
43 | | pub const SET_TLS_BASE: &str = "__wasm_set_tls_base"; |
44 | | pub const PROGRAM_TLS_INFO: &str = "__wasm_program_tls_info"; |
45 | | pub const LIBRARY_TLS_INFO: &str = "__wasm_library_tls_info"; |
46 | | |
47 | | /// Represents a core Wasm value type (not including V128 or reference types, which are not yet supported) |
48 | | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
49 | | pub enum ValueType { |
50 | | I32, |
51 | | I64, |
52 | | F32, |
53 | | F64, |
54 | | } |
55 | | |
56 | | impl TryFrom<ValType> for ValueType { |
57 | | type Error = Error; |
58 | | |
59 | 0 | fn try_from(value: ValType) -> Result<Self> { |
60 | 0 | Ok(match value { |
61 | 0 | ValType::I32 => Self::I32, |
62 | 0 | ValType::I64 => Self::I64, |
63 | 0 | ValType::F32 => Self::F32, |
64 | 0 | ValType::F64 => Self::F64, |
65 | 0 | _ => bail!("{value:?} not yet supported"), |
66 | | }) |
67 | 0 | } |
68 | | } |
69 | | |
70 | | impl From<ValueType> for wasm_encoder::ValType { |
71 | 0 | fn from(value: ValueType) -> Self { |
72 | 0 | match value { |
73 | 0 | ValueType::I32 => Self::I32, |
74 | 0 | ValueType::I64 => Self::I64, |
75 | 0 | ValueType::F32 => Self::F32, |
76 | 0 | ValueType::F64 => Self::F64, |
77 | | } |
78 | 0 | } |
79 | | } |
80 | | |
81 | | /// Represents a core Wasm function type |
82 | | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
83 | | pub struct FunctionType { |
84 | | pub parameters: Vec<ValueType>, |
85 | | pub results: Vec<ValueType>, |
86 | | } |
87 | | |
88 | | impl fmt::Display for FunctionType { |
89 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
90 | 0 | write!(f, "{:?} -> {:?}", self.parameters, self.results) |
91 | 0 | } |
92 | | } |
93 | | |
94 | | impl TryFrom<&FuncType> for FunctionType { |
95 | | type Error = Error; |
96 | | |
97 | 0 | fn try_from(value: &FuncType) -> Result<Self> { |
98 | | Ok(Self { |
99 | 0 | parameters: value |
100 | 0 | .params() |
101 | 0 | .iter() |
102 | 0 | .map(|&v| ValueType::try_from(v)) |
103 | 0 | .collect::<Result<_>>()?, |
104 | 0 | results: value |
105 | 0 | .results() |
106 | 0 | .iter() |
107 | 0 | .map(|&v| ValueType::try_from(v)) |
108 | 0 | .collect::<Result<_>>()?, |
109 | | }) |
110 | 0 | } |
111 | | } |
112 | | |
113 | 0 | fn validate_intrinsic_signature( |
114 | 0 | types: &[FuncType], |
115 | 0 | import: wasmparser::Import<'_>, |
116 | 0 | parameters: &[ValueType], |
117 | 0 | results: &[ValueType], |
118 | 0 | ) -> Result<()> { |
119 | 0 | let module = import.module; |
120 | 0 | let name = import.name; |
121 | 0 | let TypeRef::Func(type_index) = import.ty else { |
122 | 0 | bail!("unexpected type for {module}:{name}: {:?}", import.ty); |
123 | | }; |
124 | 0 | let type_index = usize::try_from(type_index).context("function type index is too large")?; |
125 | 0 | let ty = types.get(type_index).with_context(|| { |
126 | 0 | format!("invalid function type index {type_index} for `{module}.{name}`") |
127 | 0 | })?; |
128 | 0 | let actual = FunctionType::try_from(ty) |
129 | 0 | .with_context(|| format!("failed to read function type for `{module}.{name}`"))?; |
130 | 0 | ensure_linker_abi( |
131 | 0 | &format!("function `{module}.{name}`"), |
132 | 0 | &actual, |
133 | 0 | parameters, |
134 | 0 | results, |
135 | | ) |
136 | 0 | } |
137 | | |
138 | | /// Checks an export that the linker's generated start function calls, such as |
139 | | /// `__wasm_call_ctors`, against the `[] -> []` type it is called with. |
140 | | /// |
141 | | /// `_initialize` and `_start` aren't checked here: the component encoder |
142 | | /// already rejects those with the wrong type when it classifies the exports. |
143 | 0 | fn validate_linker_called_export( |
144 | 0 | types: &[FuncType], |
145 | 0 | function_types: &[usize], |
146 | 0 | export: wasmparser::Export<'_>, |
147 | 0 | ) -> Result<()> { |
148 | 0 | let name = export.name; |
149 | 0 | if export.kind != ExternalKind::Func { |
150 | 0 | bail!("unexpected kind for export `{name}`: {:?}", export.kind); |
151 | 0 | } |
152 | 0 | let actual = |
153 | 0 | FunctionType::try_from(&types[function_types[usize::try_from(export.index).unwrap()]]) |
154 | 0 | .with_context(|| format!("failed to read function type for export `{name}`"))?; |
155 | 0 | ensure_linker_abi(&format!("export `{name}`"), &actual, &[], &[]) |
156 | 0 | } |
157 | | |
158 | 0 | fn ensure_linker_abi( |
159 | 0 | what: &str, |
160 | 0 | actual: &FunctionType, |
161 | 0 | parameters: &[ValueType], |
162 | 0 | results: &[ValueType], |
163 | 0 | ) -> Result<()> { |
164 | 0 | if actual.parameters.as_slice() != parameters || actual.results.as_slice() != results { |
165 | 0 | let expected = FunctionType { |
166 | 0 | parameters: parameters.to_vec(), |
167 | 0 | results: results.to_vec(), |
168 | 0 | }; |
169 | 0 | bail!("type mismatch for {what}: required linker ABI `{expected}` but found `{actual}`"); |
170 | 0 | } |
171 | 0 | Ok(()) |
172 | 0 | } |
173 | | |
174 | | /// Represents a core Wasm global variable type |
175 | | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
176 | | pub struct GlobalType { |
177 | | pub ty: ValueType, |
178 | | pub mutable: bool, |
179 | | pub shared: bool, |
180 | | } |
181 | | |
182 | | impl fmt::Display for GlobalType { |
183 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
184 | 0 | if self.mutable { |
185 | 0 | write!(f, "mut ")?; |
186 | 0 | } |
187 | 0 | write!(f, "{:?}", self.ty) |
188 | 0 | } |
189 | | } |
190 | | |
191 | | /// Represents a core Wasm export or import type |
192 | | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
193 | | pub enum Type { |
194 | | Function(FunctionType), |
195 | | Global(GlobalType), |
196 | | Tag(FunctionType), |
197 | | } |
198 | | |
199 | | impl fmt::Display for Type { |
200 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
201 | 0 | match self { |
202 | 0 | Self::Function(ty) => write!(f, "function {ty}"), |
203 | 0 | Self::Global(ty) => write!(f, "global {ty}"), |
204 | 0 | Self::Tag(ty) => write!(f, "tag {ty}"), |
205 | | } |
206 | 0 | } |
207 | | } |
208 | | |
209 | | impl From<&Type> for wasm_encoder::ExportKind { |
210 | 0 | fn from(value: &Type) -> Self { |
211 | 0 | match value { |
212 | 0 | Type::Function(_) => wasm_encoder::ExportKind::Func, |
213 | 0 | Type::Global(_) => wasm_encoder::ExportKind::Global, |
214 | 0 | Type::Tag(_) => wasm_encoder::ExportKind::Tag, |
215 | | } |
216 | 0 | } |
217 | | } |
218 | | |
219 | | /// Represents a core Wasm import |
220 | | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
221 | | pub struct Import<'a> { |
222 | | pub module: &'a str, |
223 | | pub name: &'a str, |
224 | | pub ty: Type, |
225 | | pub flags: SymbolFlags, |
226 | | } |
227 | | |
228 | | /// Represents a core Wasm export |
229 | | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
230 | | pub struct ExportKey<'a> { |
231 | | pub name: &'a str, |
232 | | pub ty: Type, |
233 | | } |
234 | | |
235 | | impl<'a> fmt::Display for ExportKey<'a> { |
236 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
237 | 0 | write!(f, "{} ({})", self.name, self.ty) |
238 | 0 | } |
239 | | } |
240 | | |
241 | | /// Represents a core Wasm export, including dylink.0 flags |
242 | | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
243 | | pub struct Export<'a> { |
244 | | pub key: ExportKey<'a>, |
245 | | pub flags: SymbolFlags, |
246 | | } |
247 | | |
248 | | /// Metadata extracted from a dynamic library module |
249 | | #[derive(Debug)] |
250 | | pub struct Metadata<'a> { |
251 | | /// The name of the module |
252 | | /// |
253 | | /// This is currently not part of the file itself and must be provided separately, but the plan is to add |
254 | | /// something like a `WASM_DYLINK_SO_NAME` field to the dynamic linking tool convention so we can parse it |
255 | | /// along with everything else. |
256 | | pub name: &'a str, |
257 | | |
258 | | /// Whether this module should be resolvable via `dlopen` |
259 | | pub dl_openable: bool, |
260 | | |
261 | | /// The `WASM_DYLINK_MEM_INFO` value (or all zeros if not found) |
262 | | pub mem_info: MemInfo, |
263 | | |
264 | | /// The `WASM_DYLINK_NEEDED` values, if any |
265 | | pub needed_libs: Vec<&'a str>, |
266 | | |
267 | | /// The `WASM_DYLINK_RUNTIME_PATH` values, if any |
268 | | pub runtime_path: Vec<&'a str>, |
269 | | |
270 | | /// Whether this module exports `__wasm_apply_data_relocs` |
271 | | pub has_data_relocs: bool, |
272 | | |
273 | | /// Whether this module exports `__wasm_call_ctors` |
274 | | pub has_ctors: bool, |
275 | | |
276 | | /// Whether this module exports `_initialize` |
277 | | pub has_initialize: bool, |
278 | | |
279 | | /// Whether this module exports `_start` |
280 | | pub has_wasi_start: bool, |
281 | | |
282 | | /// Whether this module imports `__wasm_libdl_libraries` |
283 | | pub needs_libdl_libraries: bool, |
284 | | |
285 | | /// Whether this module includes any `component-type*` custom sections which include exports |
286 | | pub has_component_exports: bool, |
287 | | |
288 | | /// Whether this module imports `__asyncify_state` or `__asyncify_data`, indicating that it is |
289 | | /// asyncified with `--pass-arg=asyncify-relocatable` option. |
290 | | pub is_asyncified: bool, |
291 | | |
292 | | /// Whether this module imports `__stack_pointer` |
293 | | pub needs_stack_pointer: bool, |
294 | | |
295 | | /// Whether this module imports `__init_stack_pointer` |
296 | | pub needs_init_stack_pointer: bool, |
297 | | |
298 | | /// Whether this module imports `__heap_base` |
299 | | pub needs_heap_base: bool, |
300 | | |
301 | | /// Whether this module imports `__heap_end` |
302 | | pub needs_heap_end: bool, |
303 | | |
304 | | /// Whether this module imports `__stack_high` |
305 | | pub needs_stack_high: bool, |
306 | | |
307 | | /// Whether this module imports `__stack_low` |
308 | | pub needs_stack_low: bool, |
309 | | |
310 | | /// Whether this module imports `env::__wasm_get_tls_base` |
311 | | pub needs_get_tls_base: bool, |
312 | | |
313 | | /// Whether this module imports `env::__wasm_set_tls_base` |
314 | | pub needs_set_tls_base: bool, |
315 | | |
316 | | /// Whether this module imports the address of `__wasm_program_tls_info` |
317 | | pub needs_program_tls_info: bool, |
318 | | |
319 | | /// Whether this module imports `$root::[thread-new-indirect-v0]`, meaning |
320 | | /// the program may spawn threads and is thus using cooperative threading. |
321 | | pub uses_thread_new_indirect: bool, |
322 | | |
323 | | /// Whether this module exports a `__wasm_library_tls_info` symbol. |
324 | | pub has_library_tls_info: bool, |
325 | | |
326 | | /// The functions imported from the `env` module, if any |
327 | | pub env_imports: BTreeSet<(&'a str, (FunctionType, SymbolFlags))>, |
328 | | |
329 | | /// The memory addresses imported from `GOT.mem`, if any |
330 | | pub memory_address_imports: BTreeSet<&'a str>, |
331 | | |
332 | | /// The table addresses imported from `GOT.func`, if any |
333 | | pub table_address_imports: BTreeSet<&'a str>, |
334 | | |
335 | | /// Imported exception tags |
336 | | pub tag_imports: BTreeSet<(&'a str, FunctionType)>, |
337 | | |
338 | | /// The symbols exported by this module, if any |
339 | | pub exports: BTreeSet<Export<'a>>, |
340 | | |
341 | | /// The symbols imported by this module (and not accounted for in the above fields), if any |
342 | | pub imports: BTreeSet<Import<'a>>, |
343 | | } |
344 | | |
345 | | impl<'a> Metadata<'a> { |
346 | | /// Parse the specified module and extract its metadata. |
347 | 0 | pub fn try_new( |
348 | 0 | name: &'a str, |
349 | 0 | dl_openable: bool, |
350 | 0 | module: &'a [u8], |
351 | 0 | adapter_names: &HashSet<&str>, |
352 | 0 | ) -> Result<Self> { |
353 | 0 | let bindgen = crate::metadata::decode(module)?.1; |
354 | 0 | let has_component_exports = !bindgen.resolve.worlds[bindgen.world].exports.is_empty(); |
355 | | |
356 | 0 | let mut result = Self { |
357 | 0 | name, |
358 | 0 | dl_openable, |
359 | 0 | mem_info: MemInfo { |
360 | 0 | memory_size: 0, |
361 | 0 | memory_alignment: 1, |
362 | 0 | table_size: 0, |
363 | 0 | table_alignment: 1, |
364 | 0 | }, |
365 | 0 | needed_libs: Vec::new(), |
366 | 0 | runtime_path: Vec::new(), |
367 | 0 | has_data_relocs: false, |
368 | 0 | has_ctors: false, |
369 | 0 | has_initialize: false, |
370 | 0 | has_wasi_start: false, |
371 | 0 | needs_libdl_libraries: false, |
372 | 0 | has_component_exports, |
373 | 0 | is_asyncified: false, |
374 | 0 | needs_stack_pointer: false, |
375 | 0 | needs_init_stack_pointer: false, |
376 | 0 | needs_heap_base: false, |
377 | 0 | needs_heap_end: false, |
378 | 0 | needs_stack_high: false, |
379 | 0 | needs_stack_low: false, |
380 | 0 | needs_get_tls_base: false, |
381 | 0 | needs_set_tls_base: false, |
382 | 0 | needs_program_tls_info: false, |
383 | 0 | uses_thread_new_indirect: false, |
384 | 0 | has_library_tls_info: false, |
385 | 0 | env_imports: BTreeSet::new(), |
386 | 0 | memory_address_imports: BTreeSet::new(), |
387 | 0 | table_address_imports: BTreeSet::new(), |
388 | 0 | exports: BTreeSet::new(), |
389 | 0 | imports: BTreeSet::new(), |
390 | 0 | tag_imports: BTreeSet::new(), |
391 | 0 | }; |
392 | 0 | let mut types = Vec::new(); |
393 | 0 | let mut function_types = Vec::new(); |
394 | 0 | let mut global_types = Vec::new(); |
395 | 0 | let mut tag_types = Vec::new(); |
396 | 0 | let mut import_info = HashMap::new(); |
397 | 0 | let mut export_info = HashMap::new(); |
398 | | |
399 | 0 | for payload in Parser::new(0).parse_all(module) { |
400 | 0 | match payload? { |
401 | 0 | Payload::CustomSection(section) => { |
402 | 0 | if let KnownCustom::Dylink0(reader) = section.as_known() { |
403 | 0 | for subsection in reader { |
404 | 0 | match subsection.context("failed to parse `dylink.0` subsection")? { |
405 | 0 | Dylink0Subsection::MemInfo(info) => result.mem_info = info, |
406 | 0 | Dylink0Subsection::Needed(needed) => { |
407 | 0 | result.needed_libs = needed.clone() |
408 | | } |
409 | 0 | Dylink0Subsection::ExportInfo(info) => { |
410 | 0 | export_info |
411 | 0 | .extend(info.iter().map(|info| (info.name, info.flags))); |
412 | | } |
413 | 0 | Dylink0Subsection::ImportInfo(info) => { |
414 | 0 | import_info.extend( |
415 | 0 | info.iter() |
416 | 0 | .map(|info| ((info.module, info.field), info.flags)), |
417 | | ); |
418 | | } |
419 | 0 | Dylink0Subsection::RuntimePath(runtime_path) => { |
420 | 0 | result.runtime_path.extend(runtime_path.iter()); |
421 | 0 | } |
422 | 0 | Dylink0Subsection::TargetArch(arch) => { |
423 | 0 | if arch != "wasm32" { |
424 | 0 | bail!("unsupported `dylink.0` target architecture: {arch}") |
425 | 0 | } |
426 | | } |
427 | 0 | Dylink0Subsection::Unknown { ty, .. } => { |
428 | 0 | bail!("unrecognized `dylink.0` subsection: {ty}") |
429 | | } |
430 | | } |
431 | | } |
432 | 0 | } |
433 | | } |
434 | | |
435 | 0 | Payload::TypeSection(reader) => { |
436 | 0 | types = reader |
437 | 0 | .into_iter_err_on_gc_types() |
438 | 0 | .collect::<Result<Vec<_>, _>>()?; |
439 | | } |
440 | | |
441 | 0 | Payload::ImportSection(reader) => { |
442 | 0 | for import in reader.into_imports() { |
443 | 0 | let import = import?; |
444 | | |
445 | 0 | match import.ty { |
446 | 0 | TypeRef::Func(ty) => function_types.push(usize::try_from(ty).unwrap()), |
447 | 0 | TypeRef::Global(ty) => { |
448 | 0 | global_types.push(ty); |
449 | 0 | } |
450 | 0 | TypeRef::Tag(ty) => tag_types.push(ty), |
451 | 0 | _ => (), |
452 | | } |
453 | | |
454 | 0 | let type_error = || { |
455 | 0 | bail!( |
456 | | "unexpected type for {}:{}: {:?}", |
457 | | import.module, |
458 | | import.name, |
459 | | import.ty |
460 | | ) |
461 | 0 | }; |
462 | | |
463 | 0 | match (import.module, import.name) { |
464 | 0 | (self::ENV, self::MEMORY) => { |
465 | 0 | if !matches!(import.ty, TypeRef::Memory(_)) { |
466 | 0 | return type_error(); |
467 | 0 | } |
468 | | } |
469 | 0 | (self::ENV, self::ASYNCIFY_DATA | self::ASYNCIFY_STATE) => { |
470 | 0 | result.is_asyncified = true; |
471 | 0 | if !matches!( |
472 | 0 | import.ty, |
473 | | TypeRef::Global(wasmparser::GlobalType { |
474 | | content_type: ValType::I32, |
475 | | .. |
476 | | }) |
477 | | ) { |
478 | 0 | return type_error(); |
479 | 0 | } |
480 | | } |
481 | | ( |
482 | | self::ENV, |
483 | 0 | self::MEMORY_BASE |
484 | 0 | | self::TABLE_BASE |
485 | 0 | | self::STACK_POINTER |
486 | 0 | | self::INIT_STACK_POINTER, |
487 | | ) => { |
488 | 0 | if matches!( |
489 | 0 | import.ty, |
490 | | TypeRef::Global(wasmparser::GlobalType { |
491 | | content_type: ValType::I32, |
492 | | .. |
493 | | }) |
494 | | ) { |
495 | 0 | match import.name { |
496 | 0 | self::STACK_POINTER => result.needs_stack_pointer = true, |
497 | 0 | self::INIT_STACK_POINTER => { |
498 | 0 | result.needs_init_stack_pointer = true |
499 | | } |
500 | 0 | _ => {} |
501 | | } |
502 | | } else { |
503 | 0 | return type_error(); |
504 | | } |
505 | | } |
506 | 0 | (self::ENV, self::INDIRECT_FUNCTION_TABLE) => { |
507 | | if let TypeRef::Table(TableType { |
508 | 0 | element_type, |
509 | | maximum: None, |
510 | | .. |
511 | 0 | }) = import.ty |
512 | | { |
513 | 0 | if element_type != RefType::FUNCREF { |
514 | 0 | return type_error(); |
515 | 0 | } |
516 | | } else { |
517 | 0 | return type_error(); |
518 | | } |
519 | | } |
520 | | ( |
521 | | self::ENV, |
522 | 0 | name @ (self::GET_STACK_POINTER |
523 | 0 | | self::SET_STACK_POINTER |
524 | 0 | | self::GET_TLS_BASE |
525 | 0 | | self::SET_TLS_BASE), |
526 | | ) => { |
527 | 0 | let (parameters, results) = match name { |
528 | 0 | self::GET_STACK_POINTER | self::GET_TLS_BASE => { |
529 | 0 | (&[][..], &[ValueType::I32][..]) |
530 | | } |
531 | 0 | self::SET_STACK_POINTER | self::SET_TLS_BASE => { |
532 | 0 | (&[ValueType::I32][..], &[][..]) |
533 | | } |
534 | 0 | _ => unreachable!(), |
535 | | }; |
536 | 0 | validate_intrinsic_signature(&types, import, parameters, results)?; |
537 | 0 | match name { |
538 | 0 | self::GET_TLS_BASE => result.needs_get_tls_base = true, |
539 | 0 | self::SET_TLS_BASE => result.needs_set_tls_base = true, |
540 | 0 | _ => {} |
541 | | } |
542 | | } |
543 | 0 | (self::ENV, name) => match import.ty { |
544 | 0 | TypeRef::Func(ty) => { |
545 | 0 | result.env_imports.insert(( |
546 | 0 | name, |
547 | | ( |
548 | 0 | FunctionType::try_from( |
549 | 0 | &types[usize::try_from(ty).unwrap()], |
550 | 0 | )?, |
551 | 0 | import_info |
552 | 0 | .get(&(self::ENV, name)) |
553 | 0 | .copied() |
554 | 0 | .unwrap_or_default(), |
555 | | ), |
556 | | )); |
557 | | } |
558 | | TypeRef::Tag(TagType { |
559 | | kind: TagKind::Exception, |
560 | 0 | func_type_idx, |
561 | | }) => { |
562 | 0 | result.tag_imports.insert(( |
563 | 0 | name, |
564 | 0 | FunctionType::try_from( |
565 | 0 | &types[usize::try_from(func_type_idx).unwrap()], |
566 | 0 | )?, |
567 | | )); |
568 | | } |
569 | 0 | _ => return type_error(), |
570 | | }, |
571 | 0 | (self::GOT_MEM, name) => { |
572 | | if let TypeRef::Global(wasmparser::GlobalType { |
573 | | content_type: ValType::I32, |
574 | | .. |
575 | 0 | }) = import.ty |
576 | | { |
577 | 0 | match name { |
578 | 0 | self::HEAP_BASE => result.needs_heap_base = true, |
579 | 0 | self::HEAP_END => result.needs_heap_end = true, |
580 | 0 | self::STACK_HIGH => result.needs_stack_high = true, |
581 | 0 | self::STACK_LOW => result.needs_stack_low = true, |
582 | 0 | self::LIBDL_LIBRARIES => { |
583 | 0 | result.needs_libdl_libraries = true; |
584 | 0 | } |
585 | 0 | self::PROGRAM_TLS_INFO => { |
586 | 0 | result.needs_program_tls_info = true; |
587 | 0 | } |
588 | | |
589 | 0 | _ => { |
590 | 0 | result.memory_address_imports.insert(name); |
591 | 0 | } |
592 | | } |
593 | | } else { |
594 | 0 | return type_error(); |
595 | | } |
596 | | } |
597 | 0 | (self::GOT_FUNC, name) => { |
598 | | if let TypeRef::Global(wasmparser::GlobalType { |
599 | | content_type: ValType::I32, |
600 | | .. |
601 | 0 | }) = import.ty |
602 | 0 | { |
603 | 0 | result.table_address_imports.insert(name); |
604 | 0 | } else { |
605 | 0 | return type_error(); |
606 | | } |
607 | | } |
608 | 0 | (self::ROOT, self::THREAD_NEW_INDIRECT) => { |
609 | 0 | validate_intrinsic_signature( |
610 | 0 | &types, |
611 | 0 | import, |
612 | 0 | &[ValueType::I32, ValueType::I32], |
613 | 0 | &[ValueType::I32], |
614 | 0 | )?; |
615 | 0 | result.uses_thread_new_indirect = true; |
616 | | } |
617 | 0 | (module, name) if adapter_names.contains(module) => { |
618 | 0 | let ty = match import.ty { |
619 | | TypeRef::Global(wasmparser::GlobalType { |
620 | 0 | content_type, |
621 | 0 | mutable, |
622 | 0 | shared, |
623 | | }) => Type::Global(GlobalType { |
624 | 0 | ty: content_type.try_into()?, |
625 | 0 | mutable, |
626 | 0 | shared, |
627 | | }), |
628 | 0 | TypeRef::Func(ty) => Type::Function(FunctionType::try_from( |
629 | 0 | &types[usize::try_from(ty).unwrap()], |
630 | 0 | )?), |
631 | 0 | ty => { |
632 | 0 | bail!("unsupported import kind for {module}.{name}: {ty:?}",) |
633 | | } |
634 | | }; |
635 | 0 | let flags = import_info |
636 | 0 | .get(&(module, name)) |
637 | 0 | .copied() |
638 | 0 | .unwrap_or_default(); |
639 | 0 | result.imports.insert(Import { |
640 | 0 | module, |
641 | 0 | name, |
642 | 0 | ty, |
643 | 0 | flags, |
644 | 0 | }); |
645 | | } |
646 | | _ => { |
647 | 0 | if !matches!(import.ty, TypeRef::Func(_) | TypeRef::Global(_)) { |
648 | 0 | return type_error(); |
649 | 0 | } |
650 | | } |
651 | | } |
652 | | } |
653 | | } |
654 | | |
655 | 0 | Payload::FunctionSection(reader) => { |
656 | 0 | for function in reader { |
657 | 0 | function_types.push(usize::try_from(function?).unwrap()); |
658 | | } |
659 | | } |
660 | | |
661 | 0 | Payload::GlobalSection(reader) => { |
662 | 0 | for global in reader { |
663 | 0 | let global = global?; |
664 | 0 | global_types.push(global.ty); |
665 | | } |
666 | | } |
667 | | |
668 | 0 | Payload::TagSection(reader) => { |
669 | 0 | for tag in reader { |
670 | 0 | tag_types.push(tag?); |
671 | | } |
672 | | } |
673 | | |
674 | 0 | Payload::ExportSection(reader) => { |
675 | 0 | for export in reader { |
676 | 0 | let export = export?; |
677 | | |
678 | 0 | match export.name { |
679 | 0 | self::APPLY_DATA_RELOCS => { |
680 | 0 | validate_linker_called_export(&types, &function_types, export)?; |
681 | 0 | result.has_data_relocs = true; |
682 | | } |
683 | 0 | self::CALL_CTORS => { |
684 | 0 | validate_linker_called_export(&types, &function_types, export)?; |
685 | 0 | result.has_ctors = true; |
686 | | } |
687 | 0 | self::INITIALIZE => result.has_initialize = true, |
688 | 0 | self::START => result.has_wasi_start = true, |
689 | 0 | self::LIBRARY_TLS_INFO => result.has_library_tls_info = true, |
690 | | _ => { |
691 | 0 | let ty = match export.kind { |
692 | 0 | ExternalKind::Func => Type::Function(FunctionType::try_from( |
693 | 0 | &types[function_types |
694 | 0 | [usize::try_from(export.index).unwrap()]], |
695 | 0 | )?), |
696 | | ExternalKind::Global => { |
697 | 0 | let ty = |
698 | 0 | global_types[usize::try_from(export.index).unwrap()]; |
699 | | Type::Global(GlobalType { |
700 | 0 | ty: ValueType::try_from(ty.content_type)?, |
701 | 0 | mutable: ty.mutable, |
702 | 0 | shared: ty.shared, |
703 | | }) |
704 | | } |
705 | 0 | ExternalKind::Tag => Type::Tag(FunctionType::try_from( |
706 | 0 | &types[usize::try_from( |
707 | 0 | tag_types[usize::try_from(export.index).unwrap()] |
708 | 0 | .func_type_idx, |
709 | 0 | ) |
710 | 0 | .unwrap()], |
711 | 0 | )?), |
712 | 0 | kind => { |
713 | 0 | bail!( |
714 | | "unsupported export kind for {}: {kind:?}", |
715 | | export.name |
716 | | ) |
717 | | } |
718 | | }; |
719 | 0 | let flags = |
720 | 0 | export_info.get(&export.name).copied().unwrap_or_default(); |
721 | 0 | result.exports.insert(Export { |
722 | 0 | key: ExportKey { |
723 | 0 | name: export.name, |
724 | 0 | ty, |
725 | 0 | }, |
726 | 0 | flags, |
727 | 0 | }); |
728 | | } |
729 | | } |
730 | | } |
731 | | } |
732 | | |
733 | 0 | _ => {} |
734 | | } |
735 | | } |
736 | | |
737 | 0 | Ok(result) |
738 | 0 | } |
739 | | } |