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