wasmer_types/
serialize.rs

1use crate::entity::PrimaryMap;
2use crate::{
3    compilation::target::CpuFeature, CompileModuleInfo, CompiledFunctionFrameInfo, CustomSection,
4    DeserializeError, Dwarf, Features, FunctionBody, FunctionIndex, LocalFunctionIndex,
5    MemoryIndex, MemoryStyle, ModuleInfo, OwnedDataInitializer, Relocation, SectionIndex,
6    SerializeError, SignatureIndex, TableIndex, TableStyle,
7};
8use enumset::EnumSet;
9use rkyv::check_archived_value;
10use rkyv::{
11    archived_value, de::deserializers::SharedDeserializeMap, ser::serializers::AllocSerializer,
12    ser::Serializer as RkyvSerializer, Archive, CheckBytes, Deserialize as RkyvDeserialize,
13    Serialize as RkyvSerialize,
14};
15use std::convert::TryInto;
16use std::mem;
17
18/// The compilation related data for a serialized modules
19#[derive(Archive, Default, RkyvDeserialize, RkyvSerialize)]
20#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
21#[allow(missing_docs)]
22#[archive_attr(derive(CheckBytes, Debug))]
23pub struct SerializableCompilation {
24    pub function_bodies: PrimaryMap<LocalFunctionIndex, FunctionBody>,
25    pub function_relocations: PrimaryMap<LocalFunctionIndex, Vec<Relocation>>,
26    pub function_frame_info: PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>,
27    pub function_call_trampolines: PrimaryMap<SignatureIndex, FunctionBody>,
28    pub dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBody>,
29    pub custom_sections: PrimaryMap<SectionIndex, CustomSection>,
30    pub custom_section_relocations: PrimaryMap<SectionIndex, Vec<Relocation>>,
31    // The section indices corresponding to the Dwarf debug info
32    pub debug: Option<Dwarf>,
33    // Custom section containing libcall trampolines.
34    pub libcall_trampolines: SectionIndex,
35    // Length of each libcall trampoline.
36    pub libcall_trampoline_len: u32,
37}
38
39impl SerializableCompilation {
40    /// Serialize a Compilation into bytes
41    /// The bytes will have the following format:
42    /// RKYV serialization (any length) + POS (8 bytes)
43    pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
44        let mut serializer = AllocSerializer::<4096>::default();
45        let pos = serializer
46            .serialize_value(self)
47            .map_err(to_serialize_error)? as u64;
48        let mut serialized_data = serializer.into_serializer().into_inner();
49        serialized_data.extend_from_slice(&pos.to_le_bytes());
50        Ok(serialized_data.to_vec())
51    }
52}
53
54/// Serializable struct that is able to serialize from and to a `ArtifactInfo`.
55#[derive(Archive, RkyvDeserialize, RkyvSerialize)]
56#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
57#[allow(missing_docs)]
58#[archive_attr(derive(CheckBytes, Debug))]
59pub struct SerializableModule {
60    /// The main serializable compilation object
61    pub compilation: SerializableCompilation,
62    /// Compilation informations
63    pub compile_info: CompileModuleInfo,
64    /// Datas initializers
65    pub data_initializers: Box<[OwnedDataInitializer]>,
66    /// CPU Feature flags for this compilation
67    pub cpu_features: u64,
68}
69
70fn to_serialize_error(err: impl std::error::Error) -> SerializeError {
71    SerializeError::Generic(format!("{}", err))
72}
73
74impl SerializableModule {
75    /// Serialize a Module into bytes
76    /// The bytes will have the following format:
77    /// RKYV serialization (any length) + POS (8 bytes)
78    pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
79        let mut serializer = AllocSerializer::<4096>::default();
80        let pos = serializer
81            .serialize_value(self)
82            .map_err(to_serialize_error)? as u64;
83        let mut serialized_data = serializer.into_serializer().into_inner();
84        serialized_data.extend_from_slice(&pos.to_le_bytes());
85        Ok(serialized_data.to_vec())
86    }
87
88    /// Deserialize a Module from a slice.
89    /// The slice must have the following format:
90    /// RKYV serialization (any length) + POS (8 bytes)
91    ///
92    /// # Safety
93    ///
94    /// This method is unsafe since it deserializes data directly
95    /// from memory.
96    /// Right now we are not doing any extra work for validation, but
97    /// `rkyv` has an option to do bytecheck on the serialized data before
98    /// serializing (via `rkyv::check_archived_value`).
99    pub unsafe fn deserialize_unchecked(metadata_slice: &[u8]) -> Result<Self, DeserializeError> {
100        let archived = Self::archive_from_slice(metadata_slice)?;
101        Self::deserialize_from_archive(archived)
102    }
103
104    /// Deserialize a Module from a slice.
105    /// The slice must have the following format:
106    /// RKYV serialization (any length) + POS (8 bytes)
107    ///
108    /// Unlike [`Self::deserialize`], this function will validate the data.
109    ///
110    /// # Safety
111    /// Unsafe because it loads executable code into memory.
112    /// The loaded bytes must be trusted.
113    pub unsafe fn deserialize(metadata_slice: &[u8]) -> Result<Self, DeserializeError> {
114        let archived = Self::archive_from_slice_checked(metadata_slice)?;
115        Self::deserialize_from_archive(archived)
116    }
117
118    /// # Safety
119    ///
120    /// This method is unsafe.
121    /// Please check `SerializableModule::deserialize` for more details.
122    pub unsafe fn archive_from_slice(
123        metadata_slice: &[u8],
124    ) -> Result<&ArchivedSerializableModule, DeserializeError> {
125        if metadata_slice.len() < 8 {
126            return Err(DeserializeError::Incompatible(
127                "invalid serialized data".into(),
128            ));
129        }
130        let mut pos: [u8; 8] = Default::default();
131        pos.copy_from_slice(&metadata_slice[metadata_slice.len() - 8..metadata_slice.len()]);
132        let pos: u64 = u64::from_le_bytes(pos);
133        Ok(archived_value::<Self>(
134            &metadata_slice[..metadata_slice.len() - 8],
135            pos as usize,
136        ))
137    }
138
139    /// Deserialize an archived module.
140    ///
141    /// In contrast to [`Self::deserialize`], this method performs validation
142    /// and is not unsafe.
143    pub fn archive_from_slice_checked(
144        metadata_slice: &[u8],
145    ) -> Result<&ArchivedSerializableModule, DeserializeError> {
146        if metadata_slice.len() < 8 {
147            return Err(DeserializeError::Incompatible(
148                "invalid serialized data".into(),
149            ));
150        }
151        let mut pos: [u8; 8] = Default::default();
152        pos.copy_from_slice(&metadata_slice[metadata_slice.len() - 8..metadata_slice.len()]);
153        let pos: u64 = u64::from_le_bytes(pos);
154        check_archived_value::<Self>(&metadata_slice[..metadata_slice.len() - 8], pos as usize)
155            .map_err(|err| DeserializeError::CorruptedBinary(err.to_string()))
156    }
157
158    /// Deserialize a compilation module from an archive
159    pub fn deserialize_from_archive(
160        archived: &ArchivedSerializableModule,
161    ) -> Result<Self, DeserializeError> {
162        let mut deserializer = SharedDeserializeMap::new();
163        RkyvDeserialize::deserialize(archived, &mut deserializer)
164            .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))
165    }
166
167    /// Create a `ModuleInfo` for instantiation
168    pub fn create_module_info(&self) -> ModuleInfo {
169        self.compile_info.module.as_ref().clone()
170    }
171
172    /// Returns the `ModuleInfo` for instantiation
173    pub fn module_info(&self) -> &ModuleInfo {
174        &self.compile_info.module
175    }
176
177    /// Returns the features for this Artifact
178    pub fn features(&self) -> &Features {
179        &self.compile_info.features
180    }
181
182    /// Returns the CPU features for this Artifact
183    pub fn cpu_features(&self) -> EnumSet<CpuFeature> {
184        EnumSet::from_u64(self.cpu_features)
185    }
186
187    /// Returns data initializers to pass to `VMInstance::initialize`
188    pub fn data_initializers(&self) -> &[OwnedDataInitializer] {
189        &self.data_initializers
190    }
191
192    /// Returns the memory styles associated with this `Artifact`.
193    pub fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle> {
194        &self.compile_info.memory_styles
195    }
196
197    /// Returns the table plans associated with this `Artifact`.
198    pub fn table_styles(&self) -> &PrimaryMap<TableIndex, TableStyle> {
199        &self.compile_info.table_styles
200    }
201}
202
203/// Metadata header which holds an ABI version and the length of the remaining
204/// metadata.
205#[repr(C)]
206#[derive(Clone, Copy)]
207pub struct MetadataHeader {
208    magic: [u8; 8],
209    version: u32,
210    len: u32,
211}
212
213impl MetadataHeader {
214    /// Current ABI version. Increment this any time breaking changes are made
215    /// to the format of the serialized data.
216    pub const CURRENT_VERSION: u32 = 7;
217
218    /// Magic number to identify wasmer metadata.
219    const MAGIC: [u8; 8] = *b"WASMER\0\0";
220
221    /// Length of the metadata header.
222    pub const LEN: usize = 16;
223
224    /// Alignment of the metadata.
225    pub const ALIGN: usize = 16;
226
227    /// Creates a new header for metadata of the given length.
228    pub fn new(len: usize) -> Self {
229        Self {
230            magic: Self::MAGIC,
231            version: Self::CURRENT_VERSION,
232            len: len.try_into().expect("metadata exceeds maximum length"),
233        }
234    }
235
236    /// Convert the header into its bytes representation.
237    pub fn into_bytes(self) -> [u8; 16] {
238        unsafe { mem::transmute(self) }
239    }
240
241    /// Parses the header and returns the length of the metadata following it.
242    pub fn parse(bytes: &[u8]) -> Result<usize, DeserializeError> {
243        if bytes.as_ptr() as usize % 8 != 0 {
244            return Err(DeserializeError::CorruptedBinary(
245                "misaligned metadata".to_string(),
246            ));
247        }
248        let bytes: [u8; 16] = bytes
249            .get(..16)
250            .ok_or_else(|| {
251                DeserializeError::CorruptedBinary("invalid metadata header".to_string())
252            })?
253            .try_into()
254            .unwrap();
255        let header: Self = unsafe { mem::transmute(bytes) };
256        if header.magic != Self::MAGIC {
257            return Err(DeserializeError::Incompatible(
258                "The provided bytes were not serialized by Wasmer".to_string(),
259            ));
260        }
261        if header.version != Self::CURRENT_VERSION {
262            return Err(DeserializeError::Incompatible(
263                "The provided bytes were serialized by an incompatible version of Wasmer"
264                    .to_string(),
265            ));
266        }
267        Ok(header.len as usize)
268    }
269}