1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
use crate::Error; use serde::{Deserialize, Serialize}; /// Specification of the linear memory of a module #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LinearMemorySpec<'a> { /// Specification of the heap used to implement the linear memory pub heap: HeapSpec, /// Initialization values for linear memory #[serde(borrow)] pub initializer: SparseData<'a>, } /// Specification of the linear memory of a module /// /// This is a version of [`LinearMemorySpec`](../struct.LinearMemorySpec.html) with an /// `OwnedSparseData` for the initializer. /// This type is useful when directly building up a value to be serialized. pub struct OwnedLinearMemorySpec { /// Specification of the heap used to implement the linear memory pub heap: HeapSpec, /// Initialization values for linear memory pub initializer: OwnedSparseData, } impl OwnedLinearMemorySpec { pub fn to_ref<'a>(&'a self) -> LinearMemorySpec<'a> { LinearMemorySpec { heap: self.heap.clone(), initializer: self.initializer.to_ref(), } } } /// Specifications about the heap of a Lucet module. #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct HeapSpec { /// Total bytes of memory for the heap to possibly expand into, as configured for Cranelift /// codegen. /// /// All of this memory is addressable. Only some part of it is accessible - from 0 to the /// initial size, guaranteed, and up to the `max_size`. This size allows Cranelift to elide /// checks of the *base pointer*. At the moment that just means checking if it is greater than /// 4gb, in which case it can elide the base pointer check completely. In the future, Cranelift /// could use a solver to elide more base pointer checks if it can prove the calculation will /// always be less than this bound. /// /// Specified in bytes, and must be evenly divisible by the host page size (4K). pub reserved_size: u64, /// Total bytes of memory *after* the reserved area, as configured for Cranelift codegen. /// /// All of this memory is addressable, but it is never accessible - it is guaranteed to trap if /// an access happens in this region. This size allows Cranelift to use *common subexpression /// elimination* to reduce checks of the *sum of base pointer and offset* (where the offset is /// always rounded up to a multiple of the guard size, to be friendly to CSE). /// /// Specified in bytes, and must be evenly divisible by the host page size (4K). pub guard_size: u64, /// Total bytes of memory for the WebAssembly program's linear memory upon initialization. /// /// Specified in bytes, must be evenly divisible by the WebAssembly page size (64K), and must be /// less than or equal to `reserved_size`. pub initial_size: u64, /// Maximum bytes of memory for the WebAssembly program's linear memory at any time. /// /// This is not necessarily the same as `reserved_size` - we want to be able to tune the check /// bound there separately than the declaration of a max size in the client program. /// /// The program may optionally define this value. If it does, it must be less than the /// `reserved_size`. If it does not, the max size is left up to the runtime, and is allowed to /// be less than `reserved_size`. pub max_size: Option<u64>, } impl HeapSpec { pub fn new( reserved_size: u64, guard_size: u64, initial_size: u64, max_size: Option<u64>, ) -> Self { Self { reserved_size, guard_size, initial_size, max_size, } } /// Some very small test programs dont specify a memory import or definition. pub fn empty() -> Self { Self { reserved_size: 0, guard_size: 0, initial_size: 0, max_size: None, } } } /// A sparse representation of a Lucet module's initial heap. /// /// The lifetime parameter exists to support zero-copy deserialization for the `&[u8]` slices /// representing non-zero pages. For a variant with owned `Vec<u8>` pages, see /// [`OwnedSparseData`](owned/struct.OwnedSparseData.html). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SparseData<'a> { /// Indices into the vector correspond to the offset, in host page (4k) increments, from the /// base of the instance heap. /// /// If the option at a given index is None, the page is initialized as zeros. Otherwise, /// the contents of the page are given as a slice of exactly 4k bytes. /// /// The deserializer of this datastructure does not make sure the 4k invariant holds, /// but the constructor on the serializier side does. #[serde(borrow)] pages: Vec<Option<&'a [u8]>>, } impl<'a> SparseData<'a> { /// Create a new `SparseData` from its constituent pages. /// /// Entries in the `pages` argument which are `Some` must contain a slice of exactly the host /// page size (4096), otherwise this function returns `Error::IncorrectPageSize`. Entries which /// are `None` are interpreted as empty pages, which will be zeroed by the runtime. pub fn new(pages: Vec<Option<&'a [u8]>>) -> Result<Self, Error> { if !pages.iter().all(|page| match page { Some(contents) => contents.len() == 4096, None => true, }) { return Err(Error::IncorrectPageSize); } Ok(Self { pages }) } pub fn pages(&self) -> &[Option<&'a [u8]>] { &self.pages } pub fn get_page(&self, offset: usize) -> &Option<&'a [u8]> { self.pages.get(offset).unwrap_or(&None) } pub fn len(&self) -> usize { self.pages.len() } } /// A sparse representation of a Lucet module's initial heap. /// /// This is a version of [`SparseData`](../struct.SparseData.html) with owned `Vec<u8>`s /// representing pages. This type is useful when directly building up a value to be serialized. pub struct OwnedSparseData { pages: Vec<Option<Vec<u8>>>, } impl OwnedSparseData { /// Create a new `OwnedSparseData` from its consitutent pages. /// /// Entries in the `pages` argument which are `Some` must contain a vector of exactly the host /// page size (4096), otherwise this function returns `Error::IncorrectPageSize`. Entries which /// are `None` are interpreted as empty pages, which will be zeroed by the runtime. pub fn new(pages: Vec<Option<Vec<u8>>>) -> Result<Self, Error> { if !pages.iter().all(|page| match page { Some(contents) => contents.len() == 4096, None => true, }) { return Err(Error::IncorrectPageSize); } Ok(Self { pages }) } /// Create a [`SparseData`](../struct.SparseData.html) backed by the values in this /// `OwnedSparseData`. pub fn to_ref<'a>(&'a self) -> SparseData<'a> { SparseData::new( self.pages .iter() .map(|c| match c { Some(data) => Some(data.as_slice()), None => None, }) .collect(), ) .expect("SparseData invariant enforced by OwnedSparseData constructor") } }