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