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::func_data_registry::VMFuncRef;
8use crate::global::Global;
9use crate::instance::Instance;
10use crate::memory::LinearMemory;
11use crate::sig_registry::VMSharedSignatureIndex;
12use crate::table::Table;
13use crate::trap::{Trap, TrapCode};
14use crate::VMExternRef;
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        // Bounds and casts are checked above, by this point we know that
413        // everything is safe.
414        let dst = self.base.add(dst);
415        let src = self.base.add(src);
416        ptr::copy(src, dst, len as usize);
417
418        Ok(())
419    }
420
421    /// Perform the `memory.fill` operation for the memory in an unsynchronized,
422    /// non-atomic way.
423    ///
424    /// # Errors
425    ///
426    /// Returns a `Trap` error if the memory range is out of bounds.
427    ///
428    /// # Safety
429    /// The memory is not filled atomically and is not synchronized: it's the
430    /// caller's responsibility to synchronize.
431    pub(crate) unsafe fn memory_fill(&self, dst: u32, val: u32, len: u32) -> Result<(), Trap> {
432        if dst.checked_add(len).map_or(true, |m| usize::try_from(m).unwrap() > self.current_length)
433        {
434            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
435        }
436
437        let dst = isize::try_from(dst).unwrap();
438        let val = val as u8;
439
440        // Bounds and casts are checked above, by this point we know that
441        // everything is safe.
442        let dst = self.base.offset(dst);
443        ptr::write_bytes(dst, val, len as usize);
444
445        Ok(())
446    }
447}
448
449#[cfg(test)]
450mod test_vmmemory_definition {
451    use super::VMMemoryDefinition;
452    use crate::VMOffsets;
453    use memoffset::offset_of;
454    use near_vm_types::ModuleInfo;
455    use std::mem::size_of;
456
457    #[test]
458    fn check_vmmemory_definition_offsets() {
459        let module = ModuleInfo::new();
460        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
461        assert_eq!(
462            size_of::<VMMemoryDefinition>(),
463            usize::from(offsets.size_of_vmmemory_definition())
464        );
465        assert_eq!(
466            offset_of!(VMMemoryDefinition, base),
467            usize::from(offsets.vmmemory_definition_base())
468        );
469        assert_eq!(
470            offset_of!(VMMemoryDefinition, current_length),
471            usize::from(offsets.vmmemory_definition_current_length())
472        );
473    }
474}
475
476/// The fields compiled code needs to access to utilize a WebAssembly table
477/// defined within the instance.
478#[derive(Debug, Clone, Copy)]
479#[repr(C)]
480pub struct VMTableDefinition {
481    /// Pointer to the table data.
482    pub base: *mut u8,
483
484    /// The current number of elements in the table.
485    pub current_elements: u32,
486}
487
488#[cfg(test)]
489mod test_vmtable_definition {
490    use super::VMTableDefinition;
491    use crate::VMOffsets;
492    use memoffset::offset_of;
493    use near_vm_types::ModuleInfo;
494    use std::mem::size_of;
495
496    #[test]
497    fn check_vmtable_definition_offsets() {
498        let module = ModuleInfo::new();
499        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
500        assert_eq!(
501            size_of::<VMTableDefinition>(),
502            usize::from(offsets.size_of_vmtable_definition())
503        );
504        assert_eq!(
505            offset_of!(VMTableDefinition, base),
506            usize::from(offsets.vmtable_definition_base())
507        );
508        assert_eq!(
509            offset_of!(VMTableDefinition, current_elements),
510            usize::from(offsets.vmtable_definition_current_elements())
511        );
512    }
513}
514
515/// A typesafe wrapper around the storage for a global variables.
516///
517/// # Safety
518///
519/// Accessing the different members of this union is always safe because there
520/// are no invalid values for any of the types and the whole object is
521/// initialized by VMGlobalDefinition::new().
522#[derive(Clone, Copy)]
523#[repr(C, align(16))]
524pub union VMGlobalDefinitionStorage {
525    as_i32: i32,
526    as_u32: u32,
527    as_f32: f32,
528    as_i64: i64,
529    as_u64: u64,
530    as_f64: f64,
531    as_u128: u128,
532    as_funcref: VMFuncRef,
533    as_externref: VMExternRef,
534    bytes: [u8; 16],
535}
536
537impl fmt::Debug for VMGlobalDefinitionStorage {
538    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
539        f.debug_struct("VMGlobalDefinitionStorage").field("bytes", unsafe { &self.bytes }).finish()
540    }
541}
542
543/// The storage for a WebAssembly global defined within the instance.
544///
545/// TODO: Pack the globals more densely, rather than using the same size
546/// for every type.
547#[derive(Debug, Clone)]
548#[repr(C, align(16))]
549pub struct VMGlobalDefinition {
550    storage: VMGlobalDefinitionStorage,
551    // If more elements are added here, remember to add offset_of tests below!
552}
553
554#[cfg(test)]
555mod test_vmglobal_definition {
556    use super::VMGlobalDefinition;
557    use crate::{VMFuncRef, VMOffsets};
558    use more_asserts::assert_ge;
559    use near_vm_types::ModuleInfo;
560    use std::mem::{align_of, size_of};
561
562    #[test]
563    fn check_vmglobal_definition_alignment() {
564        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<i32>());
565        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<i64>());
566        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<f32>());
567        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<f64>());
568        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<VMFuncRef>());
569        assert_ge!(align_of::<VMGlobalDefinition>(), align_of::<[u8; 16]>());
570    }
571
572    #[test]
573    fn check_vmglobal_definition_offsets() {
574        let module = ModuleInfo::new();
575        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
576        assert_eq!(
577            size_of::<*const VMGlobalDefinition>(),
578            usize::from(offsets.size_of_vmglobal_local())
579        );
580    }
581
582    #[test]
583    fn check_vmglobal_begins_aligned() {
584        let module = ModuleInfo::new();
585        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
586        assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
587    }
588}
589
590impl VMGlobalDefinition {
591    /// Construct a `VMGlobalDefinition`.
592    pub fn new() -> Self {
593        Self { storage: VMGlobalDefinitionStorage { bytes: [0; 16] } }
594    }
595
596    /// Return the value as an i32.
597    ///
598    /// If this is not an I32 typed global it is unspecified what value is returned.
599    pub fn to_i32(&self) -> i32 {
600        unsafe { self.storage.as_i32 }
601    }
602
603    /// Return a mutable reference to the value as an i32.
604    ///
605    /// # Safety
606    ///
607    /// It is the callers responsibility to make sure the global has I32 type.
608    /// Until the returned borrow is dropped, reads and writes of this global
609    /// must be done exclusively through this borrow. That includes reads and
610    /// writes of globals inside wasm functions.
611    pub unsafe fn as_i32_mut(&mut self) -> &mut i32 {
612        &mut self.storage.as_i32
613    }
614
615    /// Return a reference to the value as an u32.
616    ///
617    /// If this is not an I32 typed global it is unspecified what value is returned.
618    pub fn to_u32(&self) -> u32 {
619        unsafe { self.storage.as_u32 }
620    }
621
622    /// Return a mutable reference to the value as an u32.
623    ///
624    /// # Safety
625    ///
626    /// It is the callers responsibility to make sure the global has I32 type.
627    /// Until the returned borrow is dropped, reads and writes of this global
628    /// must be done exclusively through this borrow. That includes reads and
629    /// writes of globals inside wasm functions.
630    pub unsafe fn as_u32_mut(&mut self) -> &mut u32 {
631        &mut self.storage.as_u32
632    }
633
634    /// Return a reference to the value as an i64.
635    ///
636    /// If this is not an I64 typed global it is unspecified what value is returned.
637    pub fn to_i64(&self) -> i64 {
638        unsafe { self.storage.as_i64 }
639    }
640
641    /// Return a mutable reference to the value as an i64.
642    ///
643    /// # Safety
644    ///
645    /// It is the callers responsibility to make sure the global has I32 type.
646    /// Until the returned borrow is dropped, reads and writes of this global
647    /// must be done exclusively through this borrow. That includes reads and
648    /// writes of globals inside wasm functions.
649    pub unsafe fn as_i64_mut(&mut self) -> &mut i64 {
650        &mut self.storage.as_i64
651    }
652
653    /// Return a reference to the value as an u64.
654    ///
655    /// If this is not an I64 typed global it is unspecified what value is returned.
656    pub fn to_u64(&self) -> u64 {
657        unsafe { self.storage.as_u64 }
658    }
659
660    /// Return a mutable reference to the value as an u64.
661    ///
662    /// # Safety
663    ///
664    /// It is the callers responsibility to make sure the global has I64 type.
665    /// Until the returned borrow is dropped, reads and writes of this global
666    /// must be done exclusively through this borrow. That includes reads and
667    /// writes of globals inside wasm functions.
668    pub unsafe fn as_u64_mut(&mut self) -> &mut u64 {
669        &mut self.storage.as_u64
670    }
671
672    /// Return a reference to the value as an f32.
673    ///
674    /// If this is not an F32 typed global it is unspecified what value is returned.
675    pub fn to_f32(&self) -> f32 {
676        unsafe { self.storage.as_f32 }
677    }
678
679    /// Return a mutable reference to the value as an f32.
680    ///
681    /// # Safety
682    ///
683    /// It is the callers responsibility to make sure the global has F32 type.
684    /// Until the returned borrow is dropped, reads and writes of this global
685    /// must be done exclusively through this borrow. That includes reads and
686    /// writes of globals inside wasm functions.
687    pub unsafe fn as_f32_mut(&mut self) -> &mut f32 {
688        &mut self.storage.as_f32
689    }
690
691    /// Return a reference to the value as an f64.
692    ///
693    /// If this is not an F64 typed global it is unspecified what value is returned.
694    pub fn to_f64(&self) -> f64 {
695        unsafe { self.storage.as_f64 }
696    }
697
698    /// Return a mutable reference to the value as an f64.
699    ///
700    /// # Safety
701    ///
702    /// It is the callers responsibility to make sure the global has F64 type.
703    /// Until the returned borrow is dropped, reads and writes of this global
704    /// must be done exclusively through this borrow. That includes reads and
705    /// writes of globals inside wasm functions.
706    pub unsafe fn as_f64_mut(&mut self) -> &mut f64 {
707        &mut self.storage.as_f64
708    }
709
710    /// Return a reference to the value as a `VMFuncRef`.
711    ///
712    /// If this is not a `VMFuncRef` typed global it is unspecified what value is returned.
713    pub fn to_funcref(&self) -> VMFuncRef {
714        unsafe { self.storage.as_funcref }
715    }
716
717    /// Return a mutable reference to the value as a `VMFuncRef`.
718    ///
719    /// # Safety
720    ///
721    /// It is the callers responsibility to make sure the global has `VMFuncRef` type.
722    /// Until the returned borrow is dropped, reads and writes of this global
723    /// must be done exclusively through this borrow. That includes reads and
724    /// writes of globals inside wasm functions.
725    pub unsafe fn as_funcref_mut(&mut self) -> &mut VMFuncRef {
726        &mut self.storage.as_funcref
727    }
728
729    /// Return a mutable reference to the value as an `VMExternRef`.
730    ///
731    /// # Safety
732    ///
733    /// It is the callers responsibility to make sure the global has I32 type.
734    /// Until the returned borrow is dropped, reads and writes of this global
735    /// must be done exclusively through this borrow. That includes reads and
736    /// writes of globals inside wasm functions.
737    pub unsafe fn as_externref_mut(&mut self) -> &mut VMExternRef {
738        &mut self.storage.as_externref
739    }
740
741    /// Return a reference to the value as an `VMExternRef`.
742    ///
743    /// If this is not an I64 typed global it is unspecified what value is returned.
744    pub fn to_externref(&self) -> VMExternRef {
745        unsafe { self.storage.as_externref }
746    }
747
748    /// Return a reference to the value as an u128.
749    ///
750    /// If this is not an V128 typed global it is unspecified what value is returned.
751    pub fn to_u128(&self) -> u128 {
752        unsafe { self.storage.as_u128 }
753    }
754
755    /// Return a mutable reference to the value as an u128.
756    ///
757    /// # Safety
758    ///
759    /// It is the callers responsibility to make sure the global has V128 type.
760    /// Until the returned borrow is dropped, reads and writes of this global
761    /// must be done exclusively through this borrow. That includes reads and
762    /// writes of globals inside wasm functions.
763    pub unsafe fn as_u128_mut(&mut self) -> &mut u128 {
764        &mut self.storage.as_u128
765    }
766
767    /// Return a reference to the value as bytes.
768    pub fn to_bytes(&self) -> [u8; 16] {
769        unsafe { self.storage.bytes }
770    }
771
772    /// Return a mutable reference to the value as bytes.
773    ///
774    /// # Safety
775    ///
776    /// Until the returned borrow is dropped, reads and writes of this global
777    /// must be done exclusively through this borrow. That includes reads and
778    /// writes of globals inside wasm functions.
779    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8; 16] {
780        &mut self.storage.bytes
781    }
782}
783
784#[cfg(test)]
785mod test_vmshared_signature_index {
786    use super::VMSharedSignatureIndex;
787    use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets};
788    use near_vm_types::ModuleInfo;
789    use std::mem::size_of;
790
791    #[test]
792    fn check_vmshared_signature_index() {
793        let module = ModuleInfo::new();
794        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
795        assert_eq!(
796            size_of::<VMSharedSignatureIndex>(),
797            usize::from(offsets.size_of_vmshared_signature_index())
798        );
799    }
800
801    #[test]
802    fn check_target_shared_signature_index() {
803        assert_eq!(size_of::<VMSharedSignatureIndex>(), size_of::<TargetSharedSignatureIndex>());
804    }
805}
806
807/// The VM caller-checked "anyfunc" record, for caller-side signature checking.
808/// It consists of the actual function pointer and a signature id to be checked
809/// by the caller.
810#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
811#[repr(C)]
812pub struct VMCallerCheckedAnyfunc {
813    /// Function body.
814    pub func_ptr: *const VMFunctionBody,
815    /// Function signature id.
816    pub type_index: VMSharedSignatureIndex,
817    /// Function `VMContext` or host env.
818    pub vmctx: VMFunctionEnvironment,
819    // If more elements are added here, remember to add offset_of tests below!
820}
821
822#[cfg(test)]
823mod test_vmcaller_checked_anyfunc {
824    use super::VMCallerCheckedAnyfunc;
825    use crate::VMOffsets;
826    use memoffset::offset_of;
827    use near_vm_types::ModuleInfo;
828    use std::mem::size_of;
829
830    #[test]
831    fn check_vmcaller_checked_anyfunc_offsets() {
832        let module = ModuleInfo::new();
833        let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module);
834        assert_eq!(
835            size_of::<VMCallerCheckedAnyfunc>(),
836            usize::from(offsets.size_of_vmcaller_checked_anyfunc())
837        );
838        assert_eq!(
839            offset_of!(VMCallerCheckedAnyfunc, func_ptr),
840            usize::from(offsets.vmcaller_checked_anyfunc_func_ptr())
841        );
842        assert_eq!(
843            offset_of!(VMCallerCheckedAnyfunc, type_index),
844            usize::from(offsets.vmcaller_checked_anyfunc_type_index())
845        );
846        assert_eq!(
847            offset_of!(VMCallerCheckedAnyfunc, vmctx),
848            usize::from(offsets.vmcaller_checked_anyfunc_vmctx())
849        );
850    }
851}
852
853/// An index type for builtin functions.
854#[derive(Copy, Clone, Debug)]
855pub struct VMBuiltinFunctionIndex(u32);
856
857impl VMBuiltinFunctionIndex {
858    /// Returns an index for wasm's `memory.grow` builtin function.
859    pub const fn get_memory32_grow_index() -> Self {
860        Self(0)
861    }
862    /// Returns an index for wasm's imported `memory.grow` builtin function.
863    pub const fn get_imported_memory32_grow_index() -> Self {
864        Self(1)
865    }
866    /// Returns an index for wasm's `memory.size` builtin function.
867    pub const fn get_memory32_size_index() -> Self {
868        Self(2)
869    }
870    /// Returns an index for wasm's imported `memory.size` builtin function.
871    pub const fn get_imported_memory32_size_index() -> Self {
872        Self(3)
873    }
874    /// Returns an index for wasm's `table.copy` when both tables are locally
875    /// defined.
876    pub const fn get_table_copy_index() -> Self {
877        Self(4)
878    }
879    /// Returns an index for wasm's `table.init`.
880    pub const fn get_table_init_index() -> Self {
881        Self(5)
882    }
883    /// Returns an index for wasm's `elem.drop`.
884    pub const fn get_elem_drop_index() -> Self {
885        Self(6)
886    }
887    /// Returns an index for wasm's `memory.copy` for locally defined memories.
888    pub const fn get_memory_copy_index() -> Self {
889        Self(7)
890    }
891    /// Returns an index for wasm's `memory.copy` for imported memories.
892    pub const fn get_imported_memory_copy_index() -> Self {
893        Self(8)
894    }
895    /// Returns an index for wasm's `memory.fill` for locally defined memories.
896    pub const fn get_memory_fill_index() -> Self {
897        Self(9)
898    }
899    /// Returns an index for wasm's `memory.fill` for imported memories.
900    pub const fn get_imported_memory_fill_index() -> Self {
901        Self(10)
902    }
903    /// Returns an index for wasm's `memory.init` instruction.
904    pub const fn get_memory_init_index() -> Self {
905        Self(11)
906    }
907    /// Returns an index for wasm's `data.drop` instruction.
908    pub const fn get_data_drop_index() -> Self {
909        Self(12)
910    }
911    /// Returns an index for wasm's `raise_trap` instruction.
912    pub const fn get_raise_trap_index() -> Self {
913        Self(13)
914    }
915    /// Returns an index for wasm's `table.size` instruction for local tables.
916    pub const fn get_table_size_index() -> Self {
917        Self(14)
918    }
919    /// Returns an index for wasm's `table.size` instruction for imported tables.
920    pub const fn get_imported_table_size_index() -> Self {
921        Self(15)
922    }
923    /// Returns an index for wasm's `table.grow` instruction for local tables.
924    pub const fn get_table_grow_index() -> Self {
925        Self(16)
926    }
927    /// Returns an index for wasm's `table.grow` instruction for imported tables.
928    pub const fn get_imported_table_grow_index() -> Self {
929        Self(17)
930    }
931    /// Returns an index for wasm's `table.get` instruction for local tables.
932    pub const fn get_table_get_index() -> Self {
933        Self(18)
934    }
935    /// Returns an index for wasm's `table.get` instruction for imported tables.
936    pub const fn get_imported_table_get_index() -> Self {
937        Self(19)
938    }
939    /// Returns an index for wasm's `table.set` instruction for local tables.
940    pub const fn get_table_set_index() -> Self {
941        Self(20)
942    }
943    /// Returns an index for wasm's `table.set` instruction for imported tables.
944    pub const fn get_imported_table_set_index() -> Self {
945        Self(21)
946    }
947    /// Returns an index for wasm's `func.ref` instruction.
948    pub const fn get_func_ref_index() -> Self {
949        Self(22)
950    }
951    /// Returns an index for wasm's `table.fill` instruction for local tables.
952    pub const fn get_table_fill_index() -> Self {
953        Self(23)
954    }
955    /// Returns an index for a function to increment the externref count.
956    pub const fn get_externref_inc_index() -> Self {
957        Self(24)
958    }
959    /// Returns an index for a function to decrement the externref count.
960    pub const fn get_externref_dec_index() -> Self {
961        Self(25)
962    }
963    /// Returns the total number of builtin functions.
964    pub const fn builtin_functions_total_number() -> u32 {
965        26
966    }
967
968    /// Return the index as an u32 number.
969    pub const fn index(self) -> u32 {
970        self.0
971    }
972}
973
974/// An array that stores addresses of builtin functions. We translate code
975/// to use indirect calls. This way, we don't have to patch the code.
976#[repr(C)]
977pub struct VMBuiltinFunctionsArray {
978    ptrs: [usize; Self::len()],
979}
980
981impl VMBuiltinFunctionsArray {
982    pub const fn len() -> usize {
983        VMBuiltinFunctionIndex::builtin_functions_total_number() as usize
984    }
985
986    pub fn initialized() -> Self {
987        use crate::libcalls::*;
988
989        let mut ptrs = [0; Self::len()];
990
991        ptrs[VMBuiltinFunctionIndex::get_memory32_grow_index().index() as usize] =
992            near_vm_memory32_grow as usize;
993        ptrs[VMBuiltinFunctionIndex::get_imported_memory32_grow_index().index() as usize] =
994            near_vm_imported_memory32_grow as usize;
995
996        ptrs[VMBuiltinFunctionIndex::get_memory32_size_index().index() as usize] =
997            near_vm_memory32_size as usize;
998        ptrs[VMBuiltinFunctionIndex::get_imported_memory32_size_index().index() as usize] =
999            near_vm_imported_memory32_size as usize;
1000
1001        ptrs[VMBuiltinFunctionIndex::get_table_copy_index().index() as usize] =
1002            near_vm_table_copy as usize;
1003
1004        ptrs[VMBuiltinFunctionIndex::get_table_init_index().index() as usize] =
1005            near_vm_table_init as usize;
1006        ptrs[VMBuiltinFunctionIndex::get_elem_drop_index().index() as usize] =
1007            near_vm_elem_drop as usize;
1008
1009        ptrs[VMBuiltinFunctionIndex::get_memory_copy_index().index() as usize] =
1010            near_vm_memory32_copy as usize;
1011        ptrs[VMBuiltinFunctionIndex::get_imported_memory_copy_index().index() as usize] =
1012            near_vm_imported_memory32_copy as usize;
1013        ptrs[VMBuiltinFunctionIndex::get_memory_fill_index().index() as usize] =
1014            near_vm_memory32_fill as usize;
1015        ptrs[VMBuiltinFunctionIndex::get_imported_memory_fill_index().index() as usize] =
1016            near_vm_imported_memory32_fill as usize;
1017        ptrs[VMBuiltinFunctionIndex::get_memory_init_index().index() as usize] =
1018            near_vm_memory32_init as usize;
1019        ptrs[VMBuiltinFunctionIndex::get_data_drop_index().index() as usize] =
1020            near_vm_data_drop as usize;
1021        ptrs[VMBuiltinFunctionIndex::get_raise_trap_index().index() as usize] =
1022            near_vm_raise_trap as usize;
1023        ptrs[VMBuiltinFunctionIndex::get_table_size_index().index() as usize] =
1024            near_vm_table_size as usize;
1025        ptrs[VMBuiltinFunctionIndex::get_imported_table_size_index().index() as usize] =
1026            near_vm_imported_table_size as usize;
1027        ptrs[VMBuiltinFunctionIndex::get_table_grow_index().index() as usize] =
1028            near_vm_table_grow as usize;
1029        ptrs[VMBuiltinFunctionIndex::get_imported_table_grow_index().index() as usize] =
1030            near_vm_imported_table_grow as usize;
1031        ptrs[VMBuiltinFunctionIndex::get_table_get_index().index() as usize] =
1032            near_vm_table_get as usize;
1033        ptrs[VMBuiltinFunctionIndex::get_imported_table_get_index().index() as usize] =
1034            near_vm_imported_table_get as usize;
1035        ptrs[VMBuiltinFunctionIndex::get_table_set_index().index() as usize] =
1036            near_vm_table_set as usize;
1037        ptrs[VMBuiltinFunctionIndex::get_imported_table_set_index().index() as usize] =
1038            near_vm_imported_table_set as usize;
1039        ptrs[VMBuiltinFunctionIndex::get_func_ref_index().index() as usize] =
1040            near_vm_func_ref as usize;
1041        ptrs[VMBuiltinFunctionIndex::get_table_fill_index().index() as usize] =
1042            near_vm_table_fill as usize;
1043        ptrs[VMBuiltinFunctionIndex::get_externref_inc_index().index() as usize] =
1044            near_vm_externref_inc as usize;
1045        ptrs[VMBuiltinFunctionIndex::get_externref_dec_index().index() as usize] =
1046            near_vm_externref_dec as usize;
1047
1048        debug_assert!(ptrs.iter().cloned().all(|p| p != 0));
1049
1050        Self { ptrs }
1051    }
1052}
1053
1054/// The VM "context", which is pointed to by the `vmctx` arg in the compiler.
1055/// This has information about globals, memories, tables, and other runtime
1056/// state associated with the current instance.
1057///
1058/// The struct here is empty, as the sizes of these fields are dynamic, and
1059/// we can't describe them in Rust's type system. Sufficient memory is
1060/// allocated at runtime.
1061///
1062/// TODO: We could move the globals into the `vmctx` allocation too.
1063#[derive(Debug)]
1064#[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside
1065pub struct VMContext {}
1066
1067impl VMContext {
1068    /// Return a mutable reference to the associated `Instance`.
1069    ///
1070    /// # Safety
1071    /// This is unsafe because it doesn't work on just any `VMContext`, it must
1072    /// be a `VMContext` allocated as part of an `Instance`.
1073    #[allow(clippy::cast_ptr_alignment)]
1074    #[inline]
1075    pub unsafe fn instance(&self) -> &Instance {
1076        &*((self as *const Self as *mut u8).offset(-Instance::vmctx_offset()) as *const Instance)
1077    }
1078
1079    /// Return a reference to the host state associated with this `Instance`.
1080    ///
1081    /// # Safety
1082    /// This is unsafe because it doesn't work on just any `VMContext`, it must
1083    /// be a `VMContext` allocated as part of an `Instance`.
1084    #[inline]
1085    pub unsafe fn host_state(&self) -> &dyn Any {
1086        self.instance().host_state()
1087    }
1088}
1089
1090/// Signature/ABI of the trampoline.
1091pub type VMTrampoline = unsafe extern "C" fn(
1092    *mut VMContext,        // callee vmctx
1093    *const VMFunctionBody, // function we're actually calling
1094    *mut u128,             // space for arguments and return values
1095);
1096
1097/// Pointers to section data.
1098#[derive(Clone, Copy, Debug)]
1099#[repr(transparent)]
1100pub struct SectionBodyPtr(pub *const u8);
1101
1102impl std::ops::Deref for SectionBodyPtr {
1103    type Target = *const u8;
1104
1105    fn deref(&self) -> &Self::Target {
1106        &self.0
1107    }
1108}