wasmtime_environ/compile/module_environ.rs
1use crate::error::{OutOfMemory, Result, bail};
2use crate::module::{
3 FuncRefIndex, Initializer, MemoryInitialization, Module, TableSegment, TableSegmentElements,
4 TableSegmentInit,
5};
6use crate::prelude::*;
7use crate::{
8 ConstExpr, ConstOp, DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex,
9 DefinedTableIndex, ElemIndex, EngineOrModuleTypeIndex, EntityIndex, EntityType, FuncIndex,
10 FuncKey, GlobalIndex, IndexType, MemoryIndex, MemoryInitializer, ModuleInternedTypeIndex,
11 ModuleStartup, ModuleTypesBuilder, PanicOnOom as _, PassiveElemIndex, PrimaryMap,
12 RuntimeDataIndex, StaticModuleIndex, TableIndex, TableInitialValue, TableInitialization, Tag,
13 TagIndex, Trap, Tunables, TypeConvert, TypeIndex, WasmHeapTopType, WasmHeapType, WasmResult,
14 WasmValType, WasmparserTypeConverter,
15};
16use alloc::borrow::Cow;
17use cranelift_entity::packed_option::{PackedOption, ReservedValue};
18use cranelift_entity::{EntitySet, SecondaryMap};
19use std::collections::HashMap;
20use std::mem;
21use std::path::PathBuf;
22use std::sync::Arc;
23use wasmparser::{
24 CustomSectionReader, DataKind, ElementItems, ElementKind, Encoding, ExternalKind,
25 FuncToValidate, FunctionBody, KnownCustom, NameSectionReader, Naming, Parser, Payload, TypeRef,
26 Validator, ValidatorResources, types::Types,
27};
28
29/// Object containing the standalone environment information.
30pub struct ModuleEnvironment<'a, 'data> {
31 /// The current module being translated
32 result: ModuleTranslation<'data>,
33
34 /// Intern'd types for this entire translation, shared by all modules.
35 types: &'a mut ModuleTypesBuilder,
36
37 // Various bits and pieces of configuration
38 validator: &'a mut Validator,
39 tunables: &'a Tunables,
40}
41
42/// Identifies a FACT adapter-module import that the compiler lowers inline when
43/// translating the adapter function.
44#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
45pub enum FactInlineIntrinsic {
46 /// `enter-sync-call`: push a deferred component-model thread inline.
47 EnterSyncCall,
48
49 /// `exit-sync-call`: pop the deferred thread inline on the fast path, or
50 /// fall back to the out-of-line `exit-sync-call` libcall when the thread
51 /// was promoted.
52 ExitSyncCall,
53
54 /// `trap`: raise the given trap.
55 Trap(Trap),
56
57 /// An intrinsic that only exists for components.
58 #[cfg(feature = "component-model")]
59 Component(crate::component::FactComponentIntrinsic),
60}
61
62/// A statically-known function import.
63#[derive(Clone, Debug)]
64pub enum KnownFunc {
65 /// A function described by the given key.
66 FuncKey(FuncKey),
67 /// An always-inlined FACT intrinsic.
68 FactIntrinsic(FactInlineIntrinsic),
69}
70
71impl From<FuncKey> for KnownFunc {
72 fn from(key: FuncKey) -> Self {
73 Self::FuncKey(key)
74 }
75}
76
77impl From<FactInlineIntrinsic> for KnownFunc {
78 fn from(intrinsic: FactInlineIntrinsic) -> Self {
79 Self::FactIntrinsic(intrinsic)
80 }
81}
82
83/// A statically-known import of a core Wasm global, memory, or table.
84#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
85pub struct KnownEntity<T> {
86 /// The module that defines this entity.
87 pub module: StaticModuleIndex,
88 /// The entity's index in the defining module's defined-entity index space.
89 pub index: T,
90}
91
92/// A statically-known import of a core wasm global.
93#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
94pub enum KnownGlobal {
95 /// A global defined by a module in the same component.
96 Defined(KnownEntity<DefinedGlobalIndex>),
97
98 /// A component instance's runtime-managed flags, such as its `may-leave`
99 /// flag.
100 #[cfg(feature = "component-model")]
101 ComponentInstanceFlags(crate::component::RuntimeComponentInstanceIndex),
102}
103
104/// The result of translating via `ModuleEnvironment`.
105///
106/// Function bodies are not yet translated, and data initializers have not yet
107/// been copied out of the original buffer.
108pub struct ModuleTranslation<'data> {
109 /// Module information.
110 pub module: Module,
111
112 /// The input wasm binary.
113 ///
114 /// This can be useful, for example, when modules are parsed from a
115 /// component and the embedder wants access to the raw wasm modules
116 /// themselves.
117 pub wasm: &'data [u8],
118
119 /// The byte offset of this module's Wasm binary within the outer
120 /// binary (e.g. a component). For standalone modules this is 0.
121 /// This is used to convert component-relative source locations to
122 /// module-relative source locations.
123 pub wasm_module_offset: u64,
124
125 /// References to the function bodies.
126 pub function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
127
128 /// For each imported function, the single statically-known function that
129 /// always satisfies that import, if any.
130 ///
131 /// This is used to turn what would otherwise be indirect calls through the
132 /// imports table into direct calls, when possible.
133 ///
134 /// When filled in, this only ever contains
135 /// `FuncKey::DefinedWasmFunction(..)`s, `FuncKey::Intrinsic(..)`s, and
136 /// `FuncKey::FactInlineIntrinsic`s.
137 pub known_imported_functions: SecondaryMap<FuncIndex, Option<KnownFunc>>,
138
139 /// For each function import, the function import whose `vmctx` this one
140 /// is statically guaranteed to always share, when that is not itself.
141 ///
142 /// Function imports that always resolve to functions from the same
143 /// instance hold the same `vmctx` pointer in their `VMFunctionImport`
144 /// slots, so they can all load the callee `vmctx` from a single slot and
145 /// let GVN collapse what would otherwise be many identical loads into one.
146 ///
147 /// No value means this import must use its own slot.
148 ///
149 /// Only filled in for modules compiled as part of a component.
150 pub imported_func_vmctx_representative: SecondaryMap<FuncIndex, PackedOption<FuncIndex>>,
151
152 /// For each imported global, memory, or table, the single statically-known
153 /// defined entity that always satisfies that import, if any.
154 ///
155 /// This is used to access the entity via the defining module's precise
156 /// `AliasRegionKey::Defined{Global,Memory,Table}` region instead of the
157 /// conservative `AliasRegionKey::Public{Global,Memory,Table}` region that is
158 /// shared by every entity of that kind which crosses a module boundary.
159 ///
160 /// XXX: Being "known" requires more here than it does for functions: it is
161 /// not enough that *this* module's import is always the same entity,
162 /// *every* module that may import that entity must also always import that
163 /// same entity. Otherwise a function from one of those other modules, which
164 /// accesses the entity via the conservative region, could be inlined next
165 /// to one of our accesses via the precise region, and accessing the same
166 /// memory through two different alias regions is invalid.
167 ///
168 /// This extra condition is an artifact of this implementation, and how we
169 /// consume this data to choose the alias region for loads and stores to a
170 /// global/memory/table, not something inherent to knowing exactly which
171 /// entity satisfies a particular import. Really, there are two independent
172 /// axes here:
173 ///
174 /// 1. Is this import always satisfied by the same defined entity?
175 ///
176 /// 2. Is that entity's identity additionally known to *every* other module
177 /// that may import it?
178 ///
179 /// Only alias regions need (2), but other theoretical optimizations could
180 /// be perfectly happy with just (1). For example, if we know that an import
181 /// of an immutable global is always a particular defined global, then we
182 /// could inline that global's value at each `global.get` of the import,
183 /// regardless what any other module does or does not know about that
184 /// global.
185 ///
186 /// TODO(#14164): Actually record (1) and (2) in separate maps, enabling
187 /// optimizations that rely on just (1) but not (2), instead of folding them
188 /// into this same map.
189 pub known_imported_globals: SecondaryMap<GlobalIndex, Option<KnownGlobal>>,
190
191 /// Same as `known_imported_globals`, but for memories.
192 pub known_imported_memories: SecondaryMap<MemoryIndex, Option<KnownEntity<DefinedMemoryIndex>>>,
193
194 /// Same as `known_imported_globals`, but for tables.
195 pub known_imported_tables: SecondaryMap<TableIndex, Option<KnownEntity<DefinedTableIndex>>>,
196
197 /// For each global defined by this module, whether every module that may
198 /// import this global always imports exactly this global.
199 ///
200 /// When this holds, accesses of the global may use its precise
201 /// `AliasRegionKey::DefinedGlobal` region even when the global is exported,
202 /// because every module that can reach it agrees on that same region. This
203 /// is vacuously true of globals that nothing in the component imports.
204 ///
205 /// This can only be determined by looking at the whole component, so it is
206 /// always `false` for standalone modules.
207 pub globals_known_to_importers: EntitySet<DefinedGlobalIndex>,
208
209 /// Same as [`Self::globals_known_to_importers`], but for memories.
210 pub memories_known_to_importers: EntitySet<DefinedMemoryIndex>,
211
212 /// Same as [`Self::globals_known_to_importers`], but for tables.
213 pub tables_known_to_importers: EntitySet<DefinedTableIndex>,
214
215 /// A list of type signatures which are considered exported from this
216 /// module, or those that can possibly be called. This list is sorted, and
217 /// trampolines for each of these signatures are required.
218 pub exported_signatures: Vec<ModuleInternedTypeIndex>,
219
220 /// DWARF debug information, if enabled, parsed from the module.
221 pub debuginfo: DebugInfoData<'data>,
222
223 /// Set if debuginfo was found but it was not parsed due to `Tunables`
224 /// configuration.
225 pub has_unparsed_debuginfo: bool,
226
227 /// The desired alignment of `data` in the final data section of the object
228 /// file that we'll emit.
229 ///
230 /// Note that this is 1 by default but `MemoryInitialization::Static` might
231 /// switch this to a higher alignment to facilitate mmap-ing data from
232 /// an object file into a linear memory.
233 pub data_align: Option<u64>,
234
235 /// Map from a data segment to whether it's a passive data segment or not.
236 pub runtime_data_map: SecondaryMap<DataIndex, Option<RuntimeDataIndex>>,
237
238 /// Map from an elem segment to whether it's a passive elem segment or not.
239 pub passive_elem_map: SecondaryMap<ElemIndex, Option<PassiveElemIndex>>,
240
241 /// List of passive element segments found in this module which will get
242 /// concatenated for the final artifact.
243 pub runtime_data: PrimaryMap<RuntimeDataIndex, Cow<'data, [u8]>>,
244
245 /// Record of all passive data segments that this module contains.
246 ///
247 /// These are processed during [`ModuleTranslation::finalize_memory_init`]
248 /// and eventually moved over into the `runtime_data` list above. Until
249 /// then, however, their `RuntimeDataIndex` is not yet assigned.
250 passive_data: Vec<(DataIndex, &'data [u8])>,
251
252 /// When we're parsing the code section this will be incremented so we know
253 /// which function is currently being defined.
254 code_index: u32,
255
256 /// The type information of the current module made available at the end of the
257 /// validation process.
258 types: Option<Types>,
259
260 /// Per-function [`BranchHintReader`]s from the `metadata.code.branch_hint`
261 /// section, keyed by function index. Populated only when
262 /// [`Tunables::branch_hinting`] is enabled.
263 branch_hints: HashMap<FuncIndex, BranchHintReader<'data>>,
264
265 /// The WebAssembly `start` function, if defined.
266 pub start_func: Option<FuncIndex>,
267
268 /// Initializers for `global` values which aren't considered "simple".
269 ///
270 /// These initializers are later compiled into a "module startup" function.
271 pub global_initializers: Vec<(DefinedGlobalIndex, ConstExpr)>,
272
273 /// Definitions of all passive elements found within a module.
274 ///
275 /// This maps passive element segments to their definition, either functions
276 /// or expressions-basd.
277 pub passive_elements: PrimaryMap<PassiveElemIndex, TableSegmentElements>,
278
279 /// WebAssembly table initialization data, per table.
280 ///
281 /// This keeps track of all per-table initialization (e.g. initial value for
282 /// non-null tables) as well as active element segments. This is processed
283 /// and refined by [`ModuleTranslation::finalize_table_init`] after
284 /// translation.
285 pub table_initialization: TableInitialization,
286
287 /// WebAssembly memory initialization.
288 ///
289 /// This is held here in an `Unprocessed` form during translation, and then
290 /// this is later finished with [`ModuleTranslation::finalize_memory_init`].
291 pub memory_init: MemoryInit<'data>,
292}
293
294/// Different forms of memory initialization that happens for a module.
295pub enum MemoryInit<'a> {
296 /// Raw active data segments that are being applied for an instance.
297 ///
298 /// This list contains the raw data which hasn't yet been processed into
299 /// `RuntimeDataIndex`, for example. This is later processed during
300 /// [`ModuleTranslation::finalize_memory_init`] to optionally shuffle things
301 /// around.
302 Unprocessed(Vec<MemoryInitializer<'a>>),
303
304 /// Finalized memory initialization to be executed after
305 /// [`ModuleTranslation::finalize_memory_init`] has run. This represents
306 /// active data segments which may have been merged from the `Unprocessed`
307 /// list above, and may or may not have statically know offsets.
308 Processed(Vec<(MemoryIndex, MemorySegmentOffset, RuntimeDataIndex)>),
309}
310
311/// Offset within [`MemoryInit::Processed`] which indicates the initial offset
312/// a data segment is applied at.
313pub enum MemorySegmentOffset {
314 /// A "complicated" constant expression deferred to get evaluated at runtime
315 /// with compiled code.
316 Expr(ConstExpr),
317
318 /// A statically known, in-bounds, constant value.
319 Static(u64),
320}
321
322/// Lazy decoder over the branch hints attached to a single function in the
323/// `metadata.code.branch_hint` custom section
324/// ([branch-hinting proposal](https://github.com/WebAssembly/branch-hinting)).
325pub type BranchHintReader<'a> = wasmparser::SectionLimited<'a, wasmparser::BranchHint>;
326
327impl<'data> ModuleTranslation<'data> {
328 /// Create a new translation for the module with the given index.
329 pub fn new(module_index: StaticModuleIndex) -> Self {
330 Self {
331 module: Module::new(module_index),
332 wasm: &[],
333 wasm_module_offset: 0,
334 function_body_inputs: PrimaryMap::default(),
335 known_imported_functions: SecondaryMap::default(),
336 imported_func_vmctx_representative: SecondaryMap::default(),
337 known_imported_globals: SecondaryMap::default(),
338 known_imported_memories: SecondaryMap::default(),
339 known_imported_tables: SecondaryMap::default(),
340 globals_known_to_importers: EntitySet::new(),
341 memories_known_to_importers: EntitySet::new(),
342 tables_known_to_importers: EntitySet::new(),
343 exported_signatures: Vec::default(),
344 debuginfo: DebugInfoData::default(),
345 has_unparsed_debuginfo: false,
346 data_align: None,
347 runtime_data: Default::default(),
348 code_index: 0,
349 types: None,
350 runtime_data_map: Default::default(),
351 passive_elem_map: Default::default(),
352 branch_hints: HashMap::default(),
353 start_func: None,
354 global_initializers: Vec::new(),
355 passive_elements: Default::default(),
356 table_initialization: Default::default(),
357 memory_init: MemoryInit::Unprocessed(Vec::new()),
358 passive_data: Default::default(),
359 }
360 }
361
362 /// Returns the [`BranchHintReader`] for `func`, if the section attached any.
363 pub fn branch_hints(&self, func: FuncIndex) -> Option<BranchHintReader<'data>> {
364 self.branch_hints.get(&func).cloned()
365 }
366
367 /// Returns a reference to the type information of the current module.
368 pub fn get_types(&self) -> &Types {
369 self.types
370 .as_ref()
371 .expect("module type information to be available")
372 }
373
374 /// Get this translation's module's index.
375 pub fn module_index(&self) -> StaticModuleIndex {
376 self.module.module_index
377 }
378}
379
380/// Contains function data: byte code and its offset in the module.
381pub struct FunctionBodyData<'a> {
382 /// The body of the function, containing code and locals.
383 pub body: FunctionBody<'a>,
384 /// Validator for the function body
385 pub validator: FuncToValidate<ValidatorResources>,
386}
387
388#[derive(Debug, Default)]
389#[expect(missing_docs, reason = "self-describing fields")]
390pub struct DebugInfoData<'a> {
391 pub dwarf: Dwarf<'a>,
392 pub name_section: NameSection<'a>,
393 pub wasm_file: WasmFileInfo,
394 pub debug_loc: gimli::DebugLoc<Reader<'a>>,
395 pub debug_loclists: gimli::DebugLocLists<Reader<'a>>,
396 pub debug_ranges: gimli::DebugRanges<Reader<'a>>,
397 pub debug_rnglists: gimli::DebugRngLists<Reader<'a>>,
398 pub debug_cu_index: gimli::DebugCuIndex<Reader<'a>>,
399 pub debug_tu_index: gimli::DebugTuIndex<Reader<'a>>,
400}
401
402#[expect(missing_docs, reason = "self-describing")]
403pub type Dwarf<'input> = gimli::Dwarf<Reader<'input>>;
404
405type Reader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>;
406
407#[derive(Debug, Default)]
408#[expect(missing_docs, reason = "self-describing fields")]
409pub struct NameSection<'a> {
410 pub module_name: Option<&'a str>,
411 pub func_names: HashMap<FuncIndex, &'a str>,
412 pub locals_names: HashMap<FuncIndex, HashMap<u32, &'a str>>,
413}
414
415#[derive(Debug, Default)]
416#[expect(missing_docs, reason = "self-describing fields")]
417pub struct WasmFileInfo {
418 pub path: Option<PathBuf>,
419 pub code_section_offset: u64,
420 pub imported_func_count: u32,
421 pub funcs: Vec<FunctionMetadata>,
422}
423
424#[derive(Debug)]
425#[expect(missing_docs, reason = "self-describing fields")]
426pub struct FunctionMetadata {
427 pub params: Box<[WasmValType]>,
428 pub locals: Box<[(u32, WasmValType)]>,
429}
430
431impl<'a, 'data> ModuleEnvironment<'a, 'data> {
432 /// Allocates the environment data structures.
433 pub fn new(
434 tunables: &'a Tunables,
435 validator: &'a mut Validator,
436 types: &'a mut ModuleTypesBuilder,
437 module_index: StaticModuleIndex,
438 ) -> Self {
439 Self {
440 result: ModuleTranslation::new(module_index),
441 types,
442 tunables,
443 validator,
444 }
445 }
446
447 /// Translate a wasm module using this environment.
448 ///
449 /// This function will translate the `data` provided with `parser`,
450 /// validating everything along the way with this environment's validator.
451 ///
452 /// The result of translation, [`ModuleTranslation`], contains everything
453 /// necessary to compile functions afterwards as well as learn type
454 /// information about the module at runtime.
455 pub fn translate(
456 mut self,
457 parser: Parser,
458 data: &'data [u8],
459 ) -> Result<ModuleTranslation<'data>> {
460 self.result.wasm = data;
461 self.result.module.has_shadow_memories = self.tunables.memory_watchpoints;
462
463 for payload in parser.parse_all(data) {
464 self.translate_payload(payload?)?;
465 }
466
467 Ok(self.result)
468 }
469
470 fn translate_payload(&mut self, payload: Payload<'data>) -> Result<()> {
471 match payload {
472 Payload::Version {
473 num,
474 encoding,
475 range,
476 } => {
477 self.validator.version(num, encoding, &range)?;
478 match encoding {
479 Encoding::Module => {}
480 Encoding::Component => {
481 bail!("expected a WebAssembly module but was given a WebAssembly component")
482 }
483 }
484 }
485
486 Payload::End(offset) => {
487 self.result.types = Some(self.validator.end(offset)?);
488
489 // With the `escaped_funcs` set of functions finished
490 // we can calculate the set of signatures that are exported as
491 // the set of exported functions' signatures.
492 self.result.exported_signatures = self
493 .result
494 .module
495 .functions
496 .iter()
497 .filter_map(|(_, func)| {
498 if func.is_escaping() {
499 Some(func.signature.unwrap_module_type_index())
500 } else {
501 None
502 }
503 })
504 .collect();
505 self.result.exported_signatures.sort_unstable();
506 self.result.exported_signatures.dedup();
507 }
508
509 Payload::TypeSection(types) => {
510 self.validator.type_section(&types)?;
511
512 let count = self.validator.types(0).unwrap().core_type_count_in_module();
513 log::trace!("interning {count} Wasm types");
514
515 let capacity = usize::try_from(count).unwrap();
516 self.result.module.types.reserve(capacity)?;
517 self.types.reserve_wasm_signatures(capacity);
518
519 // Iterate over each *rec group* -- not type -- defined in the
520 // types section. Rec groups are the unit of canonicalization
521 // and therefore the unit at which we need to process at a
522 // time. `wasmparser` has already done the hard work of
523 // de-duplicating and canonicalizing the rec groups within the
524 // module for us, we just need to translate them into our data
525 // structures. Note that, if the Wasm defines duplicate rec
526 // groups, we need copy the duplicates over (shallowly) as well,
527 // so that our types index space doesn't have holes.
528 let mut type_index = 0;
529 while type_index < count {
530 let validator_types = self.validator.types(0).unwrap();
531
532 // Get the rec group for the current type index, which is
533 // always the first type defined in a rec group.
534 log::trace!("looking up wasmparser type for index {type_index}");
535 let core_type_id = validator_types.core_type_at_in_module(type_index);
536 log::trace!(
537 " --> {core_type_id:?} = {:?}",
538 validator_types[core_type_id],
539 );
540 let rec_group_id = validator_types.rec_group_id_of(core_type_id);
541 debug_assert_eq!(
542 validator_types
543 .rec_group_elements(rec_group_id)
544 .position(|id| id == core_type_id),
545 Some(0)
546 );
547
548 // Intern the rec group and then fill in this module's types
549 // index space.
550 let interned = self.types.intern_rec_group(validator_types, rec_group_id)?;
551 let elems = self.types.rec_group_elements(interned);
552 let len = elems.len();
553 self.result.module.types.reserve(len)?;
554 for ty in elems {
555 self.result.module.types.push(ty.into())?;
556 }
557
558 // Advance `type_index` to the start of the next rec group.
559 type_index += u32::try_from(len).unwrap();
560 }
561 }
562
563 Payload::ImportSection(imports) => {
564 self.validator.import_section(&imports)?;
565
566 let cnt = usize::try_from(imports.count()).unwrap();
567 self.result.module.initializers.reserve(cnt)?;
568
569 for entry in imports.into_imports() {
570 let import = entry?;
571 let ty = match import.ty {
572 TypeRef::Func(index) => {
573 let index = TypeIndex::from_u32(index);
574 let interned_index = self.result.module.types[index];
575 self.result.module.num_imported_funcs += 1;
576 self.result.debuginfo.wasm_file.imported_func_count += 1;
577 EntityType::Function(interned_index)
578 }
579 TypeRef::Memory(ty) => {
580 self.result.module.num_imported_memories += 1;
581 EntityType::Memory(ty.into())
582 }
583 TypeRef::Global(ty) => {
584 self.result.module.num_imported_globals += 1;
585 EntityType::Global(self.convert_global_type(&ty)?)
586 }
587 TypeRef::Table(ty) => {
588 self.result.module.num_imported_tables += 1;
589 EntityType::Table(self.convert_table_type(&ty)?)
590 }
591 TypeRef::Tag(ty) => {
592 let index = TypeIndex::from_u32(ty.func_type_idx);
593 let signature = self.result.module.types[index];
594 let exception = self.types.define_exception_type_for_tag(
595 signature.unwrap_module_type_index(),
596 );
597 let tag = Tag {
598 signature,
599 exception: EngineOrModuleTypeIndex::Module(exception),
600 };
601 self.result.module.num_imported_tags += 1;
602 EntityType::Tag(tag)
603 }
604 TypeRef::FuncExact(_) => {
605 bail!("custom-descriptors proposal not implemented yet");
606 }
607 };
608 self.declare_import(import.module, import.name, ty)?;
609 }
610 }
611
612 Payload::FunctionSection(functions) => {
613 self.validator.function_section(&functions)?;
614
615 let cnt = usize::try_from(functions.count()).unwrap();
616 self.result.module.functions.reserve_exact(cnt)?;
617
618 for entry in functions {
619 let sigindex = entry?;
620 let ty = TypeIndex::from_u32(sigindex);
621 let interned_index = self.result.module.types[ty];
622 self.result.module.push_function(interned_index);
623 }
624 }
625
626 Payload::TableSection(tables) => {
627 self.validator.table_section(&tables)?;
628 let cnt = usize::try_from(tables.count()).unwrap();
629 self.result.module.tables.reserve_exact(cnt)?;
630
631 for entry in tables {
632 let wasmparser::Table { ty, init } = entry?;
633 let table = self.convert_table_type(&ty)?;
634 self.result.module.needs_gc_heap |= table.ref_type.is_vmgcref_type();
635 self.result.module.tables.push(table)?;
636 let init = match init {
637 wasmparser::TableInit::RefNull => TableInitialValue::Null,
638 wasmparser::TableInit::Expr(expr) => {
639 let (init, escaped) = ConstExpr::from_wasmparser(self, expr)?;
640 for f in escaped {
641 self.flag_func_escaped(f);
642 }
643 TableInitialValue::Expr(init)
644 }
645 };
646 self.result.table_initialization.initial_values.push(init)?;
647 self.result
648 .module
649 .table_initialization
650 .push(Default::default())?;
651 }
652 }
653
654 Payload::MemorySection(memories) => {
655 self.validator.memory_section(&memories)?;
656
657 let cnt = usize::try_from(memories.count()).unwrap();
658 self.result.module.memories.reserve_exact(cnt)?;
659
660 for entry in memories {
661 let memory = entry?;
662 self.result.module.memories.push(memory.into())?;
663 }
664 }
665
666 Payload::TagSection(tags) => {
667 self.validator.tag_section(&tags)?;
668
669 for entry in tags {
670 let sigindex = entry?.func_type_idx;
671 let ty = TypeIndex::from_u32(sigindex);
672 let interned_index = self.result.module.types[ty];
673 let exception = self
674 .types
675 .define_exception_type_for_tag(interned_index.unwrap_module_type_index());
676 self.result.module.push_tag(interned_index, exception);
677 }
678 }
679
680 Payload::GlobalSection(globals) => {
681 self.validator.global_section(&globals)?;
682
683 let cnt = usize::try_from(globals.count()).unwrap();
684 self.result.module.globals.reserve_exact(cnt)?;
685
686 for entry in globals {
687 let wasmparser::Global { ty, init_expr } = entry?;
688 let (initializer, escaped) = ConstExpr::from_wasmparser(self, init_expr)?;
689 for f in escaped {
690 self.flag_func_escaped(f);
691 }
692 let ty = self.convert_global_type(&ty)?;
693 let index = self.result.module.globals.push(ty)?;
694 let defined_index = self.result.module.defined_global_index(index).unwrap();
695 match initializer.const_eval() {
696 Some(val) => {
697 self.result
698 .module
699 .global_initializers
700 .push((defined_index, val))?;
701 }
702 None => {
703 // "Complicated" global initializers are deferred
704 // to get evaluated in the startup function.
705 self.require_startup_func();
706 self.result
707 .global_initializers
708 .push((defined_index, initializer));
709 }
710 }
711 }
712 }
713
714 Payload::ExportSection(exports) => {
715 self.validator.export_section(&exports)?;
716
717 let cnt = usize::try_from(exports.count()).unwrap();
718 self.result.module.exports.reserve(cnt)?;
719
720 for entry in exports {
721 let wasmparser::Export { name, kind, index } = entry?;
722 let entity = match kind {
723 ExternalKind::Func | ExternalKind::FuncExact => {
724 let index = FuncIndex::from_u32(index);
725 self.flag_func_escaped(index);
726 EntityIndex::Function(index)
727 }
728 ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(index)),
729 ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(index)),
730 ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(index)),
731 ExternalKind::Tag => EntityIndex::Tag(TagIndex::from_u32(index)),
732 };
733 let name = self.result.module.strings.insert(name)?;
734 self.result.module.exports.insert(name, entity)?;
735 }
736 }
737
738 Payload::StartSection { func, range } => {
739 self.validator.start_section(func, &range)?;
740
741 let func_index = FuncIndex::from_u32(func);
742 debug_assert!(self.result.start_func.is_none());
743 self.result.start_func = Some(func_index);
744
745 // To make startup a bit easier, invoking the `start` function
746 // is a responsibility deferred to the startup function.
747 self.require_startup_func();
748 }
749
750 Payload::ElementSection(elements) => {
751 self.validator.element_section(&elements)?;
752
753 // Whether writes to tables made so far during instantiation
754 // could be observed outside of this instance, which becomes
755 // true once an active segment applies to an imported table.
756 let mut side_effects_visible = false;
757
758 for (index, entry) in elements.into_iter().enumerate() {
759 let wasmparser::Element {
760 kind,
761 items,
762 range: _,
763 } = entry?;
764
765 // Build up a list of `FuncIndex` corresponding to all the
766 // entries listed in this segment. Note that it's not
767 // possible to create anything other than a `ref.null
768 // extern` for externref segments, so those just get
769 // translated to the reserved value of `FuncIndex`.
770 let elements = match items {
771 ElementItems::Functions(funcs) => {
772 let mut elems =
773 Vec::with_capacity(usize::try_from(funcs.count()).unwrap());
774 for func in funcs {
775 let func = FuncIndex::from_u32(func?);
776 self.flag_func_escaped(func);
777 elems.push(func);
778 }
779 TableSegmentElements::Functions(elems.into())
780 }
781 ElementItems::Expressions(ty, items) => {
782 let ty = self.convert_ref_type(ty)?;
783 let mut exprs =
784 Vec::with_capacity(usize::try_from(items.count()).unwrap());
785 for expr in items {
786 let (expr, escaped) = ConstExpr::from_wasmparser(self, expr?)?;
787 exprs.push(expr);
788 for func in escaped {
789 self.flag_func_escaped(func);
790 }
791 }
792 TableSegmentElements::Expressions {
793 ty,
794 exprs: exprs.into(),
795 }
796 }
797 };
798
799 let passive_index = match kind {
800 ElementKind::Active {
801 table_index,
802 offset_expr,
803 } => {
804 let table_index = TableIndex::from_u32(table_index.unwrap_or(0));
805 let (offset, escaped) = ConstExpr::from_wasmparser(self, offset_expr)?;
806 debug_assert!(escaped.is_empty());
807
808 // The spec's instantiation evaluates the
809 // expressions of all element segments before
810 // executing any active segment's `table.init`
811 // (https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation).
812 // So if any element expression may trap, and an
813 // earlier write (including from this segment)
814 // could be observed, then stage its evaluation
815 // into a passive element segment, which is
816 // evaluated before any active segment is applied.
817 // Note that this passive segment isn't registered
818 // in `passive_elem_map` so it's not reachable from
819 // wasm itself.
820 //
821 // If no imported table has been written yet then a
822 // trap is unobservable, so evaluating in place is
823 // fine and avoids the extra copy.
824 side_effects_visible |=
825 self.result.module.is_imported_table(table_index);
826 let init = if side_effects_visible && elements_may_trap(&elements) {
827 let len = elements.len();
828 let index = self
829 .result
830 .module
831 .passive_elements
832 .push((elements.ty(), len))?;
833 self.result.passive_elements.push(elements);
834 self.require_startup_func();
835 TableSegmentInit::Staged { index, len }
836 } else {
837 TableSegmentInit::Direct(elements)
838 };
839
840 self.result
841 .table_initialization
842 .segments
843 .push(TableSegment {
844 table_index,
845 offset,
846 init,
847 })?;
848 None
849 }
850
851 ElementKind::Passive => {
852 let passive_index = self
853 .result
854 .module
855 .passive_elements
856 .push((elements.ty(), elements.len()))?;
857 self.result.passive_elements.push(elements);
858 // One-time initialization of passive element
859 // segments is deferred to the startup function.
860 self.require_startup_func();
861 Some(passive_index)
862 }
863
864 ElementKind::Declared => None,
865 };
866 let elem_index = ElemIndex::from_u32(index as u32);
867 self.result
868 .passive_elem_map
869 .insert(elem_index, passive_index);
870 }
871 }
872
873 Payload::CodeSectionStart {
874 count,
875 unchecked_range,
876 ..
877 } => {
878 self.validator.code_section_start(&unchecked_range)?;
879 let cnt = usize::try_from(count).unwrap();
880 self.result.function_body_inputs.reserve_exact(cnt);
881 self.result.debuginfo.wasm_file.code_section_offset = unchecked_range.start;
882 }
883
884 Payload::CodeSectionEntry(body) => {
885 let validator = self.validator.code_section_entry(&body)?;
886 let func_index =
887 self.result.code_index + self.result.module.num_imported_funcs as u32;
888 let func_index = FuncIndex::from_u32(func_index);
889
890 if self.tunables.debug_native {
891 let sig_index = self.result.module.functions[func_index]
892 .signature
893 .unwrap_module_type_index();
894 let sig = self.types[sig_index].unwrap_func();
895 let mut locals = Vec::new();
896 for pair in body.get_locals_reader()? {
897 let (cnt, ty) = pair?;
898 let ty = self.convert_valtype(ty)?;
899 locals.push((cnt, ty));
900 }
901 self.result
902 .debuginfo
903 .wasm_file
904 .funcs
905 .push(FunctionMetadata {
906 locals: locals.into_boxed_slice(),
907 params: sig.params().into(),
908 });
909 }
910 if self.tunables.debug_guest {
911 // All functions are potentially reachable and
912 // callable by the guest debugger, so they must
913 // all be flagged as escaping.
914 self.flag_func_escaped(func_index);
915 }
916 self.result
917 .function_body_inputs
918 .push(FunctionBodyData { validator, body });
919 self.result.code_index += 1;
920 }
921
922 Payload::DataSection(data) => {
923 self.validator.data_section(&data)?;
924
925 assert!(self.result.module.memory_initialization.is_segmented());
926
927 for (index, entry) in data.into_iter().enumerate() {
928 let wasmparser::Data {
929 kind,
930 data,
931 range: _,
932 } = entry?;
933 let data_index = DataIndex::from_u32(index.try_into().unwrap());
934 match kind {
935 DataKind::Active {
936 memory_index,
937 offset_expr,
938 } => {
939 let memory_index = MemoryIndex::from_u32(memory_index);
940 let (offset, escaped) = ConstExpr::from_wasmparser(self, offset_expr)?;
941 debug_assert!(escaped.is_empty());
942
943 let MemoryInit::Unprocessed(list) = &mut self.result.memory_init else {
944 panic!("memory initializers should be unprocessed at this point");
945 };
946 list.push(MemoryInitializer {
947 memory_index,
948 offset,
949 data,
950 });
951 }
952 DataKind::Passive => {
953 self.result.passive_data.push((data_index, data));
954 }
955 }
956 }
957 }
958
959 Payload::DataCountSection { count, range } => {
960 self.validator.data_count_section(count, &range)?;
961
962 // Note: the count passed in here is the *total* segment count
963 // There is no way to reserve for just the passive segments as
964 // they are discovered when iterating the data section entries
965 // Given that the total segment count might be much larger than
966 // the passive count, do not reserve anything here.
967 }
968
969 Payload::CustomSection(s)
970 if s.name() == "webidl-bindings" || s.name() == "wasm-interface-types" =>
971 {
972 bail!(
973 "\
974Support for interface types has temporarily been removed from `wasmtime`.
975
976For more information about this temporary change you can read on the issue online:
977
978 https://github.com/bytecodealliance/wasmtime/issues/1271
979
980and for re-adding support for interface types you can see this issue:
981
982 https://github.com/bytecodealliance/wasmtime/issues/677
983"
984 )
985 }
986
987 Payload::CustomSection(s) => {
988 self.register_custom_section(&s);
989 }
990
991 // It's expected that validation will probably reject other
992 // payloads such as `UnknownSection` or those related to the
993 // component model. If, however, something gets past validation then
994 // that's a bug in Wasmtime as we forgot to implement something.
995 other => {
996 self.validator.payload(&other)?;
997 panic!("unimplemented section in wasm file {other:?}");
998 }
999 }
1000 Ok(())
1001 }
1002
1003 fn register_custom_section(&mut self, section: &CustomSectionReader<'data>) {
1004 match section.as_known() {
1005 KnownCustom::Name(name) => {
1006 let result = self.name_section(name);
1007 if let Err(e) = result {
1008 log::warn!("failed to parse name section {e:?}");
1009 }
1010 }
1011 KnownCustom::BranchHints(reader) if self.tunables.branch_hinting => {
1012 // Branch hints are advisory and this section is never validated;
1013 // it is decoded lazily during compilation, so record only the
1014 // per-function sub-readers here. Discard the whole section if any
1015 // entry is malformed rather than applying it partially.
1016 let mut hints = HashMap::new();
1017 let result: wasmparser::Result<()> = reader.into_iter().try_for_each(|func| {
1018 let func = func?;
1019 // A well-formed section lists each function at most once; keep
1020 // the first entry deterministically if it repeats.
1021 hints
1022 .entry(FuncIndex::from_u32(func.func))
1023 .or_insert(func.hints);
1024 Ok(())
1025 });
1026 match result {
1027 Ok(()) => self.result.branch_hints = hints,
1028 Err(e) => log::warn!("failed to parse branch-hint section {e:?}"),
1029 }
1030 }
1031 _ => {
1032 let name = section.name().trim_end_matches(".dwo");
1033 if name.starts_with(".debug_") {
1034 self.dwarf_section(name, section);
1035 }
1036 }
1037 }
1038 }
1039
1040 fn dwarf_section(&mut self, name: &str, section: &CustomSectionReader<'data>) {
1041 if !self.tunables.debug_native && !self.tunables.parse_wasm_debuginfo {
1042 self.result.has_unparsed_debuginfo = true;
1043 return;
1044 }
1045 let info = &mut self.result.debuginfo;
1046 let dwarf = &mut info.dwarf;
1047 let endian = gimli::LittleEndian;
1048 let data = section.data();
1049 let slice = gimli::EndianSlice::new(data, endian);
1050
1051 match name {
1052 // `gimli::Dwarf` fields.
1053 ".debug_abbrev" => dwarf.debug_abbrev = gimli::DebugAbbrev::new(data, endian),
1054 ".debug_addr" => dwarf.debug_addr = gimli::DebugAddr::from(slice),
1055 ".debug_info" => {
1056 dwarf.debug_info = gimli::DebugInfo::new(data, endian);
1057 }
1058 ".debug_line" => dwarf.debug_line = gimli::DebugLine::new(data, endian),
1059 ".debug_line_str" => dwarf.debug_line_str = gimli::DebugLineStr::from(slice),
1060 ".debug_str" => dwarf.debug_str = gimli::DebugStr::new(data, endian),
1061 ".debug_str_offsets" => dwarf.debug_str_offsets = gimli::DebugStrOffsets::from(slice),
1062 ".debug_str_sup" => {
1063 let mut dwarf_sup: Dwarf<'data> = Default::default();
1064 dwarf_sup.debug_str = gimli::DebugStr::from(slice);
1065 dwarf.sup = Some(Arc::new(dwarf_sup));
1066 }
1067 ".debug_types" => dwarf.debug_types = gimli::DebugTypes::from(slice),
1068
1069 // Additional fields.
1070 ".debug_loc" => info.debug_loc = gimli::DebugLoc::from(slice),
1071 ".debug_loclists" => info.debug_loclists = gimli::DebugLocLists::from(slice),
1072 ".debug_ranges" => info.debug_ranges = gimli::DebugRanges::new(data, endian),
1073 ".debug_rnglists" => info.debug_rnglists = gimli::DebugRngLists::new(data, endian),
1074
1075 // DWARF package fields
1076 ".debug_cu_index" => info.debug_cu_index = gimli::DebugCuIndex::new(data, endian),
1077 ".debug_tu_index" => info.debug_tu_index = gimli::DebugTuIndex::new(data, endian),
1078
1079 // We don't use these at the moment.
1080 ".debug_aranges" | ".debug_pubnames" | ".debug_pubtypes" => return,
1081 other => {
1082 log::warn!("unknown debug section `{other}`");
1083 return;
1084 }
1085 }
1086
1087 dwarf.ranges = gimli::RangeLists::new(info.debug_ranges, info.debug_rnglists);
1088 dwarf.locations = gimli::LocationLists::new(info.debug_loc, info.debug_loclists);
1089 }
1090
1091 /// Declares a new import with the `module` and `field` names, importing the
1092 /// `ty` specified.
1093 ///
1094 /// Note that this method is somewhat tricky due to the implementation of
1095 /// the module linking proposal. In the module linking proposal two-level
1096 /// imports are recast as single-level imports of instances. That recasting
1097 /// happens here by recording an import of an instance for the first time
1098 /// we see a two-level import.
1099 ///
1100 /// When the module linking proposal is disabled, however, disregard this
1101 /// logic and instead work directly with two-level imports since no
1102 /// instances are defined.
1103 fn declare_import(
1104 &mut self,
1105 module: &'data str,
1106 field: &'data str,
1107 ty: EntityType,
1108 ) -> Result<(), OutOfMemory> {
1109 let index = self.push_type(ty);
1110 self.result.module.initializers.push(Initializer::Import {
1111 name: self.result.module.strings.insert(module)?,
1112 field: self.result.module.strings.insert(field)?,
1113 index,
1114 })?;
1115 Ok(())
1116 }
1117
1118 fn push_type(&mut self, ty: EntityType) -> EntityIndex {
1119 match ty {
1120 EntityType::Function(ty) => EntityIndex::Function({
1121 let func_index = self
1122 .result
1123 .module
1124 .push_function(ty.unwrap_module_type_index());
1125 // Imported functions can escape; in fact, they've already done
1126 // so to get here.
1127 self.flag_func_escaped(func_index);
1128 func_index
1129 }),
1130 EntityType::Table(ty) => {
1131 EntityIndex::Table(self.result.module.tables.push(ty).panic_on_oom())
1132 }
1133 EntityType::Memory(ty) => {
1134 EntityIndex::Memory(self.result.module.memories.push(ty).panic_on_oom())
1135 }
1136 EntityType::Global(ty) => {
1137 EntityIndex::Global(self.result.module.globals.push(ty).panic_on_oom())
1138 }
1139 EntityType::Tag(ty) => {
1140 EntityIndex::Tag(self.result.module.tags.push(ty).panic_on_oom())
1141 }
1142 }
1143 }
1144
1145 fn flag_func_escaped(&mut self, func: FuncIndex) {
1146 let ty = &mut self.result.module.functions[func];
1147 // If this was already assigned a funcref index no need to re-assign it.
1148 if ty.is_escaping() {
1149 return;
1150 }
1151 let index = self.result.module.num_escaped_funcs as u32;
1152 ty.func_ref = FuncRefIndex::from_u32(index);
1153 self.result.module.num_escaped_funcs += 1;
1154 }
1155
1156 /// Parses the Name section of the wasm module.
1157 fn name_section(&mut self, names: NameSectionReader<'data>) -> WasmResult<()> {
1158 for subsection in names {
1159 match subsection? {
1160 wasmparser::Name::Function(names) => {
1161 for name in names {
1162 let Naming { index, name } = name?;
1163 // Skip this naming if it's naming a function that
1164 // doesn't actually exist.
1165 if (index as usize) >= self.result.module.functions.len() {
1166 continue;
1167 }
1168
1169 // Store the name unconditionally, regardless of
1170 // whether we're parsing debuginfo, since function
1171 // names are almost always present in the
1172 // final compilation artifact.
1173 let index = FuncIndex::from_u32(index);
1174 self.result
1175 .debuginfo
1176 .name_section
1177 .func_names
1178 .insert(index, name);
1179 }
1180 }
1181 wasmparser::Name::Module { name, .. } => {
1182 self.result.module.name =
1183 Some(self.result.module.strings.insert(name).panic_on_oom());
1184 if self.tunables.debug_native {
1185 self.result.debuginfo.name_section.module_name = Some(name);
1186 }
1187 }
1188 wasmparser::Name::Local(reader) => {
1189 if !self.tunables.debug_native {
1190 continue;
1191 }
1192 for f in reader {
1193 let f = f?;
1194 // Skip this naming if it's naming a function that
1195 // doesn't actually exist.
1196 if (f.index as usize) >= self.result.module.functions.len() {
1197 continue;
1198 }
1199 for name in f.names {
1200 let Naming { index, name } = name?;
1201
1202 self.result
1203 .debuginfo
1204 .name_section
1205 .locals_names
1206 .entry(FuncIndex::from_u32(f.index))
1207 .or_insert(HashMap::new())
1208 .insert(index, name);
1209 }
1210 }
1211 }
1212 wasmparser::Name::Label(_)
1213 | wasmparser::Name::Type(_)
1214 | wasmparser::Name::Table(_)
1215 | wasmparser::Name::Global(_)
1216 | wasmparser::Name::Memory(_)
1217 | wasmparser::Name::Element(_)
1218 | wasmparser::Name::Data(_)
1219 | wasmparser::Name::Tag(_)
1220 | wasmparser::Name::Field(_)
1221 | wasmparser::Name::Parameter(_)
1222 | wasmparser::Name::TagParameter(_)
1223 | wasmparser::Name::Unknown { .. } => {}
1224 }
1225 }
1226 Ok(())
1227 }
1228
1229 fn require_startup_func(&mut self) {
1230 self.result.require_startup_func(self.types);
1231 }
1232}
1233
1234impl TypeConvert for ModuleEnvironment<'_, '_> {
1235 fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType {
1236 WasmparserTypeConverter::new(&self.types, |idx| {
1237 self.result.module.types[idx].unwrap_module_type_index()
1238 })
1239 .lookup_heap_type(index)
1240 }
1241
1242 fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
1243 WasmparserTypeConverter::new(&self.types, |idx| {
1244 self.result.module.types[idx].unwrap_module_type_index()
1245 })
1246 .lookup_type_index(index)
1247 }
1248}
1249
1250impl ModuleTranslation<'_> {
1251 /// Called after translation is complete this will finalize the memory
1252 /// initialization strategy for this module.
1253 ///
1254 /// This will notably use `Self::try_static_init` to attempt to massage
1255 /// data segments to being CoW-init-friendly. Afterwards the
1256 /// `self.memory_init` field is transitioned from `Unprocessed` to
1257 /// `Processed`.
1258 pub fn finalize_memory_init(
1259 &mut self,
1260 tunables: &Tunables,
1261 page_size: u64,
1262 max_image_size_always_allowed: u64,
1263 types: &mut ModuleTypesBuilder,
1264 ) {
1265 // Memory images are installed before the module's startup function
1266 // runs. If a table initializer can trap, using a memory image would
1267 // initialize memory even though the startup function should trap
1268 // before reaching the memory initializers.
1269 if tunables.memory_init_cow && !self.table_initialization_may_trap() {
1270 self.try_static_init(page_size, max_image_size_always_allowed);
1271 }
1272
1273 // If any memory is statically initialized, and if that memory has an
1274 // initial data segment, then a startup function is at least
1275 // conditionally needed if the memory needs initialization. Flag as such
1276 // here.
1277 if let MemoryInitialization::Static { map } = &self.module.memory_initialization {
1278 if map.iter().any(|(_, v)| v.is_some()) {
1279 self.require_startup_func_if_memories_need_init(types);
1280 }
1281 }
1282
1283 // If, after `try_static_init`, initializers are still `Unprocessed`
1284 // then this is the catch-all fallback path for initialization. All
1285 // segments are promoted into `self.runtime_data` and then the
1286 // initialization is rewritten to `Processed`.
1287 if let MemoryInit::Unprocessed(list) = &mut self.memory_init {
1288 let segments = mem::take(list);
1289 let mut new_initializers = Vec::new();
1290 for segment in segments {
1291 new_initializers.push((
1292 segment.memory_index,
1293 MemorySegmentOffset::Expr(segment.offset),
1294 self.runtime_data.push(segment.data.into()),
1295 ));
1296 }
1297 if !new_initializers.is_empty() {
1298 self.require_startup_func(types);
1299 }
1300 self.memory_init = MemoryInit::Processed(new_initializers);
1301 }
1302
1303 // At this point append all passive data to the `runtime_data` list.
1304 // This notably occurs after `try_static_init` above to ensure that the
1305 // page-aligned data for static initialization, if applicable, comes
1306 // first.
1307 for (data_index, segment) in self.passive_data.iter() {
1308 let runtime_index = self.runtime_data.push((*segment).into());
1309 self.runtime_data_map
1310 .insert(*data_index, Some(runtime_index));
1311 }
1312
1313 // And, finally, record all chunks from `self.runtime_data` within
1314 // `self.module.runtime_data` as well.
1315 let mut cur = 0;
1316 for (idx, data) in self.runtime_data.iter() {
1317 let len = u32::try_from(data.len()).unwrap();
1318 let i = self.module.runtime_data.push(cur..cur + len).panic_on_oom();
1319 cur += len;
1320 assert_eq!(idx, i);
1321 }
1322 }
1323
1324 /// Returns whether an active table initializer may trap during startup.
1325 ///
1326 /// Memory images are installed while allocating an instance, before the
1327 /// startup function executes. Therefore a memory image cannot be used when
1328 /// a table initializer might trap: doing so would make the memory
1329 /// observable even though execution should stop before reaching the memory
1330 /// initializers.
1331 fn table_initialization_may_trap(&self) -> bool {
1332 self.table_initialization.segments.iter().any(|segment| {
1333 let offset = match segment.offset.ops() {
1334 &[ConstOp::I32Const(offset)] => u64::from(offset.cast_unsigned()),
1335 &[ConstOp::I64Const(offset)] => offset.cast_unsigned(),
1336 // A dynamic offset cannot be proven to be in bounds here.
1337 _ => return true,
1338 };
1339 let top = match offset.checked_add(segment.init.len()) {
1340 Some(top) => top,
1341 None => return true,
1342 };
1343 if top > self.module.tables[segment.table_index].limits.min {
1344 return true;
1345 }
1346 match &segment.init {
1347 TableSegmentInit::Direct(elements) => elements_may_trap(elements),
1348 TableSegmentInit::Staged { .. } => true,
1349 }
1350 })
1351 }
1352
1353 /// Attempts to convert segmented memory initialization into static
1354 /// initialization for the module that this translation represents.
1355 ///
1356 /// If this module's memory initialization is not compatible with paged
1357 /// initialization then this won't change anything. Otherwise if it is
1358 /// compatible then the `memory_initialization` field will be updated.
1359 ///
1360 /// Takes a `page_size` argument in order to ensure that all
1361 /// initialization is page-aligned for mmap-ability, and
1362 /// `max_image_size_always_allowed` to control how we decide
1363 /// whether to use static init.
1364 ///
1365 /// We will try to avoid generating very sparse images, which are
1366 /// possible if e.g. a module has an initializer at offset 0 and a
1367 /// very high offset (say, 1 GiB). To avoid this, we use a dual
1368 /// condition: we always allow images less than
1369 /// `max_image_size_always_allowed`, and the embedder of Wasmtime
1370 /// can set this if desired to ensure that static init should
1371 /// always be done if the size of the module or its heaps is
1372 /// otherwise bounded by the system. We also allow images with
1373 /// static init data bigger than that, but only if it is "dense",
1374 /// defined as having at least half (50%) of its pages with some
1375 /// data.
1376 ///
1377 /// We could do something slightly better by building a dense part
1378 /// and keeping a sparse list of outlier/leftover segments (see
1379 /// issue #3820). This would also allow mostly-static init of
1380 /// modules that have some dynamically-placed data segments. But,
1381 /// for now, this is sufficient to allow a system that "knows what
1382 /// it's doing" to always get static init.
1383 fn try_static_init(&mut self, page_size: u64, max_image_size_always_allowed: u64) {
1384 let segments = match &mut self.memory_init {
1385 MemoryInit::Unprocessed(list) => list,
1386 _ => return,
1387 };
1388
1389 // First a dry run of memory initialization is performed. This
1390 // collects information about the extent of memory initialized for each
1391 // memory as well as the size of all data segments being copied in.
1392 struct Memory<'a> {
1393 data_size: u64,
1394 min_addr: u64,
1395 max_addr: u64,
1396 segments: Vec<(u64, &'a [u8])>,
1397 }
1398 let mut info = PrimaryMap::with_capacity(self.module.memories.len());
1399 for _ in 0..self.module.memories.len() {
1400 info.push(Memory {
1401 data_size: 0,
1402 min_addr: u64::MAX,
1403 max_addr: 0,
1404 segments: Vec::new(),
1405 });
1406 }
1407
1408 for initializer in segments.iter() {
1409 let &MemoryInitializer {
1410 memory_index,
1411 ref offset,
1412 ref data,
1413 } = initializer;
1414
1415 // Currently `Static` only applies to locally-defined memories,
1416 // so if a data segment references an imported memory then
1417 // transitioning to a `Static` memory initializer is not
1418 // possible.
1419 if self.module.defined_memory_index(memory_index).is_none() {
1420 return;
1421 }
1422
1423 // First up determine the start/end range and verify that they're
1424 // in-bounds for the initial size of the memory at `memory_index`.
1425 // Note that this can bail if we don't have access to globals yet
1426 // (e.g. this is a task happening before instantiation at
1427 // compile-time).
1428 let start = match (offset.ops(), self.module.memories[memory_index].idx_type) {
1429 (&[ConstOp::I32Const(offset)], IndexType::I32) => offset.cast_unsigned().into(),
1430 (&[ConstOp::I64Const(offset)], IndexType::I64) => offset.cast_unsigned(),
1431 _ => return,
1432 };
1433 let len = u64::try_from(data.len()).unwrap();
1434 let end = match start.checked_add(len) {
1435 Some(end) => end,
1436 None => return,
1437 };
1438
1439 match self.module.memories[memory_index].minimum_byte_size() {
1440 Ok(max) => {
1441 if end > max {
1442 return;
1443 }
1444 }
1445
1446 // Note that computing the minimum can overflow if the page
1447 // size is the default 64KiB and the memory's minimum size in
1448 // pages is `1 << 48`, the maximum number of minimum pages for
1449 // 64-bit memories. We don't return `false` to signal an error
1450 // here and instead defer the error to runtime, when it will be
1451 // impossible to allocate that much memory anyways.
1452 Err(_) => return,
1453 }
1454
1455 // Skip empty in-bounds data segments.
1456 if data.is_empty() {
1457 continue;
1458 }
1459
1460 let info = &mut info[memory_index];
1461 let len64 = u64::try_from(data.len()).unwrap();
1462 info.data_size += len64;
1463 info.min_addr = info.min_addr.min(start);
1464 info.max_addr = info.max_addr.max(start + len64);
1465 info.segments.push((start, data));
1466 }
1467
1468 // Validate that the memory information collected is indeed valid for
1469 // static memory initialization.
1470 for (i, info) in info.iter().filter(|(_, info)| info.data_size > 0) {
1471 let image_size = info.max_addr - info.min_addr;
1472
1473 // Simplify things for now by bailing out entirely if any memory has
1474 // a page size smaller than the host's page size. This fixes a case
1475 // where currently initializers are created in host-page-size units
1476 // of length which means that a larger-than-the-entire-memory
1477 // initializer can be created. This can be handled technically but
1478 // would require some more changes to help fix the assert elsewhere
1479 // that this protects against.
1480 if self.module.memories[i].page_size() < page_size {
1481 return;
1482 }
1483
1484 // If the range of memory being initialized is less than twice the
1485 // total size of the data itself then it's assumed that static
1486 // initialization is ok. This means we'll at most double memory
1487 // consumption during the memory image creation process, which is
1488 // currently assumed to "probably be ok" but this will likely need
1489 // tweaks over time.
1490 if image_size < info.data_size.saturating_mul(2) {
1491 continue;
1492 }
1493
1494 // If the memory initialization image is larger than the size of all
1495 // data, then we still allow memory initialization if the image will
1496 // be of a relatively modest size, such as 1MB here.
1497 if image_size < max_image_size_always_allowed {
1498 continue;
1499 }
1500
1501 // At this point memory initialization is concluded to be too
1502 // expensive to do at compile time so it's entirely deferred to
1503 // happen at runtime.
1504 return;
1505 }
1506
1507 // Here's where we've now committed to changing to static memory. The
1508 // memory initialization image is built here from the page data and then
1509 // it's converted to a single initializer.
1510 let mut map = TryPrimaryMap::with_capacity(info.len()).panic_on_oom();
1511 let mut new_initializers = Vec::new();
1512 for (memory, info) in info.iter() {
1513 // Create the in-memory `image` which is the initialized contents of
1514 // this linear memory.
1515 let extent = if info.segments.len() > 0 {
1516 (info.max_addr - info.min_addr) as usize
1517 } else {
1518 0
1519 };
1520 let mut image = Vec::with_capacity(extent);
1521 for (offset, data) in info.segments.iter() {
1522 let offset = usize::try_from(*offset - info.min_addr).unwrap();
1523 if image.len() < offset {
1524 image.resize(offset, 0u8);
1525 image.extend_from_slice(data);
1526 } else {
1527 image.splice(
1528 offset..(offset + data.len()).min(image.len()),
1529 data.iter().copied(),
1530 );
1531 }
1532 }
1533 assert_eq!(image.len(), extent);
1534 assert_eq!(image.capacity(), extent);
1535 let mut offset = if info.segments.len() > 0 {
1536 info.min_addr
1537 } else {
1538 0
1539 };
1540
1541 // Chop off trailing zeros from the image as memory is already
1542 // zero-initialized. Note that `i` is the position of a nonzero
1543 // entry here, so to not lose it we truncate to `i + 1`.
1544 if let Some(i) = image.iter().rposition(|i| *i != 0) {
1545 image.truncate(i + 1);
1546 }
1547
1548 // Also chop off leading zeros, if any.
1549 if let Some(i) = image.iter().position(|i| *i != 0) {
1550 offset += i as u64;
1551 image.drain(..i);
1552 }
1553 let mut len = u64::try_from(image.len()).unwrap();
1554
1555 // The goal is to enable mapping this image directly into memory, so
1556 // the offset into linear memory must be a multiple of the page
1557 // size. If that's not already the case then the image is padded at
1558 // the front and back with extra zeros as necessary
1559 if offset % page_size != 0 {
1560 let zero_padding = offset % page_size;
1561 image.splice(0..0, std::iter::repeat(0).take(zero_padding as usize));
1562 offset -= zero_padding;
1563 len += zero_padding;
1564 }
1565 if len % page_size != 0 {
1566 let zero_padding = page_size - (len % page_size);
1567 image.extend(std::iter::repeat(0).take(zero_padding as usize));
1568 len += zero_padding;
1569 }
1570 let runtime_index = if image.is_empty() {
1571 None
1572 } else {
1573 Some(self.runtime_data.push(image.into()))
1574 };
1575
1576 // Offset/length should now always be page-aligned.
1577 assert!(offset % page_size == 0);
1578 assert!(len % page_size == 0);
1579
1580 // Record the static memory initializer which describes this image,
1581 // only needed if the image is actually present and has a nonzero
1582 // length. The `offset` has been calculates above, originally
1583 // sourced from `info.min_addr`. The `data` field is the extent
1584 // within the final data segment we'll emit to an ELF image, which
1585 // is the concatenation of `self.data`, so here it's the size of
1586 // the section-so-far plus the current segment we're appending.
1587 let idx = map.push(runtime_index.map(|i| (offset, i))).panic_on_oom();
1588 assert_eq!(idx, memory);
1589 if let Some(runtime_index) = runtime_index {
1590 new_initializers.push((idx, MemorySegmentOffset::Static(offset), runtime_index));
1591 }
1592 }
1593 self.data_align = Some(page_size);
1594 self.module.memory_initialization = MemoryInitialization::Static { map };
1595 self.memory_init = MemoryInit::Processed(new_initializers);
1596 }
1597
1598 /// Finalizes the initialization of tables.
1599 ///
1600 /// This is invoked after translation and notably uses
1601 /// `Self::try_func_table_init` to attempt to optimize initialization of
1602 /// tables into static precomputed images.
1603 pub fn finalize_table_init(&mut self, tunables: &Tunables, types: &mut ModuleTypesBuilder) {
1604 if tunables.table_lazy_init {
1605 self.try_func_table_init();
1606 }
1607
1608 // If any table has a non-null initializers, or if there's any active
1609 // data segments, then a startup function is unconditionally required to
1610 // configure the table.
1611 if self
1612 .table_initialization
1613 .initial_values
1614 .iter()
1615 .any(|(_, v)| !matches!(v, TableInitialValue::Null))
1616 || !self.table_initialization.segments.is_empty()
1617 {
1618 self.require_startup_func(types);
1619 }
1620 }
1621
1622 /// Attempts to convert the module's table initializers to
1623 /// FuncTable form where possible. This enables lazy table
1624 /// initialization later by providing a one-to-one map of initial
1625 /// table values, without having to parse all segments.
1626 fn try_func_table_init(&mut self) {
1627 // This should be large enough to support very large Wasm
1628 // modules with huge funcref tables, but small enough to avoid
1629 // OOMs or DoS on truly sparse tables.
1630 const MAX_FUNC_TABLE_SIZE: u64 = 1024 * 1024;
1631
1632 // First convert any element-initialized tables to images of just that
1633 // single function if the minimum size of the table allows doing so.
1634 for ((i, init), (_, table)) in self.table_initialization.initial_values.iter_mut().zip(
1635 self.module
1636 .tables
1637 .iter()
1638 .skip(self.module.num_imported_tables),
1639 ) {
1640 let table_size = table.limits.min;
1641 if table_size > MAX_FUNC_TABLE_SIZE {
1642 continue;
1643 }
1644 if let TableInitialValue::Expr(expr) = init {
1645 if let [ConstOp::RefFunc(f)] = expr.ops() {
1646 assert!(self.module.table_initialization[i].is_empty());
1647 self.module.table_initialization[i] =
1648 try_vec![*f; table_size as usize].panic_on_oom();
1649 *init = TableInitialValue::Null;
1650 }
1651 }
1652 }
1653
1654 let mut segments = mem::take(&mut self.table_initialization.segments)
1655 .into_iter()
1656 .peekable();
1657
1658 // The goal of this loop is to interpret a table segment and apply it
1659 // "statically" to a local table. This will iterate over segments and
1660 // apply them one-by-one to each table.
1661 //
1662 // If any segment can't be applied, however, then this loop exits and
1663 // all remaining segments are placed back into the segment list. This is
1664 // because segments are supposed to be initialized one-at-a-time which
1665 // means that intermediate state is visible with respect to traps. If
1666 // anything isn't statically known to not trap it's pessimistically
1667 // assumed to trap meaning all further segment initializers must be
1668 // applied manually at instantiation time.
1669 while let Some(segment) = segments.peek() {
1670 let defined_index = match self.module.defined_table_index(segment.table_index) {
1671 Some(index) => index,
1672 // Skip imported tables: we can't provide a preconstructed
1673 // table for them, because their values depend on the
1674 // imported table overlaid with whatever segments we have.
1675 None => break,
1676 };
1677
1678 // If the base of this segment is dynamic, then we can't
1679 // include it in the statically-built array of initial
1680 // contents.
1681 let offset = match segment.offset.ops() {
1682 &[ConstOp::I32Const(offset)] => u64::from(offset.cast_unsigned()),
1683 &[ConstOp::I64Const(offset)] => offset.cast_unsigned(),
1684 _ => break,
1685 };
1686
1687 // Get the end of this segment. If out-of-bounds, or too
1688 // large for our dense table representation, then skip the
1689 // segment.
1690 let top = match offset.checked_add(segment.init.len()) {
1691 Some(top) => top,
1692 None => break,
1693 };
1694 let table_size = self.module.tables[segment.table_index].limits.min;
1695 if top > table_size || top > MAX_FUNC_TABLE_SIZE {
1696 break;
1697 }
1698
1699 match self.module.tables[segment.table_index]
1700 .ref_type
1701 .heap_type
1702 .top()
1703 {
1704 WasmHeapTopType::Func => {}
1705 // If this is not a funcref table, then we can't support a
1706 // pre-computed table of function indices. Technically this
1707 // initializer won't trap so we could continue processing
1708 // segments, but that's left as a future optimization if
1709 // necessary.
1710 WasmHeapTopType::Any
1711 | WasmHeapTopType::Extern
1712 | WasmHeapTopType::Cont
1713 | WasmHeapTopType::Exn => break,
1714 }
1715
1716 // Function indices can be optimized here, but fully general
1717 // expressions are deferred to get evaluated at runtime.
1718 let function_elements = match &segment.init {
1719 TableSegmentInit::Direct(TableSegmentElements::Functions(indices)) => indices,
1720 TableSegmentInit::Direct(TableSegmentElements::Expressions { .. })
1721 | TableSegmentInit::Staged { .. } => break,
1722 };
1723
1724 match &self.table_initialization.initial_values[defined_index] {
1725 TableInitialValue::Null => {}
1726
1727 // If this table is still listed as an initial value here
1728 // then that means the initial size of the table doesn't
1729 // support a precomputed function list, so skip this.
1730 // Technically this won't trap so it's possible to process
1731 // further initializers, but that's left as a future
1732 // optimization.
1733 TableInitialValue::Expr(_) => break,
1734 }
1735 let precomputed = &mut self.module.table_initialization[defined_index];
1736
1737 // At this point we're committing to pre-initializing the table
1738 // with the `segment` that's being iterated over. This segment is
1739 // applied to the `precomputed` list for the table by ensuring
1740 // it's large enough to hold the segment and then copying the
1741 // segment into the precomputed list.
1742 if precomputed.len() < top as usize {
1743 precomputed
1744 .resize(top as usize, FuncIndex::reserved_value())
1745 .panic_on_oom();
1746 }
1747 let dst = &mut precomputed[offset as usize..top as usize];
1748 dst.copy_from_slice(&function_elements);
1749
1750 // advance the iterator to see the next segment
1751 let _ = segments.next();
1752 }
1753 self.table_initialization.segments = segments.try_collect().panic_on_oom();
1754 }
1755
1756 /// Helper function to ratchet the `startup` function for this module as
1757 /// `Always`.
1758 fn require_startup_func(&mut self, types: &mut ModuleTypesBuilder) {
1759 let ty = match self.module.startup {
1760 ModuleStartup::None => types.startup_func_type().into(),
1761 ModuleStartup::Always(_) => return,
1762 ModuleStartup::IfMemoriesNeedInit(ty) => ty,
1763 };
1764 self.module.startup = ModuleStartup::Always(ty);
1765 }
1766
1767 /// Helper function to ratchet the `startup` function for this module as
1768 /// `IfMemoriesNeedInit`.
1769 fn require_startup_func_if_memories_need_init(&mut self, types: &mut ModuleTypesBuilder) {
1770 let ty = match self.module.startup {
1771 ModuleStartup::None => types.startup_func_type().into(),
1772 ModuleStartup::Always(_) | ModuleStartup::IfMemoriesNeedInit(_) => return,
1773 };
1774 self.module.startup = ModuleStartup::IfMemoriesNeedInit(ty);
1775 }
1776}
1777
1778/// Returns whether evaluating any element of `elements` may trap.
1779fn elements_may_trap(elements: &TableSegmentElements) -> bool {
1780 match elements {
1781 TableSegmentElements::Functions(_) => false,
1782 TableSegmentElements::Expressions { exprs, .. } => exprs
1783 .iter()
1784 .any(|expr| expr.ops().iter().any(|op| op.may_trap())),
1785 }
1786}