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