near_vm_vm/
vmcontext.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/2.3.0/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::VMExternRef;
8use crate::func_data_registry::VMFuncRef;
9use crate::global::Global;
10use crate::instance::Instance;
11use crate::memory::LinearMemory;
12use crate::sig_registry::VMSharedSignatureIndex;
13use crate::table::Table;
14use crate::trap::{Trap, TrapCode};
15use std::any::Any;
16use std::convert::TryFrom;
17use std::fmt;
18use std::ptr::{self, NonNull};
19use std::sync::Arc;
20use std::u32;
21
22/// Union representing the first parameter passed when calling a function.
23///
24/// It may either be a pointer to the [`VMContext`] if it's a Wasm function
25/// or a pointer to arbitrary data controlled by the host if it's a host function.
26#[derive(Copy, Clone, Eq)]
27pub union VMFunctionEnvironment {
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 VMFunctionEnvironment {
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 VMFunctionEnvironment {
42    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
43        f.debug_struct("VMFunctionEnvironment")
44            .field("vmctx_or_hostenv", unsafe { &self.host_env })
45            .finish()
46    }
47}
48
49impl std::cmp::PartialEq for VMFunctionEnvironment {
50    fn eq(&self, rhs: &Self) -> bool {
51        unsafe { self.host_env as usize == rhs.host_env as usize }
52    }
53}
54
55impl std::hash::Hash for VMFunctionEnvironment {
56    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
57        unsafe {
58            self.vmctx.hash(state);
59        }
60    }
61}
62
63/// Represents a continuous region of executable memory starting with a function
64/// entry point.
65#[derive(Debug)]
66#[repr(C)]
67pub struct FunctionExtent {
68    /// Entry point for normal entry of the function. All addresses in the
69    /// function lie after this address.
70    pub address: FunctionBodyPtr,
71    /// Length in bytes.
72    pub length: usize,
73}
74
75/// An imported function.
76#[derive(Debug, Copy, Clone)]
77#[repr(C)]
78pub struct VMFunctionImport {
79    /// A pointer to the imported function body.
80    pub body: FunctionBodyPtr,
81
82    /// Function signature index within the source module.
83    pub signature: VMSharedSignatureIndex,
84
85    /// Function call trampoline
86    pub trampoline: Option<VMTrampoline>,
87
88    /// A pointer to the `VMContext` that owns the function or host env data.
89    pub environment: VMFunctionEnvironment,
90}
91
92#[cfg(test)]
93mod test_vmfunction_import {
94    use super::VMFunctionImport;
95    use crate::VMOffsets;
96    use memoffset::offset_of;
97    use near_vm_types::ModuleInfo;
98    use std::mem::size_of;
99
100    #[test]
101    fn check_vmfunction_import_offsets() {
102        let module = ModuleInfo::new();
103        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
104        assert_eq!(size_of::<VMFunctionImport>(), usize::from(offsets.size_of_vmfunction_import()));
105        assert_eq!(
106            offset_of!(VMFunctionImport, body),
107            usize::from(offsets.vmfunction_import_body())
108        );
109        assert_eq!(
110            offset_of!(VMFunctionImport, environment),
111            usize::from(offsets.vmfunction_import_vmctx())
112        );
113    }
114}
115
116/// A locally defined function.
117#[derive(Debug, Copy, Clone)]
118#[repr(C)]
119pub struct VMLocalFunction {
120    /// A pointer to the imported function body.
121    pub body: FunctionBodyPtr,
122
123    /// Length of the function code
124    pub length: u32,
125
126    /// Function signature
127    pub signature: VMSharedSignatureIndex,
128
129    /// Trampoline for host->VM function calls.
130    pub trampoline: VMTrampoline,
131}
132
133/// The `VMDynamicFunctionContext` is the context that dynamic
134/// functions will receive when called (rather than `vmctx`).
135/// A dynamic function is a function for which we don't know the signature
136/// until runtime.
137///
138/// As such, we need to expose the dynamic function `context`
139/// containing the relevant context for running the function indicated
140/// in `address`.
141#[repr(C)]
142pub struct VMDynamicFunctionContext<T: Sized + Send + Sync> {
143    /// The address of the inner dynamic function.
144    ///
145    /// Note: The function must be on the form of
146    /// `(*mut T, SignatureIndex, *mut i128)`.
147    pub address: *const VMFunctionBody,
148
149    /// The context that the inner dynamic function will receive.
150    pub ctx: T,
151}
152
153// The `ctx` itself must be `Send`, `address` can be passed between
154// threads because all usage is `unsafe` and synchronized.
155unsafe impl<T: Sized + Send + Sync> Send for VMDynamicFunctionContext<T> {}
156// The `ctx` itself must be `Sync`, `address` can be shared between
157// threads because all usage is `unsafe` and synchronized.
158unsafe impl<T: Sized + Send + Sync> Sync for VMDynamicFunctionContext<T> {}
159
160impl<T: Sized + Clone + Send + Sync> Clone for VMDynamicFunctionContext<T> {
161    fn clone(&self) -> Self {
162        Self { address: self.address, ctx: self.ctx.clone() }
163    }
164}
165
166#[cfg(test)]
167mod test_vmdynamicfunction_import_context {
168    use super::VMDynamicFunctionContext;
169    use crate::VMOffsets;
170    use memoffset::offset_of;
171    use near_vm_types::ModuleInfo;
172    use std::mem::size_of;
173
174    #[test]
175    fn check_vmdynamicfunction_import_context_offsets() {
176        let module = ModuleInfo::new();
177        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
178        assert_eq!(
179            size_of::<VMDynamicFunctionContext<usize>>(),
180            usize::from(offsets.size_of_vmdynamicfunction_import_context())
181        );
182        assert_eq!(
183            offset_of!(VMDynamicFunctionContext<usize>, address),
184            usize::from(offsets.vmdynamicfunction_import_context_address())
185        );
186        assert_eq!(
187            offset_of!(VMDynamicFunctionContext<usize>, ctx),
188            usize::from(offsets.vmdynamicfunction_import_context_ctx())
189        );
190    }
191}
192
193/// A placeholder byte-sized type which is just used to provide some amount of type
194/// safety when dealing with pointers to JIT-compiled function bodies. Note that it's
195/// deliberately not Copy, as we shouldn't be carelessly copying function body bytes
196/// around.
197#[repr(C)]
198pub struct VMFunctionBody(u8);
199
200#[cfg(test)]
201mod test_vmfunction_body {
202    use super::VMFunctionBody;
203    use std::mem::size_of;
204
205    #[test]
206    fn check_vmfunction_body_offsets() {
207        assert_eq!(size_of::<VMFunctionBody>(), 1);
208    }
209}
210
211/// A pointer to the beginning of the function body.
212#[derive(Clone, Copy, Debug)]
213#[repr(transparent)]
214pub struct FunctionBodyPtr(pub *const VMFunctionBody);
215
216impl std::ops::Deref for FunctionBodyPtr {
217    type Target = *const VMFunctionBody;
218
219    fn deref(&self) -> &Self::Target {
220        &self.0
221    }
222}
223
224// SAFETY: The VMFunctionBody that this points to is opaque, so there's no data to read or write
225// through this pointer. This is essentially a usize.
226unsafe impl Send for FunctionBodyPtr {}
227
228/// SAFETY: The VMFunctionBody that this points to is opaque, so there's no data to read or write
229/// through this pointer. This is essentially a usize.
230unsafe impl Sync for FunctionBodyPtr {}
231
232/// A function kind is a calling convention into and out of wasm code.
233#[derive(Debug, Copy, Clone, PartialEq)]
234#[repr(C)]
235pub enum VMFunctionKind {
236    /// A static function has the native signature:
237    /// `extern "C" (vmctx, arg1, arg2...) -> (result1, result2, ...)`.
238    ///
239    /// This is the default for functions that are defined:
240    /// 1. In the Host, natively
241    /// 2. In the WebAssembly file
242    Static,
243
244    /// A dynamic function has the native signature:
245    /// `extern "C" (ctx, &[Value]) -> Vec<Value>`.
246    ///
247    /// This is the default for functions that are defined:
248    /// 1. In the Host, dynamically
249    Dynamic,
250}
251
252/// The fields compiled code needs to access to utilize a WebAssembly table
253/// imported from another instance.
254#[derive(Debug, Clone)]
255#[repr(C)]
256pub struct VMTableImport {
257    /// A pointer to the imported table description.
258    pub definition: NonNull<VMTableDefinition>,
259
260    /// A pointer to the `Table` that owns the table description.
261    pub from: Arc<dyn Table>,
262}
263
264#[cfg(test)]
265mod test_vmtable_import {
266    use super::VMTableImport;
267    use crate::VMOffsets;
268    use memoffset::offset_of;
269    use near_vm_types::ModuleInfo;
270    use std::mem::size_of;
271
272    #[test]
273    fn check_vmtable_import_offsets() {
274        let module = ModuleInfo::new();
275        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
276        assert_eq!(size_of::<VMTableImport>(), usize::from(offsets.size_of_vmtable_import()));
277        assert_eq!(
278            offset_of!(VMTableImport, definition),
279            usize::from(offsets.vmtable_import_definition())
280        );
281        assert_eq!(offset_of!(VMTableImport, from), usize::from(offsets.vmtable_import_from()));
282    }
283}
284
285/// The fields compiled code needs to access to utilize a WebAssembly linear
286/// memory imported from another instance.
287#[derive(Debug, Clone)]
288#[repr(C)]
289pub struct VMMemoryImport {
290    /// A pointer to the imported memory description.
291    pub definition: NonNull<VMMemoryDefinition>,
292
293    /// A pointer to the `Memory` that owns the memory description.
294    pub from: Arc<LinearMemory>,
295}
296
297#[cfg(test)]
298mod test_vmmemory_import {
299    use super::VMMemoryImport;
300    use crate::VMOffsets;
301    use memoffset::offset_of;
302    use near_vm_types::ModuleInfo;
303    use std::mem::size_of;
304
305    #[test]
306    fn check_vmmemory_import_offsets() {
307        let module = ModuleInfo::new();
308        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
309        assert_eq!(size_of::<VMMemoryImport>(), usize::from(offsets.size_of_vmmemory_import()));
310        assert_eq!(
311            offset_of!(VMMemoryImport, definition),
312            usize::from(offsets.vmmemory_import_definition())
313        );
314        assert_eq!(offset_of!(VMMemoryImport, from), usize::from(offsets.vmmemory_import_from()));
315    }
316}
317
318/// The fields compiled code needs to access to utilize a WebAssembly global
319/// variable imported from another instance.
320#[derive(Debug, Clone)]
321#[repr(C)]
322pub struct VMGlobalImport {
323    /// A pointer to the imported global variable description.
324    pub definition: NonNull<VMGlobalDefinition>,
325
326    /// A pointer to the `Global` that owns the global description.
327    pub from: Arc<Global>,
328}
329
330/// # Safety
331/// This data is safe to share between threads because it's plain data that
332/// is the user's responsibility to synchronize. Additionally, all operations
333/// on `from` are thread-safe through the use of a mutex in [`Global`].
334unsafe impl Send for VMGlobalImport {}
335/// # Safety
336/// This data is safe to share between threads because it's plain data that
337/// is the user's responsibility to synchronize. And because it's `Clone`, there's
338/// really no difference between passing it by reference or by value as far as
339/// correctness in a multi-threaded context is concerned.
340unsafe impl Sync for VMGlobalImport {}
341
342#[cfg(test)]
343mod test_vmglobal_import {
344    use super::VMGlobalImport;
345    use crate::VMOffsets;
346    use memoffset::offset_of;
347    use near_vm_types::ModuleInfo;
348    use std::mem::size_of;
349
350    #[test]
351    fn check_vmglobal_import_offsets() {
352        let module = ModuleInfo::new();
353        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
354        assert_eq!(size_of::<VMGlobalImport>(), usize::from(offsets.size_of_vmglobal_import()));
355        assert_eq!(
356            offset_of!(VMGlobalImport, definition),
357            usize::from(offsets.vmglobal_import_definition())
358        );
359        assert_eq!(offset_of!(VMGlobalImport, from), usize::from(offsets.vmglobal_import_from()));
360    }
361}
362
363/// The fields compiled code needs to access to utilize a WebAssembly linear
364/// memory defined within the instance, namely the start address and the
365/// size in bytes.
366#[derive(Debug, Copy, Clone)]
367#[repr(C)]
368pub struct VMMemoryDefinition {
369    /// The start address which is always valid, even if the memory grows.
370    pub base: *mut u8,
371
372    /// The current logical size of this linear memory in bytes.
373    pub current_length: usize,
374}
375
376/// # Safety
377/// This data is safe to share between threads because it's plain data that
378/// is the user's responsibility to synchronize.
379unsafe impl Send for VMMemoryDefinition {}
380/// # Safety
381/// This data is safe to share between threads because it's plain data that
382/// is the user's responsibility to synchronize. And it's `Copy` so there's
383/// really no difference between passing it by reference or by value as far as
384/// correctness in a multi-threaded context is concerned.
385unsafe impl Sync for VMMemoryDefinition {}
386
387impl VMMemoryDefinition {
388    /// Do an unsynchronized, non-atomic `memory.copy` for the memory.
389    ///
390    /// # Errors
391    ///
392    /// Returns a `Trap` error when the source or destination ranges are out of
393    /// bounds.
394    ///
395    /// # Safety
396    ///
397    /// The memory is not copied atomically and is not synchronized: it's the
398    /// caller's responsibility to synchronize.
399    pub(crate) unsafe fn memory_copy(&self, dst: u32, src: u32, len: u32) -> Result<(), Trap> {
400        // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
401        if src.checked_add(len).map_or(true, |n| usize::try_from(n).unwrap() > self.current_length)
402            || dst
403                .checked_add(len)
404                .map_or(true, |m| usize::try_from(m).unwrap() > self.current_length)
405        {
406            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
407        }
408
409        let dst = usize::try_from(dst).unwrap();
410        let src = usize::try_from(src).unwrap();
411
412        // SAFE: Bounds and casts are checked above.
413        unsafe {
414            let dst = self.base.add(dst);
415            let src = self.base.add(src);
416            ptr::copy(src, dst, len as usize);
417        }
418
419        Ok(())
420    }
421
422    /// Perform the `memory.fill` operation for the memory in an unsynchronized,
423    /// non-atomic way.
424    ///
425    /// # Errors
426    ///
427    /// Returns a `Trap` error if the memory range is out of bounds.
428    ///
429    /// # Safety
430    /// The memory is not filled atomically and is not synchronized: it's the
431    /// caller's responsibility to synchronize.
432    pub(crate) unsafe fn memory_fill(&self, dst: u32, val: u32, len: u32) -> Result<(), Trap> {
433        if dst.checked_add(len).map_or(true, |m| usize::try_from(m).unwrap() > self.current_length)
434        {
435            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
436        }
437
438        let dst = isize::try_from(dst).unwrap();
439        let val = val as u8;
440
441        // SAFE: Bounds and casts are checked above.
442        unsafe {
443            let dst = self.base.offset(dst);
444            ptr::write_bytes(dst, val, len as usize);
445        }
446
447        Ok(())
448    }
449}
450
451#[cfg(test)]
452mod test_vmmemory_definition {
453    use super::VMMemoryDefinition;
454    use crate::VMOffsets;
455    use memoffset::offset_of;
456    use near_vm_types::ModuleInfo;
457    use std::mem::size_of;
458
459    #[test]
460    fn check_vmmemory_definition_offsets() {
461        let module = ModuleInfo::new();
462        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
463        assert_eq!(
464            size_of::<VMMemoryDefinition>(),
465            usize::from(offsets.size_of_vmmemory_definition())
466        );
467        assert_eq!(
468            offset_of!(VMMemoryDefinition, base),
469            usize::from(offsets.vmmemory_definition_base())
470        );
471        assert_eq!(
472            offset_of!(VMMemoryDefinition, current_length),
473            usize::from(offsets.vmmemory_definition_current_length())
474        );
475    }
476}
477
478/// The fields compiled code needs to access to utilize a WebAssembly table
479/// defined within the instance.
480#[derive(Debug, Clone, Copy)]
481#[repr(C)]
482pub struct VMTableDefinition {
483    /// Pointer to the table data.
484    pub base: *mut u8,
485
486    /// The current number of elements in the table.
487    pub current_elements: u32,
488}
489
490#[cfg(test)]
491mod test_vmtable_definition {
492    use super::VMTableDefinition;
493    use crate::VMOffsets;
494    use memoffset::offset_of;
495    use near_vm_types::ModuleInfo;
496    use std::mem::size_of;
497
498    #[test]
499    fn check_vmtable_definition_offsets() {
500        let module = ModuleInfo::new();
501        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
502        assert_eq!(
503            size_of::<VMTableDefinition>(),
504            usize::from(offsets.size_of_vmtable_definition())
505        );
506        assert_eq!(
507            offset_of!(VMTableDefinition, base),
508            usize::from(offsets.vmtable_definition_base())
509        );
510        assert_eq!(
511            offset_of!(VMTableDefinition, current_elements),
512            usize::from(offsets.vmtable_definition_current_elements())
513        );
514    }
515}
516
517/// A typesafe wrapper around the storage for a global variables.
518///
519/// # Safety
520///
521/// Accessing the different members of this union is always safe because there
522/// are no invalid values for any of the types and the whole object is
523/// initialized by VMGlobalDefinition::new().
524#[derive(Clone, Copy)]
525#[repr(C, align(16))]
526pub union VMGlobalDefinitionStorage {
527    as_i32: i32,
528    as_u32: u32,
529    as_f32: f32,
530    as_i64: i64,
531    as_u64: u64,
532    as_f64: f64,
533    as_u128: u128,
534    as_funcref: VMFuncRef,
535    as_externref: VMExternRef,
536    bytes: [u8; 16],
537}
538
539impl fmt::Debug for VMGlobalDefinitionStorage {
540    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
541        f.debug_struct("VMGlobalDefinitionStorage").field("bytes", unsafe { &self.bytes }).finish()
542    }
543}
544
545/// The storage for a WebAssembly global defined within the instance.
546///
547/// TODO: Pack the globals more densely, rather than using the same size
548/// for every type.
549#[derive(Debug, Clone)]
550#[repr(C, align(16))]
551pub struct VMGlobalDefinition {
552    storage: VMGlobalDefinitionStorage,
553    // If more elements are added here, remember to add offset_of tests below!
554}
555
556#[cfg(test)]
557mod test_vmglobal_definition {
558    use super::VMGlobalDefinition;
559    use crate::{VMFuncRef, VMOffsets};
560    use more_asserts::assert_ge;
561    use near_vm_types::ModuleInfo;
562    use std::mem::{align_of, size_of};
563
564    #[test]
565    fn check_vmglobal_definition_alignment() {
566        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<i32>());
567        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<i64>());
568        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<f32>());
569        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<f64>());
570        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<VMFuncRef>());
571        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<[u8; 16]>());
572    }
573
574    #[test]
575    fn check_vmglobal_definition_offsets() {
576        let module = ModuleInfo::new();
577        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
578        assert_eq!(
579            size_of::<*const VMGlobalDefinition>(),
580            usize::from(offsets.size_of_vmglobal_local())
581        );
582    }
583
584    #[test]
585    fn check_vmglobal_begins_aligned() {
586        let module = ModuleInfo::new();
587        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
588        assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
589    }
590}
591
592impl VMGlobalDefinition {
593    /// Construct a `VMGlobalDefinition`.
594    pub fn new() -> Self {
595        Self { storage: VMGlobalDefinitionStorage { bytes: [0; 16] } }
596    }
597
598    /// Return the value as an i32.
599    ///
600    /// If this is not an I32 typed global it is unspecified what value is returned.
601    pub unsafe fn to_i32(&self) -> i32 {
602        unsafe { self.storage.as_i32 }
603    }
604
605    /// Return a mutable reference to the value as an i32.
606    ///
607    /// # Safety
608    ///
609    /// It is the callers responsibility to make sure the global has I32 type.
610    /// Until the returned borrow is dropped, reads and writes of this global
611    /// must be done exclusively through this borrow. That includes reads and
612    /// writes of globals inside wasm functions.
613    pub unsafe fn as_i32_mut(&mut self) -> &mut i32 {
614        unsafe { &mut self.storage.as_i32 }
615    }
616
617    /// Return a reference to the value as an u32.
618    ///
619    /// If this is not an I32 typed global it is unspecified what value is returned.
620    pub unsafe fn to_u32(&self) -> u32 {
621        unsafe { self.storage.as_u32 }
622    }
623
624    /// Return a mutable reference to the value as an u32.
625    ///
626    /// # Safety
627    ///
628    /// It is the callers responsibility to make sure the global has I32 type.
629    /// Until the returned borrow is dropped, reads and writes of this global
630    /// must be done exclusively through this borrow. That includes reads and
631    /// writes of globals inside wasm functions.
632    pub unsafe fn as_u32_mut(&mut self) -> &mut u32 {
633        unsafe { &mut self.storage.as_u32 }
634    }
635
636    /// Return a reference to the value as an i64.
637    ///
638    /// If this is not an I64 typed global it is unspecified what value is returned.
639    pub unsafe fn to_i64(&self) -> i64 {
640        unsafe { self.storage.as_i64 }
641    }
642
643    /// Return a mutable reference to the value as an i64.
644    ///
645    /// # Safety
646    ///
647    /// It is the callers responsibility to make sure the global has I64 type.
648    /// Until the returned borrow is dropped, reads and writes of this global
649    /// must be done exclusively through this borrow. That includes reads and
650    /// writes of globals inside wasm functions.
651    pub unsafe fn as_i64_mut(&mut self) -> &mut i64 {
652        unsafe { &mut self.storage.as_i64 }
653    }
654
655    /// Return a reference to the value as an u64.
656    ///
657    /// If this is not an I64 typed global it is unspecified what value is returned.
658    pub unsafe fn to_u64(&self) -> u64 {
659        unsafe { self.storage.as_u64 }
660    }
661
662    /// Return a mutable reference to the value as an u64.
663    ///
664    /// # Safety
665    ///
666    /// It is the callers responsibility to make sure the global has I64 type.
667    /// Until the returned borrow is dropped, reads and writes of this global
668    /// must be done exclusively through this borrow. That includes reads and
669    /// writes of globals inside wasm functions.
670    pub unsafe fn as_u64_mut(&mut self) -> &mut u64 {
671        unsafe { &mut self.storage.as_u64 }
672    }
673
674    /// Return a reference to the value as an f32.
675    ///
676    /// If this is not an F32 typed global it is unspecified what value is returned.
677    pub unsafe fn to_f32(&self) -> f32 {
678        unsafe { self.storage.as_f32 }
679    }
680
681    /// Return a mutable reference to the value as an f32.
682    ///
683    /// # Safety
684    ///
685    /// It is the callers responsibility to make sure the global has F32 type.
686    /// Until the returned borrow is dropped, reads and writes of this global
687    /// must be done exclusively through this borrow. That includes reads and
688    /// writes of globals inside wasm functions.
689    pub unsafe fn as_f32_mut(&mut self) -> &mut f32 {
690        unsafe { &mut self.storage.as_f32 }
691    }
692
693    /// Return a reference to the value as an f64.
694    ///
695    /// If this is not an F64 typed global it is unspecified what value is returned.
696    pub unsafe fn to_f64(&self) -> f64 {
697        unsafe { self.storage.as_f64 }
698    }
699
700    /// Return a mutable reference to the value as an f64.
701    ///
702    /// # Safety
703    ///
704    /// It is the callers responsibility to make sure the global has F64 type.
705    /// Until the returned borrow is dropped, reads and writes of this global
706    /// must be done exclusively through this borrow. That includes reads and
707    /// writes of globals inside wasm functions.
708    pub unsafe fn as_f64_mut(&mut self) -> &mut f64 {
709        unsafe { &mut self.storage.as_f64 }
710    }
711
712    /// Return a reference to the value as a `VMFuncRef`.
713    ///
714    /// If this is not a `VMFuncRef` typed global it is unspecified what value is returned.
715    pub unsafe fn to_funcref(&self) -> VMFuncRef {
716        unsafe { self.storage.as_funcref }
717    }
718
719    /// Return a mutable reference to the value as a `VMFuncRef`.
720    ///
721    /// # Safety
722    ///
723    /// It is the callers responsibility to make sure the global has `VMFuncRef` type.
724    /// Until the returned borrow is dropped, reads and writes of this global
725    /// must be done exclusively through this borrow. That includes reads and
726    /// writes of globals inside wasm functions.
727    pub unsafe fn as_funcref_mut(&mut self) -> &mut VMFuncRef {
728        unsafe { &mut self.storage.as_funcref }
729    }
730
731    /// Return a mutable reference to the value as an `VMExternRef`.
732    ///
733    /// # Safety
734    ///
735    /// It is the callers responsibility to make sure the global has I32 type.
736    /// Until the returned borrow is dropped, reads and writes of this global
737    /// must be done exclusively through this borrow. That includes reads and
738    /// writes of globals inside wasm functions.
739    pub unsafe fn as_externref_mut(&mut self) -> &mut VMExternRef {
740        unsafe { &mut self.storage.as_externref }
741    }
742
743    /// Return a reference to the value as an `VMExternRef`.
744    ///
745    /// If this is not an I64 typed global it is unspecified what value is returned.
746    pub unsafe fn to_externref(&self) -> VMExternRef {
747        unsafe { self.storage.as_externref }
748    }
749
750    /// Return a reference to the value as an u128.
751    ///
752    /// If this is not an V128 typed global it is unspecified what value is returned.
753    pub unsafe fn to_u128(&self) -> u128 {
754        unsafe { self.storage.as_u128 }
755    }
756
757    /// Return a mutable reference to the value as an u128.
758    ///
759    /// # Safety
760    ///
761    /// It is the callers responsibility to make sure the global has V128 type.
762    /// Until the returned borrow is dropped, reads and writes of this global
763    /// must be done exclusively through this borrow. That includes reads and
764    /// writes of globals inside wasm functions.
765    pub unsafe fn as_u128_mut(&mut self) -> &mut u128 {
766        unsafe { &mut self.storage.as_u128 }
767    }
768
769    /// Return a reference to the value as bytes.
770    pub unsafe fn to_bytes(&self) -> [u8; 16] {
771        unsafe { self.storage.bytes }
772    }
773
774    /// Return a mutable reference to the value as bytes.
775    ///
776    /// # Safety
777    ///
778    /// Until the returned borrow is dropped, reads and writes of this global
779    /// must be done exclusively through this borrow. That includes reads and
780    /// writes of globals inside wasm functions.
781    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8; 16] {
782        unsafe { &mut self.storage.bytes }
783    }
784}
785
786#[cfg(test)]
787mod test_vmshared_signature_index {
788    use super::VMSharedSignatureIndex;
789    use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets};
790    use near_vm_types::ModuleInfo;
791    use std::mem::size_of;
792
793    #[test]
794    fn check_vmshared_signature_index() {
795        let module = ModuleInfo::new();
796        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
797        assert_eq!(
798            size_of::<VMSharedSignatureIndex>(),
799            usize::from(offsets.size_of_vmshared_signature_index())
800        );
801    }
802
803    #[test]
804    fn check_target_shared_signature_index() {
805        assert_eq!(size_of::<VMSharedSignatureIndex>(), size_of::<TargetSharedSignatureIndex>());
806    }
807}
808
809/// The VM caller-checked "anyfunc" record, for caller-side signature checking.
810/// It consists of the actual function pointer and a signature id to be checked
811/// by the caller.
812#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
813#[repr(C)]
814pub struct VMCallerCheckedAnyfunc {
815    /// Function body.
816    pub func_ptr: *const VMFunctionBody,
817    /// Function signature id.
818    pub type_index: VMSharedSignatureIndex,
819    /// Function `VMContext` or host env.
820    pub vmctx: VMFunctionEnvironment,
821    // If more elements are added here, remember to add offset_of tests below!
822}
823
824#[cfg(test)]
825mod test_vmcaller_checked_anyfunc {
826    use super::VMCallerCheckedAnyfunc;
827    use crate::VMOffsets;
828    use memoffset::offset_of;
829    use near_vm_types::ModuleInfo;
830    use std::mem::size_of;
831
832    #[test]
833    fn check_vmcaller_checked_anyfunc_offsets() {
834        let module = ModuleInfo::new();
835        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
836        assert_eq!(
837            size_of::<VMCallerCheckedAnyfunc>(),
838            usize::from(offsets.size_of_vmcaller_checked_anyfunc())
839        );
840        assert_eq!(
841            offset_of!(VMCallerCheckedAnyfunc, func_ptr),
842            usize::from(offsets.vmcaller_checked_anyfunc_func_ptr())
843        );
844        assert_eq!(
845            offset_of!(VMCallerCheckedAnyfunc, type_index),
846            usize::from(offsets.vmcaller_checked_anyfunc_type_index())
847        );
848        assert_eq!(
849            offset_of!(VMCallerCheckedAnyfunc, vmctx),
850            usize::from(offsets.vmcaller_checked_anyfunc_vmctx())
851        );
852    }
853}
854
855/// An index type for builtin functions.
856#[derive(Copy, Clone, Debug)]
857pub struct VMBuiltinFunctionIndex(u32);
858
859impl VMBuiltinFunctionIndex {
860    /// Returns an index for wasm's `memory.grow` builtin function.
861    pub const fn get_memory32_grow_index() -> Self {
862        Self(0)
863    }
864    /// Returns an index for wasm's imported `memory.grow` builtin function.
865    pub const fn get_imported_memory32_grow_index() -> Self {
866        Self(1)
867    }
868    /// Returns an index for wasm's `memory.size` builtin function.
869    pub const fn get_memory32_size_index() -> Self {
870        Self(2)
871    }
872    /// Returns an index for wasm's imported `memory.size` builtin function.
873    pub const fn get_imported_memory32_size_index() -> Self {
874        Self(3)
875    }
876    /// Returns an index for wasm's `table.copy` when both tables are locally
877    /// defined.
878    pub const fn get_table_copy_index() -> Self {
879        Self(4)
880    }
881    /// Returns an index for wasm's `table.init`.
882    pub const fn get_table_init_index() -> Self {
883        Self(5)
884    }
885    /// Returns an index for wasm's `elem.drop`.
886    pub const fn get_elem_drop_index() -> Self {
887        Self(6)
888    }
889    /// Returns an index for wasm's `memory.copy` for locally defined memories.
890    pub const fn get_memory_copy_index() -> Self {
891        Self(7)
892    }
893    /// Returns an index for wasm's `memory.copy` for imported memories.
894    pub const fn get_imported_memory_copy_index() -> Self {
895        Self(8)
896    }
897    /// Returns an index for wasm's `memory.fill` for locally defined memories.
898    pub const fn get_memory_fill_index() -> Self {
899        Self(9)
900    }
901    /// Returns an index for wasm's `memory.fill` for imported memories.
902    pub const fn get_imported_memory_fill_index() -> Self {
903        Self(10)
904    }
905    /// Returns an index for wasm's `memory.init` instruction.
906    pub const fn get_memory_init_index() -> Self {
907        Self(11)
908    }
909    /// Returns an index for wasm's `data.drop` instruction.
910    pub const fn get_data_drop_index() -> Self {
911        Self(12)
912    }
913    /// Returns an index for wasm's `raise_trap` instruction.
914    pub const fn get_raise_trap_index() -> Self {
915        Self(13)
916    }
917    /// Returns an index for wasm's `table.size` instruction for local tables.
918    pub const fn get_table_size_index() -> Self {
919        Self(14)
920    }
921    /// Returns an index for wasm's `table.size` instruction for imported tables.
922    pub const fn get_imported_table_size_index() -> Self {
923        Self(15)
924    }
925    /// Returns an index for wasm's `table.grow` instruction for local tables.
926    pub const fn get_table_grow_index() -> Self {
927        Self(16)
928    }
929    /// Returns an index for wasm's `table.grow` instruction for imported tables.
930    pub const fn get_imported_table_grow_index() -> Self {
931        Self(17)
932    }
933    /// Returns an index for wasm's `table.get` instruction for local tables.
934    pub const fn get_table_get_index() -> Self {
935        Self(18)
936    }
937    /// Returns an index for wasm's `table.get` instruction for imported tables.
938    pub const fn get_imported_table_get_index() -> Self {
939        Self(19)
940    }
941    /// Returns an index for wasm's `table.set` instruction for local tables.
942    pub const fn get_table_set_index() -> Self {
943        Self(20)
944    }
945    /// Returns an index for wasm's `table.set` instruction for imported tables.
946    pub const fn get_imported_table_set_index() -> Self {
947        Self(21)
948    }
949    /// Returns an index for wasm's `func.ref` instruction.
950    pub const fn get_func_ref_index() -> Self {
951        Self(22)
952    }
953    /// Returns an index for wasm's `table.fill` instruction for local tables.
954    pub const fn get_table_fill_index() -> Self {
955        Self(23)
956    }
957    /// Returns an index for a function to increment the externref count.
958    pub const fn get_externref_inc_index() -> Self {
959        Self(24)
960    }
961    /// Returns an index for a function to decrement the externref count.
962    pub const fn get_externref_dec_index() -> Self {
963        Self(25)
964    }
965    /// Returns the total number of builtin functions.
966    pub const fn builtin_functions_total_number() -> u32 {
967        26
968    }
969
970    /// Return the index as an u32 number.
971    pub const fn index(self) -> u32 {
972        self.0
973    }
974}
975
976/// An array that stores addresses of builtin functions. We translate code
977/// to use indirect calls. This way, we don't have to patch the code.
978#[repr(C)]
979pub struct VMBuiltinFunctionsArray {
980    ptrs: [usize; Self::len()],
981}
982
983impl VMBuiltinFunctionsArray {
984    pub const fn len() -> usize {
985        VMBuiltinFunctionIndex::builtin_functions_total_number() as usize
986    }
987
988    pub fn initialized() -> Self {
989        use crate::libcalls::*;
990
991        let mut ptrs = [0; Self::len()];
992
993        ptrs[VMBuiltinFunctionIndex::get_memory32_grow_index().index() as usize] =
994            near_vm_memory32_grow as usize;
995        ptrs[VMBuiltinFunctionIndex::get_imported_memory32_grow_index().index() as usize] =
996            near_vm_imported_memory32_grow as usize;
997
998        ptrs[VMBuiltinFunctionIndex::get_memory32_size_index().index() as usize] =
999            near_vm_memory32_size as usize;
1000        ptrs[VMBuiltinFunctionIndex::get_imported_memory32_size_index().index() as usize] =
1001            near_vm_imported_memory32_size as usize;
1002
1003        ptrs[VMBuiltinFunctionIndex::get_table_copy_index().index() as usize] =
1004            near_vm_table_copy as usize;
1005
1006        ptrs[VMBuiltinFunctionIndex::get_table_init_index().index() as usize] =
1007            near_vm_table_init as usize;
1008        ptrs[VMBuiltinFunctionIndex::get_elem_drop_index().index() as usize] =
1009            near_vm_elem_drop as usize;
1010
1011        ptrs[VMBuiltinFunctionIndex::get_memory_copy_index().index() as usize] =
1012            near_vm_memory32_copy as usize;
1013        ptrs[VMBuiltinFunctionIndex::get_imported_memory_copy_index().index() as usize] =
1014            near_vm_imported_memory32_copy as usize;
1015        ptrs[VMBuiltinFunctionIndex::get_memory_fill_index().index() as usize] =
1016            near_vm_memory32_fill as usize;
1017        ptrs[VMBuiltinFunctionIndex::get_imported_memory_fill_index().index() as usize] =
1018            near_vm_imported_memory32_fill as usize;
1019        ptrs[VMBuiltinFunctionIndex::get_memory_init_index().index() as usize] =
1020            near_vm_memory32_init as usize;
1021        ptrs[VMBuiltinFunctionIndex::get_data_drop_index().index() as usize] =
1022            near_vm_data_drop as usize;
1023        ptrs[VMBuiltinFunctionIndex::get_raise_trap_index().index() as usize] =
1024            near_vm_raise_trap as usize;
1025        ptrs[VMBuiltinFunctionIndex::get_table_size_index().index() as usize] =
1026            near_vm_table_size as usize;
1027        ptrs[VMBuiltinFunctionIndex::get_imported_table_size_index().index() as usize] =
1028            near_vm_imported_table_size as usize;
1029        ptrs[VMBuiltinFunctionIndex::get_table_grow_index().index() as usize] =
1030            near_vm_table_grow as usize;
1031        ptrs[VMBuiltinFunctionIndex::get_imported_table_grow_index().index() as usize] =
1032            near_vm_imported_table_grow as usize;
1033        ptrs[VMBuiltinFunctionIndex::get_table_get_index().index() as usize] =
1034            near_vm_table_get as usize;
1035        ptrs[VMBuiltinFunctionIndex::get_imported_table_get_index().index() as usize] =
1036            near_vm_imported_table_get as usize;
1037        ptrs[VMBuiltinFunctionIndex::get_table_set_index().index() as usize] =
1038            near_vm_table_set as usize;
1039        ptrs[VMBuiltinFunctionIndex::get_imported_table_set_index().index() as usize] =
1040            near_vm_imported_table_set as usize;
1041        ptrs[VMBuiltinFunctionIndex::get_func_ref_index().index() as usize] =
1042            near_vm_func_ref as usize;
1043        ptrs[VMBuiltinFunctionIndex::get_table_fill_index().index() as usize] =
1044            near_vm_table_fill as usize;
1045        ptrs[VMBuiltinFunctionIndex::get_externref_inc_index().index() as usize] =
1046            near_vm_externref_inc as usize;
1047        ptrs[VMBuiltinFunctionIndex::get_externref_dec_index().index() as usize] =
1048            near_vm_externref_dec as usize;
1049
1050        debug_assert!(ptrs.iter().cloned().all(|p| p != 0));
1051
1052        Self { ptrs }
1053    }
1054}
1055
1056/// The VM "context", which is pointed to by the `vmctx` arg in the compiler.
1057/// This has information about globals, memories, tables, and other runtime
1058/// state associated with the current instance.
1059///
1060/// The struct here is empty, as the sizes of these fields are dynamic, and
1061/// we can't describe them in Rust's type system. Sufficient memory is
1062/// allocated at runtime.
1063///
1064/// TODO: We could move the globals into the `vmctx` allocation too.
1065#[derive(Debug)]
1066#[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside
1067pub struct VMContext {}
1068
1069impl VMContext {
1070    /// Return a mutable reference to the associated `Instance`.
1071    ///
1072    /// # Safety
1073    /// This is unsafe because it doesn't work on just any `VMContext`, it must
1074    /// be a `VMContext` allocated as part of an `Instance`.
1075    #[allow(clippy::cast_ptr_alignment)]
1076    #[inline]
1077    pub unsafe fn instance(&self) -> &Instance {
1078        unsafe { &*((self as *const Self as *mut u8).offset(-Instance::vmctx_offset()).cast()) }
1079    }
1080
1081    /// Return a reference to the host state associated with this `Instance`.
1082    ///
1083    /// # Safety
1084    /// This is unsafe because it doesn't work on just any `VMContext`, it must
1085    /// be a `VMContext` allocated as part of an `Instance`.
1086    #[inline]
1087    pub unsafe fn host_state(&self) -> &dyn Any {
1088        unsafe { self.instance().host_state() }
1089    }
1090}
1091
1092/// Signature/ABI of the trampoline.
1093pub type VMTrampoline = unsafe extern "C" fn(
1094    *mut VMContext,        // callee vmctx
1095    *const VMFunctionBody, // function we're actually calling
1096    *mut u128,             // space for arguments and return values
1097);
1098
1099/// Pointers to section data.
1100#[derive(Clone, Copy, Debug)]
1101#[repr(transparent)]
1102pub struct SectionBodyPtr(pub *const u8);
1103
1104impl std::ops::Deref for SectionBodyPtr {
1105    type Target = *const u8;
1106
1107    fn deref(&self) -> &Self::Target {
1108        &self.0
1109    }
1110}