wiggle_runtime/guest_type.rs
1use crate::{GuestError, GuestPtr};
2use std::mem;
3
4pub trait GuestErrorType<'a> {
5 type Context;
6 fn success() -> Self;
7 fn from_error(e: GuestError, ctx: &Self::Context) -> Self;
8}
9
10/// A trait for types that are intended to be pointees in `GuestPtr<T>`.
11///
12/// This trait abstracts how to read/write information from the guest memory, as
13/// well as how to offset elements in an array of guest memory. This layer of
14/// abstraction allows the guest representation of a type to be different from
15/// the host representation of a type, if necessary. It also allows for
16/// validation when reading/writing.
17pub trait GuestType<'a>: Sized {
18 /// Returns the size, in bytes, of this type in the guest memory.
19 fn guest_size() -> u32;
20
21 /// Returns the required alignment of this type, in bytes, for both guest
22 /// and host memory.
23 fn guest_align() -> usize;
24
25 /// Reads this value from the provided `ptr`.
26 ///
27 /// Must internally perform any safety checks necessary and is allowed to
28 /// fail if the bytes pointed to are also invalid.
29 ///
30 /// Typically if you're implementing this by hand you'll want to delegate to
31 /// other safe implementations of this trait (e.g. for primitive types like
32 /// `u32`) rather than writing lots of raw code yourself.
33 fn read(ptr: &GuestPtr<'a, Self>) -> Result<Self, GuestError>;
34
35 /// Writes a value to `ptr` after verifying that `ptr` is indeed valid to
36 /// store `val`.
37 ///
38 /// Similar to `read`, you'll probably want to implement this in terms of
39 /// other primitives.
40 fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError>;
41}
42
43/// A trait for `GuestType`s that have the same representation in guest memory
44/// as in Rust. These types can be used with the `GuestPtr::as_raw` method to
45/// view as a slice.
46///
47/// Unsafe trait because a correct GuestTypeTransparent implemengation ensures that the
48/// GuestPtr::as_raw methods are safe. This trait should only ever be implemented
49/// by wiggle_generate-produced code.
50pub unsafe trait GuestTypeTransparent<'a>: GuestType<'a> {
51 /// Checks that the memory at `ptr` is a valid representation of `Self`.
52 ///
53 /// Assumes that memory safety checks have already been performed: `ptr`
54 /// has been checked to be aligned correctly and reside in memory using
55 /// `GuestMemory::validate_size_align`
56 fn validate(ptr: *mut Self) -> Result<(), GuestError>;
57}
58
59macro_rules! primitives {
60 ($($i:ident)*) => ($(
61 impl<'a> GuestType<'a> for $i {
62 fn guest_size() -> u32 { mem::size_of::<Self>() as u32 }
63 fn guest_align() -> usize { mem::align_of::<Self>() }
64
65 #[inline]
66 fn read(ptr: &GuestPtr<'a, Self>) -> Result<Self, GuestError> {
67 // Any bit pattern for any primitive implemented with this
68 // macro is safe, so our `validate_size_align` method will
69 // guarantee that if we are given a pointer it's valid for the
70 // size of our type as well as properly aligned. Consequently we
71 // should be able to safely ready the pointer just after we
72 // validated it, returning it along here.
73 let host_ptr = ptr.mem().validate_size_align(
74 ptr.offset(),
75 Self::guest_align(),
76 Self::guest_size(),
77 )?;
78 Ok(unsafe { *host_ptr.cast::<Self>() })
79 }
80
81 #[inline]
82 fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> {
83 let host_ptr = ptr.mem().validate_size_align(
84 ptr.offset(),
85 Self::guest_align(),
86 Self::guest_size(),
87 )?;
88 // Similar to above `as_raw` will do a lot of validation, and
89 // then afterwards we can safely write our value into the
90 // memory location.
91 unsafe {
92 *host_ptr.cast::<Self>() = val;
93 }
94 Ok(())
95 }
96 }
97
98 unsafe impl<'a> GuestTypeTransparent<'a> for $i {
99 #[inline]
100 fn validate(_ptr: *mut $i) -> Result<(), GuestError> {
101 // All bit patterns are safe, nothing to do here
102 Ok(())
103 }
104 }
105
106 )*)
107}
108
109primitives! {
110 // signed
111 i8 i16 i32 i64 i128
112 // unsigned
113 u8 u16 u32 u64 u128
114 // floats
115 f32 f64
116}
117
118// Support pointers-to-pointers where pointers are always 32-bits in wasm land
119impl<'a, T> GuestType<'a> for GuestPtr<'a, T> {
120 fn guest_size() -> u32 {
121 u32::guest_size()
122 }
123
124 fn guest_align() -> usize {
125 u32::guest_align()
126 }
127
128 fn read(ptr: &GuestPtr<'a, Self>) -> Result<Self, GuestError> {
129 let offset = ptr.cast::<u32>().read()?;
130 Ok(GuestPtr::new(ptr.mem(), offset))
131 }
132
133 fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> {
134 ptr.cast::<u32>().write(val.offset())
135 }
136}