wasmer_engine_universal/
executable.rs

1use std::sync::Arc;
2
3use enumset::EnumSet;
4use rkyv::de::deserializers::SharedDeserializeMap;
5use rkyv::ser::serializers::{
6    AllocScratchError, AllocSerializer, CompositeSerializerError, SharedSerializeMapError,
7};
8use wasmer_compiler::{
9    CompileError, CompileModuleInfo, CompiledFunctionFrameInfo, CpuFeature, CustomSection, Dwarf,
10    Features, FunctionBody, JumpTableOffsets, Relocation, SectionIndex, TrampolinesSection,
11};
12use wasmer_engine::{DeserializeError, Engine};
13use wasmer_types::entity::PrimaryMap;
14use wasmer_types::{
15    ExportIndex, FunctionIndex, ImportIndex, LocalFunctionIndex, OwnedDataInitializer,
16    SignatureIndex,
17};
18use wasmer_vm::Artifact;
19
20const MAGIC_HEADER: [u8; 32] = {
21    let value = *b"\0wasmer-universal\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
22    let _length_must_be_multiple_of_16: bool = [true][value.len() % 16];
23    value
24};
25
26/// A 0-copy view of the encoded `UniversalExecutable` payload.
27#[derive(Clone, Copy)]
28pub struct UniversalExecutableRef<'a> {
29    buffer: &'a [u8],
30    archive: &'a ArchivedUniversalExecutable,
31}
32
33impl<'a> std::ops::Deref for UniversalExecutableRef<'a> {
34    type Target = ArchivedUniversalExecutable;
35    fn deref(&self) -> &Self::Target {
36        self.archive
37    }
38}
39
40impl<'a> UniversalExecutableRef<'a> {
41    /// Verify the buffer for whether it is a valid `UniversalExecutable`.
42    pub fn verify_serialized(data: &[u8]) -> Result<(), &'static str> {
43        if !data.starts_with(&MAGIC_HEADER) {
44            return Err("the provided bytes are not wasmer-universal");
45        }
46        if data.len() < MAGIC_HEADER.len() + 8 {
47            return Err("the data buffer is too small to be valid");
48        }
49        let (remaining, position) = data.split_at(data.len() - 8);
50        let mut position_value = [0u8; 8];
51        position_value.copy_from_slice(position);
52        if u64::from_le_bytes(position_value) > remaining.len() as u64 {
53            return Err("the buffer is malformed");
54        }
55        // TODO(0-copy): bytecheck too.
56        Ok(())
57    }
58
59    /// # Safety
60    ///
61    /// This method is unsafe since it deserializes data directly
62    /// from memory.
63    /// Right now we are not doing any extra work for validation, but
64    /// `rkyv` has an option to do bytecheck on the serialized data before
65    /// serializing (via `rkyv::check_archived_value`).
66    pub unsafe fn deserialize(
67        data: &'a [u8],
68    ) -> Result<UniversalExecutableRef<'a>, DeserializeError> {
69        Self::verify_serialized(data).map_err(|e| DeserializeError::Incompatible(e.to_string()))?;
70        let (archive, position) = data.split_at(data.len() - 8);
71        let mut position_value = [0u8; 8];
72        position_value.copy_from_slice(position);
73        let (_, data) = archive.split_at(MAGIC_HEADER.len());
74        Ok(UniversalExecutableRef {
75            buffer: data,
76            archive: rkyv::archived_value::<UniversalExecutable>(
77                data,
78                u64::from_le_bytes(position_value) as usize,
79            ),
80        })
81    }
82
83    // TODO(0-copy): this should never fail.
84    /// Convert this reference to an owned `UniversalExecutable` value.
85    pub fn to_owned(self) -> Result<UniversalExecutable, DeserializeError> {
86        let mut deserializer = SharedDeserializeMap::new();
87        rkyv::Deserialize::deserialize(self.archive, &mut deserializer)
88            .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))
89    }
90}
91
92/// A wasm module compiled to some shape, ready to be loaded with `UniversalEngine` to produce an
93/// `UniversalArtifact`.
94///
95/// This is the result obtained after validating and compiling a WASM module with any of the
96/// supported compilers. This type falls in-between a module and [`Artifact`](crate::Artifact).
97#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
98pub struct UniversalExecutable {
99    pub(crate) function_bodies: PrimaryMap<LocalFunctionIndex, FunctionBody>,
100    pub(crate) function_relocations: PrimaryMap<LocalFunctionIndex, Vec<Relocation>>,
101    pub(crate) function_jt_offsets: PrimaryMap<LocalFunctionIndex, JumpTableOffsets>,
102    pub(crate) function_frame_info: PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>,
103    pub(crate) function_call_trampolines: PrimaryMap<SignatureIndex, FunctionBody>,
104    pub(crate) dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBody>,
105    pub(crate) custom_sections: PrimaryMap<SectionIndex, CustomSection>,
106    pub(crate) custom_section_relocations: PrimaryMap<SectionIndex, Vec<Relocation>>,
107    // The section indices corresponding to the Dwarf debug info
108    pub(crate) debug: Option<Dwarf>,
109    // the Trampoline for Arm arch
110    pub(crate) trampolines: Option<TrampolinesSection>,
111    pub(crate) compile_info: CompileModuleInfo,
112    pub(crate) data_initializers: Vec<OwnedDataInitializer>,
113    pub(crate) cpu_features: u64,
114}
115
116#[derive(thiserror::Error, Debug)]
117pub enum ExecutableSerializeError {
118    #[error("could not serialize the executable data")]
119    Executable(
120        #[source]
121        CompositeSerializerError<
122            std::convert::Infallible,
123            AllocScratchError,
124            SharedSerializeMapError,
125        >,
126    ),
127}
128
129impl wasmer_engine::Executable for UniversalExecutable {
130    fn load(
131        &self,
132        engine: &(dyn Engine + 'static),
133    ) -> Result<std::sync::Arc<dyn Artifact>, CompileError> {
134        engine
135            .downcast_ref::<crate::UniversalEngine>()
136            .ok_or(CompileError::EngineDowncast)?
137            .load_universal_executable(self)
138            .map(|a| Arc::new(a) as _)
139    }
140
141    fn features(&self) -> Features {
142        self.compile_info.features.clone()
143    }
144
145    fn cpu_features(&self) -> EnumSet<CpuFeature> {
146        EnumSet::from_u64(self.cpu_features)
147    }
148
149    fn serialize(&self) -> Result<Vec<u8>, Box<(dyn std::error::Error + Send + Sync + 'static)>> {
150        // The format is as thus:
151        //
152        // HEADER
153        // RKYV PAYLOAD
154        // RKYV POSITION
155        //
156        // It is expected that any framing for message length is handled by the caller.
157        let mut serializer = AllocSerializer::<1024>::default();
158        let pos = rkyv::ser::Serializer::serialize_value(&mut serializer, self)
159            .map_err(ExecutableSerializeError::Executable)? as u64;
160        let pos_bytes = pos.to_le_bytes();
161        let data = serializer.into_serializer().into_inner();
162        let mut out = Vec::with_capacity(MAGIC_HEADER.len() + pos_bytes.len() + data.len());
163        out.extend(&MAGIC_HEADER);
164        out.extend(data.as_slice());
165        out.extend(&pos_bytes);
166        Ok(out)
167    }
168
169    fn function_name(&self, index: FunctionIndex) -> Option<&str> {
170        let module = &self.compile_info.module;
171        // First, lets see if there's a name by which this function is exported.
172        for (name, idx) in module.exports.iter() {
173            match idx {
174                &ExportIndex::Function(fi) if fi == index => return Some(&*name),
175                _ => continue,
176            }
177        }
178        if let Some(r) = module.function_names.get(&index) {
179            return Some(&**r);
180        }
181        for ((_, field, _), idx) in module.imports.iter() {
182            match idx {
183                &ImportIndex::Function(fi) if fi == index => return Some(&*field),
184                _ => continue,
185            }
186        }
187        None
188    }
189}
190
191impl<'a> wasmer_engine::Executable for UniversalExecutableRef<'a> {
192    fn load(
193        &self,
194        engine: &(dyn Engine + 'static),
195    ) -> Result<std::sync::Arc<dyn Artifact>, CompileError> {
196        engine
197            .downcast_ref::<crate::UniversalEngine>()
198            .ok_or_else(|| CompileError::Codegen("can't downcast TODO FIXME".into()))?
199            .load_universal_executable_ref(self)
200            .map(|a| Arc::new(a) as _)
201    }
202
203    fn features(&self) -> Features {
204        unrkyv(&self.archive.compile_info.features)
205    }
206
207    fn cpu_features(&self) -> EnumSet<CpuFeature> {
208        EnumSet::from_u64(unrkyv(&self.archive.cpu_features))
209    }
210
211    fn serialize(&self) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
212        Ok(self.buffer.to_vec())
213    }
214
215    fn function_name(&self, index: FunctionIndex) -> Option<&str> {
216        let module = &self.compile_info.module;
217        // First, lets see if there's a name by which this function is exported.
218        for (name, idx) in module.exports.iter() {
219            match idx {
220                &ExportIndex::Function(fi) if fi == index => return Some(&*name),
221                _ => continue,
222            }
223        }
224        if let Some(r) = module.function_names.get(&index) {
225            return Some(&**r);
226        }
227        for ((_, field, _), idx) in module.imports.iter() {
228            match idx {
229                &ImportIndex::Function(fi) if fi == index => return Some(&*field),
230                _ => continue,
231            }
232        }
233        None
234    }
235}
236
237pub(crate) fn unrkyv<T>(archive: &T::Archived) -> T
238where
239    T: rkyv::Archive,
240    T::Archived: rkyv::Deserialize<T, rkyv::Infallible>,
241{
242    Result::<_, std::convert::Infallible>::unwrap(rkyv::Deserialize::deserialize(
243        archive,
244        &mut rkyv::Infallible,
245    ))
246}