unc_vm_engine/universal/
engine.rs

1//! Universal compilation.
2
3use super::code_memory::{ARCH_FUNCTION_ALIGNMENT, DATA_SECTION_ALIGNMENT};
4use super::executable::{unrkyv, UniversalExecutableRef};
5use super::{CodeMemory, UniversalArtifact, UniversalExecutable};
6use crate::EngineId;
7use rkyv::de::deserializers::SharedDeserializeMap;
8use std::collections::BTreeMap;
9use std::convert::TryFrom;
10use std::sync::{Arc, Mutex};
11use unc_vm_compiler::Compiler;
12use unc_vm_compiler::{
13    CompileError, CustomSectionProtection, CustomSectionRef, FunctionBodyRef, JumpTable,
14    SectionIndex, Target,
15};
16use unc_vm_types::entity::{EntityRef, PrimaryMap};
17use unc_vm_types::{
18    DataInitializer, ExportIndex, Features, FunctionIndex, FunctionType, FunctionTypeRef,
19    GlobalInit, GlobalType, ImportCounts, ImportIndex, LocalFunctionIndex, LocalGlobalIndex,
20    MemoryIndex, SignatureIndex, TableIndex,
21};
22use unc_vm_vm::{
23    FuncDataRegistry, FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, Tunables,
24    VMCallerCheckedAnyfunc, VMFuncRef, VMImportType, VMLocalFunction, VMOffsets,
25    VMSharedSignatureIndex, VMTrampoline,
26};
27
28/// A WebAssembly `Universal` Engine.
29#[derive(Clone)]
30pub struct UniversalEngine {
31    inner: Arc<Mutex<UniversalEngineInner>>,
32    /// The target for the compiler
33    target: Arc<Target>,
34    engine_id: EngineId,
35}
36
37impl UniversalEngine {
38    /// Create a new `UniversalEngine` with the given config
39    pub fn new(
40        compiler: Box<dyn Compiler>,
41        target: Target,
42        features: Features,
43        memory_allocator: super::LimitedMemoryPool,
44    ) -> Self {
45        Self {
46            inner: Arc::new(Mutex::new(UniversalEngineInner {
47                compiler: Some(compiler),
48                code_memory_pool: memory_allocator,
49                signatures: SignatureRegistry::new(),
50                func_data: Arc::new(FuncDataRegistry::new()),
51                features,
52            })),
53            target: Arc::new(target),
54            engine_id: EngineId::default(),
55        }
56    }
57
58    /// Create a headless `UniversalEngine`
59    ///
60    /// A headless engine is an engine without any compiler attached.
61    /// This is useful for assuring a minimal runtime for running
62    /// WebAssembly modules.
63    ///
64    /// For example, for running in IoT devices where compilers are very
65    /// expensive, or also to optimize startup speed.
66    ///
67    /// # Important
68    ///
69    /// Headless engines can't compile or validate any modules,
70    /// they just take already processed Modules (via `Module::serialize`).
71    pub fn headless(memory_allocator: super::LimitedMemoryPool) -> Self {
72        Self {
73            inner: Arc::new(Mutex::new(UniversalEngineInner {
74                compiler: None,
75                code_memory_pool: memory_allocator,
76                signatures: SignatureRegistry::new(),
77                func_data: Arc::new(FuncDataRegistry::new()),
78                features: Features::default(),
79            })),
80            target: Arc::new(Target::default()),
81            engine_id: EngineId::default(),
82        }
83    }
84
85    pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> {
86        self.inner.lock().unwrap()
87    }
88
89    pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> {
90        self.inner.lock().unwrap()
91    }
92
93    /// Compile a WebAssembly binary
94    #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)]
95    pub fn compile_universal(
96        &self,
97        binary: &[u8],
98        tunables: &dyn Tunables,
99    ) -> Result<super::UniversalExecutable, CompileError> {
100        // Compute the needed instrumentation
101        let instrumentation = finite_wasm::Analysis::new()
102            .with_stack(tunables.stack_limiter_cfg())
103            .with_gas(tunables.gas_cfg())
104            .analyze(binary)
105            .map_err(CompileError::Analyze)?;
106
107        let inner_engine = self.inner_mut();
108        let features = inner_engine.features();
109        let compiler = inner_engine.compiler()?;
110        let environ = unc_vm_compiler::ModuleEnvironment::new();
111        let translation = environ.translate(binary).map_err(CompileError::Wasm)?;
112
113        let memory_styles: PrimaryMap<unc_vm_types::MemoryIndex, _> = translation
114            .module
115            .memories
116            .values()
117            .map(|memory_type| tunables.memory_style(memory_type))
118            .collect();
119        let table_styles: PrimaryMap<unc_vm_types::TableIndex, _> = translation
120            .module
121            .tables
122            .values()
123            .map(|table_type| tunables.table_style(table_type))
124            .collect();
125
126        // Compile the Module
127        let compile_info = unc_vm_compiler::CompileModuleInfo {
128            module: Arc::new(translation.module),
129            features: features.clone(),
130            memory_styles,
131            table_styles,
132        };
133        let unc_vm_compiler::Compilation {
134            functions,
135            custom_sections,
136            function_call_trampolines,
137            dynamic_function_trampolines,
138            debug,
139            trampolines,
140        } = compiler.compile_module(
141            &self.target(),
142            &compile_info,
143            translation.function_body_inputs,
144            tunables,
145            &instrumentation,
146        )?;
147        let data_initializers = translation
148            .data_initializers
149            .iter()
150            .map(unc_vm_types::OwnedDataInitializer::new)
151            .collect();
152        let mut function_frame_info = PrimaryMap::with_capacity(functions.len());
153        let mut function_bodies = PrimaryMap::with_capacity(functions.len());
154        let mut function_relocations = PrimaryMap::with_capacity(functions.len());
155        let mut function_jt_offsets = PrimaryMap::with_capacity(functions.len());
156        for (_, func) in functions.into_iter() {
157            function_bodies.push(func.body);
158            function_relocations.push(func.relocations);
159            function_jt_offsets.push(func.jt_offsets);
160            function_frame_info.push(func.frame_info);
161        }
162        let custom_section_relocations = custom_sections
163            .iter()
164            .map(|(_, section)| section.relocations.clone())
165            .collect::<PrimaryMap<SectionIndex, _>>();
166        Ok(super::UniversalExecutable {
167            function_bodies,
168            function_relocations,
169            function_jt_offsets,
170            function_frame_info,
171            function_call_trampolines,
172            dynamic_function_trampolines,
173            custom_sections,
174            custom_section_relocations,
175            debug,
176            trampolines,
177            compile_info,
178            data_initializers,
179            cpu_features: self.target().cpu_features().as_u64(),
180        })
181    }
182
183    /// Load a [`UniversalExecutable`](crate::UniversalExecutable) with this engine.
184    #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)]
185    pub fn load_universal_executable(
186        &self,
187        executable: &UniversalExecutable,
188    ) -> Result<UniversalArtifact, CompileError> {
189        let info = &executable.compile_info;
190        let module = &info.module;
191        let local_memories = (module.import_counts.memories as usize..module.memories.len())
192            .map(|idx| {
193                let idx = MemoryIndex::new(idx);
194                (module.memories[idx], info.memory_styles[idx].clone())
195            })
196            .collect();
197        let local_tables = (module.import_counts.tables as usize..module.tables.len())
198            .map(|idx| {
199                let idx = TableIndex::new(idx);
200                (module.tables[idx], info.table_styles[idx].clone())
201            })
202            .collect();
203        let local_globals: Vec<(GlobalType, GlobalInit)> = module
204            .globals
205            .iter()
206            .skip(module.import_counts.globals as usize)
207            .enumerate()
208            .map(|(idx, (_, t))| {
209                let init = module.global_initializers[LocalGlobalIndex::new(idx)];
210                (*t, init)
211            })
212            .collect();
213        let mut inner_engine = self.inner_mut();
214
215        let local_functions = executable.function_bodies.iter().map(|(_, b)| b.into());
216        let function_call_trampolines = &executable.function_call_trampolines;
217        let dynamic_function_trampolines = &executable.dynamic_function_trampolines;
218        let signatures = module
219            .signatures
220            .iter()
221            .map(|(_, sig)| inner_engine.signatures.register(sig.clone()))
222            .collect::<PrimaryMap<SignatureIndex, _>>()
223            .into_boxed_slice();
224        let (functions, trampolines, dynamic_trampolines, custom_sections, mut code_memory) =
225            inner_engine.allocate(
226                local_functions,
227                function_call_trampolines.iter().map(|(_, b)| b.into()),
228                dynamic_function_trampolines.iter().map(|(_, b)| b.into()),
229                executable.custom_sections.iter().map(|(_, s)| s.into()),
230                |idx: LocalFunctionIndex| {
231                    let func_idx = module.import_counts.function_index(idx);
232                    let sig_idx = module.functions[func_idx];
233                    (sig_idx, signatures[sig_idx])
234                },
235            )?;
236        let imports = module
237            .imports
238            .iter()
239            .map(|((module_name, field, idx), entity)| unc_vm_vm::VMImport {
240                module: String::from(module_name),
241                field: String::from(field),
242                import_no: *idx,
243                ty: match entity {
244                    ImportIndex::Function(i) => {
245                        let sig_idx = module.functions[*i];
246                        VMImportType::Function {
247                            sig: signatures[sig_idx],
248                            static_trampoline: trampolines[sig_idx],
249                        }
250                    }
251                    ImportIndex::Table(i) => VMImportType::Table(module.tables[*i]),
252                    &ImportIndex::Memory(i) => {
253                        let ty = module.memories[i];
254                        VMImportType::Memory(ty, info.memory_styles[i].clone())
255                    }
256                    ImportIndex::Global(i) => VMImportType::Global(module.globals[*i]),
257                },
258            })
259            .collect();
260
261        let function_relocations = executable.function_relocations.iter();
262        let section_relocations = executable.custom_section_relocations.iter();
263        crate::universal::link_module(
264            &functions,
265            |func_idx, jt_idx| executable.function_jt_offsets[func_idx][jt_idx],
266            function_relocations.map(|(i, rs)| (i, rs.iter().cloned())),
267            &custom_sections,
268            section_relocations.map(|(i, rs)| (i, rs.iter().cloned())),
269            &executable.trampolines,
270        );
271
272        // Make all code loaded executable.
273        unsafe {
274            // SAFETY: We finished relocation and linking just above. There should be no write
275            // access past this point, though I don’t think we have a good mechanism to ensure this
276            // statically at this point..
277            code_memory.publish()?;
278        }
279        let exports = module
280            .exports
281            .iter()
282            .map(|(s, i)| (s.clone(), i.clone()))
283            .collect::<BTreeMap<String, ExportIndex>>();
284
285        Ok(UniversalArtifact {
286            engine: self.clone(),
287            _code_memory: code_memory,
288            import_counts: module.import_counts,
289            start_function: module.start_function,
290            vmoffsets: VMOffsets::for_host().with_module_info(&*module),
291            imports,
292            dynamic_function_trampolines: dynamic_trampolines.into_boxed_slice(),
293            functions: functions.into_boxed_slice(),
294            exports,
295            signatures,
296            local_memories,
297            data_segments: executable.data_initializers.clone(),
298            passive_data: module.passive_data.clone(),
299            local_tables,
300            element_segments: module.table_initializers.clone(),
301            passive_elements: module.passive_elements.clone(),
302            local_globals,
303        })
304    }
305
306    /// Load a [`UniversalExecutableRef`](crate::UniversalExecutableRef) with this engine.
307    pub fn load_universal_executable_ref(
308        &self,
309        executable: &UniversalExecutableRef,
310    ) -> Result<UniversalArtifact, CompileError> {
311        let info = &executable.compile_info;
312        let module = &info.module;
313        let import_counts: ImportCounts = unrkyv(&module.import_counts);
314        let local_memories = (import_counts.memories as usize..module.memories.len())
315            .map(|idx| {
316                let idx = MemoryIndex::new(idx);
317                let mty = &module.memories[&idx];
318                (unrkyv(mty), unrkyv(&info.memory_styles[&idx]))
319            })
320            .collect();
321        let local_tables = (import_counts.tables as usize..module.tables.len())
322            .map(|idx| {
323                let idx = TableIndex::new(idx);
324                let tty = &module.tables[&idx];
325                (unrkyv(tty), unrkyv(&info.table_styles[&idx]))
326            })
327            .collect();
328        let local_globals: Vec<(GlobalType, GlobalInit)> = module
329            .globals
330            .iter()
331            .skip(import_counts.globals as _)
332            .enumerate()
333            .map(|(idx, (_, t))| {
334                let init = unrkyv(&module.global_initializers[&LocalGlobalIndex::new(idx)]);
335                (*t, init)
336            })
337            .collect();
338
339        let passive_data =
340            rkyv::Deserialize::deserialize(&module.passive_data, &mut SharedDeserializeMap::new())
341                .map_err(|_| CompileError::Validate("could not deserialize passive data".into()))?;
342        let data_segments = executable.data_initializers.iter();
343        let data_segments = data_segments.map(|s| DataInitializer::from(s).into()).collect();
344        let element_segments = unrkyv(&module.table_initializers);
345        let passive_elements: BTreeMap<unc_vm_types::ElemIndex, Box<[FunctionIndex]>> =
346            unrkyv(&module.passive_elements);
347
348        let import_counts: ImportCounts = unrkyv(&module.import_counts);
349        let mut inner_engine = self.inner_mut();
350
351        let local_functions = executable.function_bodies.iter().map(|(_, b)| b.into());
352        let call_trampolines = executable.function_call_trampolines.iter();
353        let dynamic_trampolines = executable.dynamic_function_trampolines.iter();
354        let signatures = module
355            .signatures
356            .values()
357            .map(|sig| {
358                let sig_ref = FunctionTypeRef::from(sig);
359                inner_engine
360                    .signatures
361                    .register(FunctionType::new(sig_ref.params(), sig_ref.results()))
362            })
363            .collect::<PrimaryMap<SignatureIndex, _>>()
364            .into_boxed_slice();
365        let (functions, trampolines, dynamic_trampolines, custom_sections, mut code_memory) =
366            inner_engine.allocate(
367                local_functions,
368                call_trampolines.map(|(_, b)| b.into()),
369                dynamic_trampolines.map(|(_, b)| b.into()),
370                executable.custom_sections.iter().map(|(_, s)| s.into()),
371                |idx: LocalFunctionIndex| {
372                    let func_idx = import_counts.function_index(idx);
373                    let sig_idx = module.functions[&func_idx];
374                    (sig_idx, signatures[sig_idx])
375                },
376            )?;
377        let imports = {
378            module
379                .imports
380                .iter()
381                .map(|((module_name, field, idx), entity)| unc_vm_vm::VMImport {
382                    module: String::from(module_name.as_str()),
383                    field: String::from(field.as_str()),
384                    import_no: *idx,
385                    ty: match entity {
386                        ImportIndex::Function(i) => {
387                            let sig_idx = module.functions[i];
388                            VMImportType::Function {
389                                sig: signatures[sig_idx],
390                                static_trampoline: trampolines[sig_idx],
391                            }
392                        }
393                        ImportIndex::Table(i) => VMImportType::Table(unrkyv(&module.tables[i])),
394                        ImportIndex::Memory(i) => {
395                            let ty = unrkyv(&module.memories[i]);
396                            VMImportType::Memory(ty, unrkyv(&info.memory_styles[i]))
397                        }
398                        ImportIndex::Global(i) => VMImportType::Global(unrkyv(&module.globals[i])),
399                    },
400                })
401                .collect()
402        };
403
404        let function_relocations = executable.function_relocations.iter();
405        let section_relocations = executable.custom_section_relocations.iter();
406        crate::universal::link_module(
407            &functions,
408            |func_idx, jt_idx| {
409                let func_idx = rkyv::Archived::<LocalFunctionIndex>::new(func_idx.index());
410                let jt_idx = rkyv::Archived::<JumpTable>::new(jt_idx.index());
411                executable.function_jt_offsets[&func_idx][&jt_idx]
412            },
413            function_relocations.map(|(i, r)| (i, r.iter().map(unrkyv))),
414            &custom_sections,
415            section_relocations.map(|(i, r)| (i, r.iter().map(unrkyv))),
416            &unrkyv(&executable.trampolines),
417        );
418
419        // Make all code compiled thus far executable.
420        unsafe {
421            // SAFETY: We finished relocation and linking just above. There should be no write
422            // access past this point, though I don’t think we have a good mechanism to ensure this
423            // statically at this point..
424            code_memory.publish()?;
425        }
426        let exports = module
427            .exports
428            .iter()
429            .map(|(s, i)| (unrkyv(s), unrkyv(i)))
430            .collect::<BTreeMap<String, ExportIndex>>();
431        Ok(UniversalArtifact {
432            engine: self.clone(),
433            _code_memory: code_memory,
434            import_counts,
435            start_function: unrkyv(&module.start_function),
436            vmoffsets: VMOffsets::for_host().with_archived_module_info(&*module),
437            imports,
438            dynamic_function_trampolines: dynamic_trampolines.into_boxed_slice(),
439            functions: functions.into_boxed_slice(),
440            exports,
441            signatures,
442            local_memories,
443            data_segments,
444            passive_data,
445            local_tables,
446            element_segments,
447            passive_elements,
448            local_globals,
449        })
450    }
451
452    /// The target
453    pub fn target(&self) -> &Target {
454        &self.target
455    }
456
457    /// Register a signature
458    pub fn register_signature(&self, func_type: FunctionType) -> VMSharedSignatureIndex {
459        self.inner().signatures.register(func_type)
460    }
461
462    /// Register some function metadata
463    pub fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef {
464        self.inner().func_data().register(func_data)
465    }
466
467    /// Lookup a signature
468    pub fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
469        self.inner().signatures.lookup(sig).cloned()
470    }
471
472    /// Validates a WebAssembly module
473    #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)]
474    pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
475        self.inner().validate(binary)
476    }
477
478    /// Engine ID
479    pub fn id(&self) -> &EngineId {
480        &self.engine_id
481    }
482}
483
484/// The inner contents of `UniversalEngine`
485pub struct UniversalEngineInner {
486    /// The compiler
487    compiler: Option<Box<dyn Compiler>>,
488    /// Pool from which code memory can be allocated.
489    code_memory_pool: super::LimitedMemoryPool,
490    /// The features to compile the Wasm module with
491    features: Features,
492    /// The signature registry is used mainly to operate with trampolines
493    /// performantly.
494    pub(crate) signatures: SignatureRegistry,
495    /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2
496    /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`.
497    /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped.
498    func_data: Arc<FuncDataRegistry>,
499}
500
501impl UniversalEngineInner {
502    /// Gets the compiler associated to this engine.
503    pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
504        if self.compiler.is_none() {
505            return Err(CompileError::Codegen("The UniversalEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
506        }
507        Ok(&**self.compiler.as_ref().unwrap())
508    }
509
510    /// Validate the module
511    pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
512        self.compiler()?.validate_module(self.features(), data)
513    }
514
515    /// The Wasm features
516    pub fn features(&self) -> &Features {
517        &self.features
518    }
519
520    /// Allocate compiled functions into memory
521    #[allow(clippy::type_complexity)]
522    pub(crate) fn allocate<'a>(
523        &mut self,
524        local_functions: impl ExactSizeIterator<Item = FunctionBodyRef<'a>>,
525        call_trampolines: impl ExactSizeIterator<Item = FunctionBodyRef<'a>>,
526        dynamic_trampolines: impl ExactSizeIterator<Item = FunctionBodyRef<'a>>,
527        custom_sections: impl ExactSizeIterator<Item = CustomSectionRef<'a>>,
528        function_signature: impl Fn(LocalFunctionIndex) -> (SignatureIndex, VMSharedSignatureIndex),
529    ) -> Result<
530        (
531            PrimaryMap<LocalFunctionIndex, VMLocalFunction>,
532            PrimaryMap<SignatureIndex, VMTrampoline>,
533            PrimaryMap<FunctionIndex, FunctionBodyPtr>,
534            PrimaryMap<SectionIndex, SectionBodyPtr>,
535            CodeMemory,
536        ),
537        CompileError,
538    > {
539        let code_memory_pool = &mut self.code_memory_pool;
540        let function_count = local_functions.len();
541        let call_trampoline_count = call_trampolines.len();
542        let function_bodies =
543            call_trampolines.chain(local_functions).chain(dynamic_trampolines).collect::<Vec<_>>();
544
545        // TOOD: this shouldn't be necessary....
546        let mut section_types = Vec::with_capacity(custom_sections.len());
547        let mut executable_sections = Vec::new();
548        let mut data_sections = Vec::new();
549        for section in custom_sections {
550            if let CustomSectionProtection::ReadExecute = section.protection {
551                executable_sections.push(section);
552            } else {
553                data_sections.push(section);
554            }
555            section_types.push(section.protection);
556        }
557
558        // 1. Calculate the total size, that is:
559        // - function body size, including all trampolines
560        // -- windows unwind info
561        // -- padding between functions
562        // - executable section body
563        // -- padding between executable sections
564        // - padding until a new page to change page permissions
565        // - data section body size
566        // -- padding between data sections
567        let page_size = rustix::param::page_size();
568        let total_len = 0;
569        let total_len = function_bodies.iter().fold(total_len, |acc, func| {
570            round_up(acc, ARCH_FUNCTION_ALIGNMENT.into()) + function_allocation_size(*func)
571        });
572        let total_len = executable_sections.iter().fold(total_len, |acc, exec| {
573            round_up(acc, ARCH_FUNCTION_ALIGNMENT.into()) + exec.bytes.len()
574        });
575        let total_len = round_up(total_len, page_size);
576        let total_len = data_sections.iter().fold(total_len, |acc, data| {
577            round_up(acc, DATA_SECTION_ALIGNMENT.into()) + data.bytes.len()
578        });
579
580        let mut code_memory = code_memory_pool.get(total_len).map_err(|e| {
581            CompileError::Resource(format!("could not allocate code memory: {}", e))
582        })?;
583        let mut code_writer = unsafe {
584            // SAFETY: We just popped out an unused code memory from an allocator pool.
585            code_memory.writer()
586        };
587
588        let mut allocated_functions = vec![];
589        let mut allocated_data_sections = vec![];
590        let mut allocated_executable_sections = vec![];
591        for func in function_bodies {
592            let offset = code_writer
593                .write_executable(ARCH_FUNCTION_ALIGNMENT, func.body)
594                .expect("incorrectly computed code memory size");
595            allocated_functions.push((offset, func.body.len()));
596        }
597        for section in executable_sections {
598            let offset = code_writer.write_executable(ARCH_FUNCTION_ALIGNMENT, section.bytes)?;
599            allocated_executable_sections.push(offset);
600        }
601        if !data_sections.is_empty() {
602            for section in data_sections {
603                let offset = code_writer
604                    .write_data(DATA_SECTION_ALIGNMENT, section.bytes)
605                    .expect("incorrectly computed code memory size");
606                allocated_data_sections.push(offset);
607            }
608        }
609
610        let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
611            PrimaryMap::new();
612
613        for (offset, _) in allocated_functions.drain(0..call_trampoline_count) {
614            let trampoline = unsafe {
615                // SAFETY: The executable code was written at the specified offset just above.
616                // TODO: Somewhat concerning is that the `VMTrampoline` does not ensure that the
617                // lifetime of the function pointer is a subset of the lifetime of the
618                // `code_memory`. Quite conversely, this `transmute` asserts that `VMTrampoline:
619                // 'static` and thus that this function pointer is callable even after
620                // `code_memory` is freed.
621                //
622                // As lifetime annotations in Rust cannot influence the codegen, this is not a
623                // source of undefined behaviour but we do lose static lifetime checks that Rust
624                // enforces.
625                std::mem::transmute::<_, VMTrampoline>(code_memory.executable_address(offset))
626            };
627            allocated_function_call_trampolines.push(trampoline);
628        }
629
630        let allocated_functions_result = allocated_functions
631            .drain(0..function_count)
632            .enumerate()
633            .map(|(index, (offset, length))| -> Result<_, CompileError> {
634                let index = LocalFunctionIndex::new(index);
635                let (sig_idx, sig) = function_signature(index);
636                Ok(VMLocalFunction {
637                    body: FunctionBodyPtr(unsafe { code_memory.executable_address(offset).cast() }),
638                    length: u32::try_from(length).map_err(|_| {
639                        CompileError::Codegen("function body length exceeds 4GiB".into())
640                    })?,
641                    signature: sig,
642                    trampoline: allocated_function_call_trampolines[sig_idx],
643                })
644            })
645            .collect::<Result<PrimaryMap<LocalFunctionIndex, _>, _>>()?;
646
647        let allocated_dynamic_function_trampolines = allocated_functions
648            .drain(..)
649            .map(|(offset, _)| {
650                FunctionBodyPtr(unsafe { code_memory.executable_address(offset).cast() })
651            })
652            .collect::<PrimaryMap<FunctionIndex, _>>();
653
654        let mut exec_iter = allocated_executable_sections.iter();
655        let mut data_iter = allocated_data_sections.iter();
656        let allocated_custom_sections = section_types
657            .into_iter()
658            .map(|protection| {
659                SectionBodyPtr(if protection == CustomSectionProtection::ReadExecute {
660                    unsafe { code_memory.executable_address(*exec_iter.next().unwrap()).cast() }
661                } else {
662                    unsafe { code_memory.writable_address(*data_iter.next().unwrap()).cast() }
663                })
664            })
665            .collect::<PrimaryMap<SectionIndex, _>>();
666
667        Ok((
668            allocated_functions_result,
669            allocated_function_call_trampolines,
670            allocated_dynamic_function_trampolines,
671            allocated_custom_sections,
672            code_memory,
673        ))
674    }
675
676    /// Shared func metadata registry.
677    pub(crate) fn func_data(&self) -> &Arc<FuncDataRegistry> {
678        &self.func_data
679    }
680}
681
682fn round_up(size: usize, multiple: usize) -> usize {
683    debug_assert!(multiple.is_power_of_two());
684    (size + (multiple - 1)) & !(multiple - 1)
685}
686
687fn function_allocation_size(func: FunctionBodyRef<'_>) -> usize {
688    func.body.len()
689}