Skip to main content

wasmer_vm/
vmcontext.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! This file declares `VMContext` and several related structs which contain
5//! fields that compiled wasm code accesses directly.
6
7use crate::VMFunctionBody;
8use crate::VMTable;
9use crate::global::VMGlobal;
10use crate::instance::Instance;
11use crate::memory::VMMemory;
12use crate::store::InternalStoreHandle;
13use crate::trap::{Trap, TrapCode};
14use crate::{VMBuiltinFunctionIndex, VMFunction};
15use std::convert::TryFrom;
16use std::hash::{Hash, Hasher};
17use std::ptr::{self, NonNull};
18use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
19use wasmer_types::RawValue;
20
21/// Union representing the first parameter passed when calling a function.
22///
23/// It may either be a pointer to the [`VMContext`] if it's a Wasm function
24/// or a pointer to arbitrary data controlled by the host if it's a host function.
25#[derive(Copy, Clone, Eq)]
26#[repr(C)]
27pub union VMFunctionContext {
28    /// Wasm functions take a pointer to [`VMContext`].
29    pub vmctx: *mut VMContext,
30    /// Host functions can have custom environments.
31    pub host_env: *mut std::ffi::c_void,
32}
33
34impl VMFunctionContext {
35    /// Check whether the pointer stored is null or not.
36    pub fn is_null(&self) -> bool {
37        unsafe { self.host_env.is_null() }
38    }
39}
40
41impl std::fmt::Debug for VMFunctionContext {
42    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
43        f.debug_struct("VMFunctionContext")
44            .field("vmctx_or_hostenv", unsafe { &self.host_env })
45            .finish()
46    }
47}
48
49impl std::cmp::PartialEq for VMFunctionContext {
50    fn eq(&self, rhs: &Self) -> bool {
51        unsafe { std::ptr::eq(self.host_env, rhs.host_env) }
52    }
53}
54
55impl std::hash::Hash for VMFunctionContext {
56    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
57        unsafe {
58            self.vmctx.hash(state);
59        }
60    }
61}
62
63/// An imported function.
64#[derive(Debug, Copy, Clone)]
65#[repr(C)]
66pub struct VMFunctionImport {
67    /// A pointer to the imported function body.
68    pub body: *const VMFunctionBody,
69
70    /// A pointer to the `VMContext` that owns the function or host env data.
71    pub environment: VMFunctionContext,
72
73    /// Handle to the `VMFunction` in the context.
74    pub handle: InternalStoreHandle<VMFunction>,
75
76    /// Flag if the function requires extra the m0 argument (used for m0 optimization dispatch).
77    pub include_m0_param: bool,
78}
79
80#[cfg(test)]
81mod test_vmfunction_import {
82    use super::VMFunctionImport;
83    use core::mem::offset_of;
84    use std::mem::size_of;
85    use wasmer_types::ModuleInfo;
86    use wasmer_types::VMOffsets;
87
88    #[test]
89    fn check_vmfunction_import_offsets() {
90        let module = ModuleInfo::new();
91        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
92        assert_eq!(
93            size_of::<VMFunctionImport>(),
94            usize::from(offsets.size_of_vmfunction_import())
95        );
96        assert_eq!(
97            offset_of!(VMFunctionImport, body),
98            usize::from(offsets.vmfunction_import_body())
99        );
100        assert_eq!(
101            offset_of!(VMFunctionImport, environment),
102            usize::from(offsets.vmfunction_import_vmctx())
103        );
104    }
105}
106
107/// The `VMDynamicFunctionContext` is the context that dynamic
108/// functions will receive when called (rather than `vmctx`).
109/// A dynamic function is a function for which we don't know the signature
110/// until runtime.
111///
112/// As such, we need to expose the dynamic function `context`
113/// containing the relevant context for running the function indicated
114/// in `address`.
115#[repr(C)]
116pub struct VMDynamicFunctionContext<T> {
117    /// The address of the inner dynamic function.
118    ///
119    /// Note: The function must be on the form of
120    /// `(*mut T, SignatureIndex, *mut i128)`.
121    pub address: *const VMFunctionBody,
122
123    /// The context that the inner dynamic function will receive.
124    pub ctx: T,
125}
126
127// The `ctx` itself must be `Send`, `address` can be passed between
128// threads because all usage is `unsafe` and synchronized.
129unsafe impl<T: Sized + Send + Sync> Send for VMDynamicFunctionContext<T> {}
130// The `ctx` itself must be `Sync`, `address` can be shared between
131// threads because all usage is `unsafe` and synchronized.
132unsafe impl<T: Sized + Send + Sync> Sync for VMDynamicFunctionContext<T> {}
133
134impl<T: Sized + Clone + Send + Sync> Clone for VMDynamicFunctionContext<T> {
135    fn clone(&self) -> Self {
136        Self {
137            address: self.address,
138            ctx: self.ctx.clone(),
139        }
140    }
141}
142
143#[cfg(test)]
144mod test_vmdynamicfunction_import_context {
145    use super::VMDynamicFunctionContext;
146    use crate::VMOffsets;
147    use core::mem::offset_of;
148    use std::mem::size_of;
149    use wasmer_types::ModuleInfo;
150
151    #[test]
152    fn check_vmdynamicfunction_import_context_offsets() {
153        let module = ModuleInfo::new();
154        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
155        assert_eq!(
156            size_of::<VMDynamicFunctionContext<usize>>(),
157            usize::from(offsets.size_of_vmdynamicfunction_import_context())
158        );
159        assert_eq!(
160            offset_of!(VMDynamicFunctionContext<usize>, address),
161            usize::from(offsets.vmdynamicfunction_import_context_address())
162        );
163        assert_eq!(
164            offset_of!(VMDynamicFunctionContext<usize>, ctx),
165            usize::from(offsets.vmdynamicfunction_import_context_ctx())
166        );
167    }
168}
169
170/// A function kind is a calling convention into and out of wasm code.
171#[derive(Debug, Copy, Clone, Eq, PartialEq)]
172#[repr(C)]
173pub enum VMFunctionKind {
174    /// A static function has the native signature:
175    /// `extern "C" (vmctx, arg1, arg2...) -> (result1, result2, ...)`.
176    ///
177    /// This is the default for functions that are defined:
178    /// 1. In the Host, natively
179    /// 2. In the WebAssembly file
180    Static,
181
182    /// A dynamic function has the native signature:
183    /// `extern "C" (ctx, &[Value]) -> Vec<Value>`.
184    ///
185    /// This is the default for functions that are defined:
186    /// 1. In the Host, dynamically
187    Dynamic,
188}
189
190/// The fields compiled code needs to access to utilize a WebAssembly table
191/// imported from another instance.
192#[derive(Clone)]
193#[repr(C)]
194pub struct VMTableImport {
195    /// A pointer to the imported table description.
196    pub definition: NonNull<VMTableDefinition>,
197
198    /// Handle to the `VMTable` in the context.
199    pub handle: InternalStoreHandle<VMTable>,
200}
201
202#[cfg(test)]
203mod test_vmtable_import {
204    use super::VMTableImport;
205    use crate::VMOffsets;
206    use core::mem::offset_of;
207    use std::mem::size_of;
208    use wasmer_types::ModuleInfo;
209
210    #[test]
211    fn check_vmtable_import_offsets() {
212        let module = ModuleInfo::new();
213        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
214        assert_eq!(
215            size_of::<VMTableImport>(),
216            usize::from(offsets.size_of_vmtable_import())
217        );
218        assert_eq!(
219            offset_of!(VMTableImport, definition),
220            usize::from(offsets.vmtable_import_definition())
221        );
222    }
223}
224
225/// The fields compiled code needs to access to utilize a WebAssembly linear
226/// memory imported from another instance.
227#[derive(Clone)]
228#[repr(C)]
229pub struct VMMemoryImport {
230    /// A pointer to the imported memory description.
231    pub definition: NonNull<VMMemoryDefinition>,
232
233    /// A handle to the `Memory` that owns the memory description.
234    pub handle: InternalStoreHandle<VMMemory>,
235}
236
237#[cfg(test)]
238mod test_vmmemory_import {
239    use super::VMMemoryImport;
240    use crate::VMOffsets;
241    use core::mem::offset_of;
242    use std::mem::size_of;
243    use wasmer_types::ModuleInfo;
244
245    #[test]
246    fn check_vmmemory_import_offsets() {
247        let module = ModuleInfo::new();
248        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
249        assert_eq!(
250            size_of::<VMMemoryImport>(),
251            usize::from(offsets.size_of_vmmemory_import())
252        );
253        assert_eq!(
254            offset_of!(VMMemoryImport, definition),
255            usize::from(offsets.vmmemory_import_definition())
256        );
257        assert_eq!(
258            offset_of!(VMMemoryImport, handle),
259            usize::from(offsets.vmmemory_import_handle())
260        );
261    }
262}
263
264/// The fields compiled code needs to access to utilize a WebAssembly global
265/// variable imported from another instance.
266#[derive(Clone)]
267#[repr(C)]
268pub struct VMGlobalImport {
269    /// A pointer to the imported global variable description.
270    pub definition: NonNull<VMGlobalDefinition>,
271
272    /// A handle to the `Global` that owns the global description.
273    pub handle: InternalStoreHandle<VMGlobal>,
274}
275
276/// # Safety
277/// This data is safe to share between threads because it's plain data that
278/// is the user's responsibility to synchronize. Additionally, all operations
279/// on `from` are thread-safe through the use of a mutex in [`VMGlobal`].
280unsafe impl Send for VMGlobalImport {}
281/// # Safety
282/// This data is safe to share between threads because it's plain data that
283/// is the user's responsibility to synchronize. And because it's `Clone`, there's
284/// really no difference between passing it by reference or by value as far as
285/// correctness in a multi-threaded context is concerned.
286unsafe impl Sync for VMGlobalImport {}
287
288#[cfg(test)]
289mod test_vmglobal_import {
290    use super::VMGlobalImport;
291    use crate::VMOffsets;
292    use core::mem::offset_of;
293    use std::mem::size_of;
294    use wasmer_types::ModuleInfo;
295
296    #[test]
297    fn check_vmglobal_import_offsets() {
298        let module = ModuleInfo::new();
299        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
300        assert_eq!(
301            size_of::<VMGlobalImport>(),
302            usize::from(offsets.size_of_vmglobal_import())
303        );
304        assert_eq!(
305            offset_of!(VMGlobalImport, definition),
306            usize::from(offsets.vmglobal_import_definition())
307        );
308    }
309}
310
311/// Do an unsynchronized, non-atomic `memory.copy` for the memory.
312///
313/// # Errors
314///
315/// Returns a `Trap` error when the source or destination ranges are out of
316/// bounds.
317///
318/// # Safety
319/// The memory is not copied atomically and is not synchronized: it's the
320/// caller's responsibility to synchronize.
321pub(crate) unsafe fn memory_copy(
322    dst_mem: &VMMemoryDefinition,
323    src_mem: &VMMemoryDefinition,
324    dst: u32,
325    src: u32,
326    len: u32,
327) -> Result<(), Trap> {
328    unsafe {
329        // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
330        if src
331            .checked_add(len)
332            .is_none_or(|n| usize::try_from(n).unwrap() > src_mem.current_length)
333            || dst
334                .checked_add(len)
335                .is_none_or(|m| usize::try_from(m).unwrap() > dst_mem.current_length)
336        {
337            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
338        }
339
340        let dst = usize::try_from(dst).unwrap();
341        let src = usize::try_from(src).unwrap();
342
343        // Bounds and casts are checked above, by this point we know that
344        // everything is safe.
345        let dst = dst_mem.base.add(dst);
346        let src = src_mem.base.add(src);
347        ptr::copy(src, dst, len as usize);
348
349        Ok(())
350    }
351}
352
353/// Perform the `memory.fill` operation for the memory in an unsynchronized,
354/// non-atomic way.
355///
356/// # Errors
357///
358/// Returns a `Trap` error if the memory range is out of bounds.
359///
360/// # Safety
361/// The memory is not filled atomically and is not synchronized: it's the
362/// caller's responsibility to synchronize.
363pub(crate) unsafe fn memory_fill(
364    mem: &VMMemoryDefinition,
365    dst: u32,
366    val: u32,
367    len: u32,
368) -> Result<(), Trap> {
369    unsafe {
370        if dst
371            .checked_add(len)
372            .is_none_or(|m| usize::try_from(m).unwrap() > mem.current_length)
373        {
374            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
375        }
376
377        let dst = isize::try_from(dst).unwrap();
378        let val = val as u8;
379
380        // Bounds and casts are checked above, by this point we know that
381        // everything is safe.
382        let dst = mem.base.offset(dst);
383        ptr::write_bytes(dst, val, len as usize);
384
385        Ok(())
386    }
387}
388
389/// Check the bounds and alignment of a `memory.atomic.notify` address.
390///
391/// # Errors
392///
393/// Returns a `Trap` error if the memory range is out of bounds or not 32-bit aligned.
394pub(crate) fn memory32_atomic_check_notify(mem: &VMMemoryDefinition, dst: u32) -> Result<(), Trap> {
395    const TYPE_SIZE: usize = size_of::<u32>();
396    let dst = usize::try_from(dst).unwrap();
397    if dst
398        .checked_add(TYPE_SIZE)
399        .is_none_or(|end| end > mem.current_length)
400    {
401        return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
402    }
403
404    if !dst.is_multiple_of(TYPE_SIZE) {
405        return Err(Trap::lib(TrapCode::UnalignedAtomic));
406    }
407    Ok(())
408}
409
410/// Perform the `memory32.atomic.check32` operation for the memory. Return 0 if same, 1 if different
411///
412/// # Errors
413///
414/// Returns a `Trap` error if the memory range is out of bounds or 32bits unligned.
415///
416/// # Safety
417/// memory access is unsafe
418pub(crate) unsafe fn memory32_atomic_check32(
419    mem: &VMMemoryDefinition,
420    dst: u32,
421    val: u32,
422) -> Result<u32, Trap> {
423    unsafe {
424        const TYPE_SIZE: usize = size_of::<u32>();
425        let dst = usize::try_from(dst).unwrap();
426        if dst
427            .checked_add(TYPE_SIZE)
428            .is_none_or(|end| end > mem.current_length)
429        {
430            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
431        }
432
433        if !dst.is_multiple_of(TYPE_SIZE) {
434            return Err(Trap::lib(TrapCode::UnalignedAtomic));
435        }
436        let Ok(dst) = isize::try_from(dst) else {
437            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
438        };
439
440        // Bounds and casts are checked above, by this point we know that
441        // everything is safe.
442        let dst = mem.base.offset(dst) as *mut u32;
443        let read_val = AtomicU32::from_ptr(dst).load(Ordering::Acquire);
444        let ret = if read_val == val { 0 } else { 1 };
445        Ok(ret)
446    }
447}
448
449/// Perform the `memory32.atomic.check64` operation for the memory. Return 0 if same, 1 if different
450///
451/// # Errors
452///
453/// Returns a `Trap` error if the memory range is out of bounds or 64bits unaligned.
454///
455/// # Safety
456/// memory access is unsafe
457pub(crate) unsafe fn memory32_atomic_check64(
458    mem: &VMMemoryDefinition,
459    dst: u32,
460    val: u64,
461) -> Result<u32, Trap> {
462    unsafe {
463        const TYPE_SIZE: usize = size_of::<u64>();
464        let dst = usize::try_from(dst).unwrap();
465        if dst
466            .checked_add(TYPE_SIZE)
467            .is_none_or(|end| end > mem.current_length)
468        {
469            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
470        }
471
472        if !dst.is_multiple_of(TYPE_SIZE) {
473            return Err(Trap::lib(TrapCode::UnalignedAtomic));
474        }
475        let Ok(dst) = isize::try_from(dst) else {
476            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
477        };
478
479        // Bounds and casts are checked above, by this point we know that
480        // everything is safe.
481        let dst = mem.base.offset(dst) as *mut u64;
482        let read_val = AtomicU64::from_ptr(dst).load(Ordering::Acquire);
483        let ret = if read_val == val { 0 } else { 1 };
484        Ok(ret)
485    }
486}
487
488/// The fields compiled code needs to access to utilize a WebAssembly table
489/// defined within the instance.
490#[derive(Debug, Clone, Copy)]
491#[repr(C)]
492pub struct VMTableDefinition {
493    /// Pointer to the table data.
494    pub base: *mut u8,
495
496    /// The current number of elements in the table.
497    pub current_elements: u32,
498}
499
500#[cfg(test)]
501mod test_vmtable_definition {
502    use super::VMTableDefinition;
503    use crate::VMOffsets;
504    use core::mem::offset_of;
505    use std::mem::size_of;
506    use wasmer_types::ModuleInfo;
507
508    #[test]
509    fn check_vmtable_definition_offsets() {
510        let module = ModuleInfo::new();
511        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
512        assert_eq!(
513            size_of::<VMTableDefinition>(),
514            usize::from(offsets.size_of_vmtable_definition())
515        );
516        assert_eq!(
517            offset_of!(VMTableDefinition, base),
518            usize::from(offsets.vmtable_definition_base())
519        );
520        assert_eq!(
521            offset_of!(VMTableDefinition, current_elements),
522            usize::from(offsets.vmtable_definition_current_elements())
523        );
524    }
525}
526
527/// The storage for a WebAssembly global defined within the instance.
528///
529/// TODO: Pack the globals more densely, rather than using the same size
530/// for every type.
531#[derive(Debug, Clone)]
532#[repr(C, align(16))]
533pub struct VMGlobalDefinition {
534    /// Raw value of the global.
535    pub val: RawValue,
536}
537
538#[cfg(test)]
539mod test_vmglobal_definition {
540    use super::VMGlobalDefinition;
541    use crate::{VMFuncRef, VMOffsets};
542    use more_asserts::assert_ge;
543    use std::mem::{align_of, size_of};
544    use wasmer_types::ModuleInfo;
545
546    #[test]
547    fn check_vmglobal_definition_alignment() {
548        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<i32>());
549        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<i64>());
550        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<f32>());
551        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<f64>());
552        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<VMFuncRef>());
553        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<[u8; 16]>());
554    }
555
556    #[test]
557    fn check_vmglobal_definition_offsets() {
558        let module = ModuleInfo::new();
559        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
560        assert_eq!(
561            size_of::<VMGlobalDefinition>(),
562            usize::from(offsets.size_of_vmglobal_local())
563        );
564    }
565
566    #[test]
567    fn check_vmglobal_begins_aligned() {
568        let module = ModuleInfo::new();
569        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
570        assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
571    }
572}
573
574impl VMGlobalDefinition {
575    /// Construct a `VMGlobalDefinition`.
576    pub fn new() -> Self {
577        Self {
578            val: Default::default(),
579        }
580    }
581}
582
583/// A tag index, unique within the Store in which the instance was created.
584/// Usable for translating module-local tag indices to store-unique ones.
585#[repr(C)]
586#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
587pub struct VMSharedTagIndex(u32);
588
589impl VMSharedTagIndex {
590    /// Create a new `VMSharedTagIndex`.
591    pub fn new(value: u32) -> Self {
592        Self(value)
593    }
594
595    /// Get the inner value.
596    pub fn index(&self) -> u32 {
597        self.0
598    }
599}
600
601/// An index into the shared signature registry, usable for checking signatures
602/// at indirect calls.
603#[repr(C)]
604#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
605#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
606pub struct VMSignatureHash(u32);
607
608impl VMSignatureHash {
609    /// Create a new `VMSignatureHash`.
610    pub fn new(value: u32) -> Self {
611        Self(value)
612    }
613}
614
615/// The VM caller-checked "anyfunc" record, for caller-side signature checking.
616/// It consists of the actual function pointer and a signature id to be checked
617/// by the caller.
618#[derive(Debug, Clone, Copy)]
619#[repr(C)]
620pub struct VMCallerCheckedAnyfunc {
621    /// Function body.
622    pub func_ptr: *const VMFunctionBody,
623    /// Function signature id.
624    pub type_signature_hash: VMSignatureHash,
625    /// Function `VMContext` or host env.
626    pub vmctx: VMFunctionContext,
627    /// Address of the function call trampoline to invoke this function using
628    /// a dynamic argument list.
629    pub call_trampoline: VMTrampoline,
630    // If more elements are added here, remember to add offset_of tests below!
631}
632
633unsafe extern "C" fn null_call_trampoline(
634    _vmctx: *mut VMContext,
635    _callee: *const VMFunctionBody,
636    _values: *mut RawValue,
637) {
638    unreachable!("null funcref trampoline should never be invoked");
639}
640
641impl VMCallerCheckedAnyfunc {
642    /// Construct the sentinel value for an uninitialized `funcref` table entry.
643    pub fn null() -> Self {
644        Self {
645            func_ptr: ptr::null(),
646            type_signature_hash: VMSignatureHash(0),
647            vmctx: VMFunctionContext {
648                host_env: ptr::null_mut(),
649            },
650            call_trampoline: null_call_trampoline,
651        }
652    }
653}
654
655impl PartialEq for VMCallerCheckedAnyfunc {
656    fn eq(&self, other: &Self) -> bool {
657        self.func_ptr == other.func_ptr
658            && self.type_signature_hash == other.type_signature_hash
659            && self.vmctx == other.vmctx
660            && ptr::fn_addr_eq(self.call_trampoline, other.call_trampoline)
661    }
662}
663
664impl Eq for VMCallerCheckedAnyfunc {}
665
666impl Hash for VMCallerCheckedAnyfunc {
667    fn hash<H: Hasher>(&self, state: &mut H) {
668        self.func_ptr.hash(state);
669        self.type_signature_hash.hash(state);
670        self.vmctx.hash(state);
671        ptr::hash(self.call_trampoline as *const (), state);
672    }
673}
674
675#[cfg(test)]
676mod test_vmcaller_checked_anyfunc {
677    use super::VMCallerCheckedAnyfunc;
678    use crate::VMOffsets;
679    use core::mem::offset_of;
680    use std::mem::size_of;
681    use wasmer_types::ModuleInfo;
682
683    #[test]
684    fn check_vmcaller_checked_anyfunc_offsets() {
685        let module = ModuleInfo::new();
686        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
687        assert_eq!(
688            size_of::<VMCallerCheckedAnyfunc>(),
689            usize::from(offsets.size_of_vmcaller_checked_anyfunc())
690        );
691        assert_eq!(
692            offset_of!(VMCallerCheckedAnyfunc, func_ptr),
693            usize::from(offsets.vmcaller_checked_anyfunc_func_ptr())
694        );
695        assert_eq!(
696            offset_of!(VMCallerCheckedAnyfunc, type_signature_hash),
697            usize::from(offsets.vmcaller_checked_anyfunc_signature_hash())
698        );
699        assert_eq!(
700            offset_of!(VMCallerCheckedAnyfunc, vmctx),
701            usize::from(offsets.vmcaller_checked_anyfunc_vmctx())
702        );
703    }
704}
705
706/// An array that stores addresses of builtin functions. We translate code
707/// to use indirect calls. This way, we don't have to patch the code.
708#[repr(C)]
709pub struct VMBuiltinFunctionsArray {
710    ptrs: [usize; Self::len()],
711}
712
713impl VMBuiltinFunctionsArray {
714    pub const fn len() -> usize {
715        VMBuiltinFunctionIndex::builtin_functions_total_number() as usize
716    }
717
718    pub fn initialized() -> Self {
719        use crate::libcalls::*;
720
721        let mut ptrs = [0; Self::len()];
722
723        ptrs[VMBuiltinFunctionIndex::get_memory32_grow_index().index() as usize] =
724            wasmer_vm_memory32_grow as *const () as usize;
725        ptrs[VMBuiltinFunctionIndex::get_imported_memory32_grow_index().index() as usize] =
726            wasmer_vm_imported_memory32_grow as *const () as usize;
727        ptrs[VMBuiltinFunctionIndex::get_memory32_size_index().index() as usize] =
728            wasmer_vm_memory32_size as *const () as usize;
729        ptrs[VMBuiltinFunctionIndex::get_imported_memory32_size_index().index() as usize] =
730            wasmer_vm_imported_memory32_size as *const () as usize;
731        ptrs[VMBuiltinFunctionIndex::get_table_copy_index().index() as usize] =
732            wasmer_vm_table_copy as *const () as usize;
733        ptrs[VMBuiltinFunctionIndex::get_table_init_index().index() as usize] =
734            wasmer_vm_table_init as *const () as usize;
735        ptrs[VMBuiltinFunctionIndex::get_elem_drop_index().index() as usize] =
736            wasmer_vm_elem_drop as *const () as usize;
737        ptrs[VMBuiltinFunctionIndex::get_memory_copy_index().index() as usize] =
738            wasmer_vm_memory32_copy as *const () as usize;
739        ptrs[VMBuiltinFunctionIndex::get_memory_fill_index().index() as usize] =
740            wasmer_vm_memory32_fill as *const () as usize;
741        ptrs[VMBuiltinFunctionIndex::get_imported_memory_fill_index().index() as usize] =
742            wasmer_vm_imported_memory32_fill as *const () as usize;
743        ptrs[VMBuiltinFunctionIndex::get_memory_init_index().index() as usize] =
744            wasmer_vm_memory32_init as *const () as usize;
745        ptrs[VMBuiltinFunctionIndex::get_data_drop_index().index() as usize] =
746            wasmer_vm_data_drop as *const () as usize;
747        ptrs[VMBuiltinFunctionIndex::get_raise_trap_index().index() as usize] =
748            wasmer_vm_raise_trap as *const () as usize;
749        ptrs[VMBuiltinFunctionIndex::get_table_size_index().index() as usize] =
750            wasmer_vm_table_size as *const () as usize;
751        ptrs[VMBuiltinFunctionIndex::get_imported_table_size_index().index() as usize] =
752            wasmer_vm_imported_table_size as *const () as usize;
753        ptrs[VMBuiltinFunctionIndex::get_table_grow_index().index() as usize] =
754            wasmer_vm_table_grow as *const () as usize;
755        ptrs[VMBuiltinFunctionIndex::get_imported_table_grow_index().index() as usize] =
756            wasmer_vm_imported_table_grow as *const () as usize;
757        ptrs[VMBuiltinFunctionIndex::get_table_get_index().index() as usize] =
758            wasmer_vm_table_get as *const () as usize;
759        ptrs[VMBuiltinFunctionIndex::get_imported_table_get_index().index() as usize] =
760            wasmer_vm_imported_table_get as *const () as usize;
761        ptrs[VMBuiltinFunctionIndex::get_table_set_index().index() as usize] =
762            wasmer_vm_table_set as *const () as usize;
763        ptrs[VMBuiltinFunctionIndex::get_imported_table_set_index().index() as usize] =
764            wasmer_vm_imported_table_set as *const () as usize;
765        ptrs[VMBuiltinFunctionIndex::get_func_ref_index().index() as usize] =
766            wasmer_vm_func_ref as *const () as usize;
767        ptrs[VMBuiltinFunctionIndex::get_table_fill_index().index() as usize] =
768            wasmer_vm_table_fill as *const () as usize;
769        ptrs[VMBuiltinFunctionIndex::get_memory_atomic_wait32_index().index() as usize] =
770            wasmer_vm_memory32_atomic_wait32 as *const () as usize;
771        ptrs[VMBuiltinFunctionIndex::get_imported_memory_atomic_wait32_index().index() as usize] =
772            wasmer_vm_imported_memory32_atomic_wait32 as *const () as usize;
773        ptrs[VMBuiltinFunctionIndex::get_memory_atomic_wait64_index().index() as usize] =
774            wasmer_vm_memory32_atomic_wait64 as *const () as usize;
775        ptrs[VMBuiltinFunctionIndex::get_imported_memory_atomic_wait64_index().index() as usize] =
776            wasmer_vm_imported_memory32_atomic_wait64 as *const () as usize;
777        ptrs[VMBuiltinFunctionIndex::get_memory_atomic_notify_index().index() as usize] =
778            wasmer_vm_memory32_atomic_notify as *const () as usize;
779        ptrs[VMBuiltinFunctionIndex::get_imported_memory_atomic_notify_index().index() as usize] =
780            wasmer_vm_imported_memory32_atomic_notify as *const () as usize;
781        ptrs[VMBuiltinFunctionIndex::get_imported_debug_usize_index().index() as usize] =
782            wasmer_vm_dbg_usize as *const () as usize;
783        ptrs[VMBuiltinFunctionIndex::get_imported_debug_str_index().index() as usize] =
784            wasmer_vm_dbg_str as *const () as usize;
785        ptrs[VMBuiltinFunctionIndex::get_imported_personality2_index().index() as usize] =
786            wasmer_eh_personality2 as *const () as usize;
787        ptrs[VMBuiltinFunctionIndex::get_imported_alloc_exception_index().index() as usize] =
788            wasmer_vm_alloc_exception as *const () as usize;
789        ptrs[VMBuiltinFunctionIndex::get_imported_throw_index().index() as usize] =
790            wasmer_vm_throw as *const () as usize;
791        ptrs[VMBuiltinFunctionIndex::get_imported_read_exnref_index().index() as usize] =
792            wasmer_vm_read_exnref as *const () as usize;
793        ptrs[VMBuiltinFunctionIndex::get_imported_exception_into_exnref_index().index() as usize] =
794            wasmer_vm_exception_into_exnref as *const () as usize;
795
796        debug_assert!(ptrs.iter().cloned().all(|p| p != 0));
797
798        Self { ptrs }
799    }
800}
801
802/// The VM "context", which is pointed to by the `vmctx` arg in the compiler.
803/// This has information about globals, memories, tables, and other runtime
804/// state associated with the current instance.
805///
806/// The struct here is empty, as the sizes of these fields are dynamic, and
807/// we can't describe them in Rust's type system. Sufficient memory is
808/// allocated at runtime.
809///
810/// TODO: We could move the globals into the `vmctx` allocation too.
811#[derive(Debug)]
812#[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside
813pub struct VMContext {}
814
815impl VMContext {
816    /// Return a mutable reference to the associated `Instance`.
817    ///
818    /// # Safety
819    /// This is unsafe because it doesn't work on just any `VMContext`, it must
820    /// be a `VMContext` allocated as part of an `Instance`.
821    #[allow(clippy::cast_ptr_alignment)]
822    #[inline]
823    pub(crate) unsafe fn instance(&self) -> &Instance {
824        unsafe {
825            &*((self as *const Self as *mut u8).offset(-Instance::vmctx_offset())
826                as *const Instance)
827        }
828    }
829
830    #[inline]
831    pub(crate) unsafe fn instance_mut(&mut self) -> &mut Instance {
832        unsafe {
833            &mut *((self as *const Self as *mut u8).offset(-Instance::vmctx_offset())
834                as *mut Instance)
835        }
836    }
837}
838
839/// The type for tramplines in the VM.
840pub type VMTrampoline = unsafe extern "C" fn(
841    *mut VMContext,        // callee vmctx
842    *const VMFunctionBody, // function we're actually calling
843    *mut RawValue,         // space for arguments and return values
844);
845
846/// The fields compiled code needs to access to utilize a WebAssembly linear
847/// memory defined within the instance, namely the start address and the
848/// size in bytes.
849#[derive(Debug, Copy, Clone)]
850#[repr(C)]
851pub struct VMMemoryDefinition {
852    /// The start address which is always valid, even if the memory grows.
853    pub base: *mut u8,
854
855    /// The current logical size of this linear memory in bytes.
856    pub current_length: usize,
857}
858
859/// # Safety
860/// This data is safe to share between threads because it's plain data that
861/// is the user's responsibility to synchronize.
862unsafe impl Send for VMMemoryDefinition {}
863/// # Safety
864/// This data is safe to share between threads because it's plain data that
865/// is the user's responsibility to synchronize. And it's `Copy` so there's
866/// really no difference between passing it by reference or by value as far as
867/// correctness in a multi-threaded context is concerned.
868unsafe impl Sync for VMMemoryDefinition {}
869
870#[cfg(test)]
871mod test_vmmemory_definition {
872    use super::VMMemoryDefinition;
873    use crate::VMOffsets;
874    use core::mem::offset_of;
875    use std::mem::size_of;
876    use wasmer_types::ModuleInfo;
877
878    #[test]
879    fn check_vmmemory_definition_offsets() {
880        let module = ModuleInfo::new();
881        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
882        assert_eq!(
883            size_of::<VMMemoryDefinition>(),
884            usize::from(offsets.size_of_vmmemory_definition())
885        );
886        assert_eq!(
887            offset_of!(VMMemoryDefinition, base),
888            usize::from(offsets.vmmemory_definition_base())
889        );
890        assert_eq!(
891            offset_of!(VMMemoryDefinition, current_length),
892            usize::from(offsets.vmmemory_definition_current_length())
893        );
894    }
895}