near_vm_engine/universal/
engine.rs

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