wiggle/guest_type.rs
1use crate::{GuestError, GuestMemory, GuestPtr};
2use std::cell::UnsafeCell;
3use std::mem;
4use std::sync::atomic::{
5 AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicU16, AtomicU32, AtomicU64, AtomicU8, Ordering,
6};
7
8/// A trait for types which are used to report errors. Each type used in the
9/// first result position of an interface function is used, by convention, to
10/// indicate whether the function was successful and subsequent results are valid,
11/// or whether an error occurred. This trait allows wiggle to return the correct
12/// value when the interface function's idiomatic Rust method returns
13/// `Ok(<rest of return values>)`.
14pub trait GuestErrorType {
15 fn success() -> Self;
16}
17
18/// A trait for types that are intended to be pointees in `GuestPtr<T>`.
19///
20/// This trait abstracts how to read/write information from the guest memory, as
21/// well as how to offset elements in an array of guest memory. This layer of
22/// abstraction allows the guest representation of a type to be different from
23/// the host representation of a type, if necessary. It also allows for
24/// validation when reading/writing.
25pub trait GuestType: Sized {
26 /// Returns the size, in bytes, of this type in the guest memory.
27 fn guest_size() -> u32;
28
29 /// Returns the required alignment of this type, in bytes, for both guest
30 /// and host memory.
31 fn guest_align() -> usize;
32
33 /// Reads this value from the provided `ptr`.
34 ///
35 /// Must internally perform any safety checks necessary and is allowed to
36 /// fail if the bytes pointed to are also invalid.
37 ///
38 /// Typically if you're implementing this by hand you'll want to delegate to
39 /// other safe implementations of this trait (e.g. for primitive types like
40 /// `u32`) rather than writing lots of raw code yourself.
41 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError>;
42
43 /// Writes a value to `ptr` after verifying that `ptr` is indeed valid to
44 /// store `val`.
45 ///
46 /// Similar to `read`, you'll probably want to implement this in terms of
47 /// other primitives.
48 fn write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError>;
49}
50
51/// A trait for `GuestType`s that have the same representation in guest memory
52/// as in Rust. These types can be used with the `GuestPtr::as_slice` method to
53/// view as a slice.
54///
55/// Unsafe trait because a correct `GuestTypeTransparent` implementation ensures
56/// that the `GuestPtr::as_slice` methods are safe, notably that the
57/// representation on the host matches the guest and all bit patterns are
58/// valid. This trait should only ever be implemented by
59/// wiggle_generate-produced code.
60pub unsafe trait GuestTypeTransparent: GuestType {}
61
62macro_rules! integer_primitives {
63 ($([$ty:ident, $ty_atomic:ident],)*) => ($(
64 impl GuestType for $ty {
65 #[inline]
66 fn guest_size() -> u32 { mem::size_of::<Self>() as u32 }
67 #[inline]
68 fn guest_align() -> usize { mem::align_of::<Self>() }
69
70 #[inline]
71 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError> {
72 // Use `validate_size_align` to validate offset and alignment
73 // internally. The `host_ptr` type will be `&UnsafeCell<Self>`
74 // indicating that the memory is valid, and next safety checks
75 // are required to access it.
76 let offset = ptr.offset();
77 let host_ptr = mem.validate_size_align::<Self>(offset, 1)?;
78
79 // If the accessed memory is shared, we need to load the bytes
80 // with the correct memory consistency. We could check if the
81 // memory is shared each time, but we expect little performance
82 // difference between an additional branch and a relaxed memory
83 // access and thus always do the relaxed access here.
84 let host_ptr: &$ty_atomic = unsafe {
85 let host_ptr: &UnsafeCell<Self> = &host_ptr[0];
86 &*((host_ptr as *const UnsafeCell<Self>).cast::<$ty_atomic>())
87 };
88 let val = host_ptr.load(Ordering::Relaxed);
89
90 // And as a final operation convert from the little-endian wasm
91 // value to a native-endian value for the host.
92 Ok($ty::from_le(val))
93 }
94
95 #[inline]
96 fn write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError> {
97 // See `read` above for various checks here.
98 let val = val.to_le();
99 let offset = ptr.offset();
100 let host_ptr = mem.validate_size_align::<Self>(offset, 1)?;
101 let host_ptr = &host_ptr[0];
102 let atomic_value_ref: &$ty_atomic =
103 unsafe { &*(host_ptr.get().cast::<$ty_atomic>()) };
104 atomic_value_ref.store(val, Ordering::Relaxed);
105 Ok(())
106 }
107 }
108
109 unsafe impl GuestTypeTransparent for $ty {}
110
111 )*)
112}
113
114macro_rules! float_primitives {
115 ($([$ty:ident, $ty_unsigned:ident, $ty_atomic:ident],)*) => ($(
116 impl GuestType for $ty {
117 #[inline]
118 fn guest_size() -> u32 { mem::size_of::<Self>() as u32 }
119 #[inline]
120 fn guest_align() -> usize { mem::align_of::<Self>() }
121
122 #[inline]
123 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError> {
124 <$ty_unsigned as GuestType>::read(mem, ptr.cast()).map($ty::from_bits)
125 }
126
127 #[inline]
128 fn write(mem:&mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError> {
129 <$ty_unsigned as GuestType>::write(mem, ptr.cast(), val.to_bits())
130 }
131 }
132
133 unsafe impl GuestTypeTransparent for $ty {}
134
135 )*)
136}
137
138integer_primitives! {
139 // signed
140 [i8, AtomicI8], [i16, AtomicI16], [i32, AtomicI32], [i64, AtomicI64],
141 // unsigned
142 [u8, AtomicU8], [u16, AtomicU16], [u32, AtomicU32], [u64, AtomicU64],
143}
144
145float_primitives! {
146 [f32, u32, AtomicU32], [f64, u64, AtomicU64],
147}
148
149// Support pointers-to-pointers where pointers are always 32-bits in wasm land
150impl<T> GuestType for GuestPtr<T> {
151 #[inline]
152 fn guest_size() -> u32 {
153 u32::guest_size()
154 }
155
156 #[inline]
157 fn guest_align() -> usize {
158 u32::guest_align()
159 }
160
161 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError> {
162 let offset = u32::read(mem, ptr.cast())?;
163 Ok(GuestPtr::new(offset))
164 }
165
166 fn write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError> {
167 u32::write(mem, ptr.cast(), val.offset())
168 }
169}
170
171// Support pointers-to-arrays where pointers are always 32-bits in wasm land
172impl<T> GuestType for GuestPtr<[T]>
173where
174 T: GuestType,
175{
176 #[inline]
177 fn guest_size() -> u32 {
178 u32::guest_size() * 2
179 }
180
181 #[inline]
182 fn guest_align() -> usize {
183 u32::guest_align()
184 }
185
186 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError> {
187 let ptr = ptr.cast::<u32>();
188 let offset = u32::read(mem, ptr)?;
189 let len = u32::read(mem, ptr.add(1)?)?;
190 Ok(GuestPtr::new(offset).as_array(len))
191 }
192
193 fn write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError> {
194 let (offset, len) = val.offset();
195 let ptr = ptr.cast::<u32>();
196 u32::write(mem, ptr, offset)?;
197 u32::write(mem, ptr.add(1)?, len)?;
198 Ok(())
199 }
200}