unc_vm_engine/universal/
executable.rs

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