use std::sync::Arc;
use enumset::EnumSet;
use rkyv::de::deserializers::SharedDeserializeMap;
use rkyv::ser::serializers::{
AllocScratchError, AllocSerializer, CompositeSerializerError, SharedSerializeMapError,
};
use wasmer_compiler::{
CompileError, CompileModuleInfo, CompiledFunctionFrameInfo, CpuFeature, CustomSection, Dwarf,
Features, FunctionBody, JumpTableOffsets, Relocation, SectionIndex, TrampolinesSection,
};
use wasmer_engine::{DeserializeError, Engine};
use wasmer_types::entity::PrimaryMap;
use wasmer_types::{
ExportIndex, FunctionIndex, ImportIndex, LocalFunctionIndex, OwnedDataInitializer,
SignatureIndex,
};
use wasmer_vm::Artifact;
const MAGIC_HEADER: [u8; 32] = {
let value = *b"\0wasmer-universal\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
let _length_must_be_multiple_of_16: bool = [true][value.len() % 16];
value
};
#[derive(Clone, Copy)]
pub struct UniversalExecutableRef<'a> {
buffer: &'a [u8],
archive: &'a ArchivedUniversalExecutable,
}
impl<'a> std::ops::Deref for UniversalExecutableRef<'a> {
type Target = ArchivedUniversalExecutable;
fn deref(&self) -> &Self::Target {
self.archive
}
}
impl<'a> UniversalExecutableRef<'a> {
pub fn verify_serialized(data: &[u8]) -> Result<(), &'static str> {
if !data.starts_with(&MAGIC_HEADER) {
return Err("the provided bytes are not wasmer-universal");
}
if data.len() < MAGIC_HEADER.len() + 8 {
return Err("the data buffer is too small to be valid");
}
let (remaining, position) = data.split_at(data.len() - 8);
let mut position_value = [0u8; 8];
position_value.copy_from_slice(position);
if u64::from_le_bytes(position_value) > remaining.len() as u64 {
return Err("the buffer is malformed");
}
Ok(())
}
pub unsafe fn deserialize(
data: &'a [u8],
) -> Result<UniversalExecutableRef<'a>, DeserializeError> {
Self::verify_serialized(data).map_err(|e| DeserializeError::Incompatible(e.to_string()))?;
let (archive, position) = data.split_at(data.len() - 8);
let mut position_value = [0u8; 8];
position_value.copy_from_slice(position);
let (_, data) = archive.split_at(MAGIC_HEADER.len());
Ok(UniversalExecutableRef {
buffer: data,
archive: rkyv::archived_value::<UniversalExecutable>(
data,
u64::from_le_bytes(position_value) as usize,
),
})
}
pub fn to_owned(self) -> Result<UniversalExecutable, DeserializeError> {
let mut deserializer = SharedDeserializeMap::new();
rkyv::Deserialize::deserialize(self.archive, &mut deserializer)
.map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))
}
}
#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
pub struct UniversalExecutable {
pub(crate) function_bodies: PrimaryMap<LocalFunctionIndex, FunctionBody>,
pub(crate) function_relocations: PrimaryMap<LocalFunctionIndex, Vec<Relocation>>,
pub(crate) function_jt_offsets: PrimaryMap<LocalFunctionIndex, JumpTableOffsets>,
pub(crate) function_frame_info: PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>,
pub(crate) function_call_trampolines: PrimaryMap<SignatureIndex, FunctionBody>,
pub(crate) dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBody>,
pub(crate) custom_sections: PrimaryMap<SectionIndex, CustomSection>,
pub(crate) custom_section_relocations: PrimaryMap<SectionIndex, Vec<Relocation>>,
pub(crate) debug: Option<Dwarf>,
pub(crate) trampolines: Option<TrampolinesSection>,
pub(crate) compile_info: CompileModuleInfo,
pub(crate) data_initializers: Vec<OwnedDataInitializer>,
pub(crate) cpu_features: u64,
}
#[derive(thiserror::Error, Debug)]
pub enum ExecutableSerializeError {
#[error("could not serialize the executable data")]
Executable(
#[source]
CompositeSerializerError<
std::convert::Infallible,
AllocScratchError,
SharedSerializeMapError,
>,
),
}
impl wasmer_engine::Executable for UniversalExecutable {
fn load(
&self,
engine: &(dyn Engine + 'static),
) -> Result<std::sync::Arc<dyn Artifact>, CompileError> {
engine
.downcast_ref::<crate::UniversalEngine>()
.ok_or(CompileError::EngineDowncast)?
.load_universal_executable(self)
.map(|a| Arc::new(a) as _)
}
fn features(&self) -> Features {
self.compile_info.features.clone()
}
fn cpu_features(&self) -> EnumSet<CpuFeature> {
EnumSet::from_u64(self.cpu_features)
}
fn serialize(&self) -> Result<Vec<u8>, Box<(dyn std::error::Error + Send + Sync + 'static)>> {
let mut serializer = AllocSerializer::<1024>::default();
let pos = rkyv::ser::Serializer::serialize_value(&mut serializer, self)
.map_err(ExecutableSerializeError::Executable)? as u64;
let pos_bytes = pos.to_le_bytes();
let data = serializer.into_serializer().into_inner();
let mut out = Vec::with_capacity(MAGIC_HEADER.len() + pos_bytes.len() + data.len());
out.extend(&MAGIC_HEADER);
out.extend(data.as_slice());
out.extend(&pos_bytes);
Ok(out)
}
fn function_name(&self, index: FunctionIndex) -> Option<&str> {
let module = &self.compile_info.module;
for (name, idx) in module.exports.iter() {
match idx {
&ExportIndex::Function(fi) if fi == index => return Some(&*name),
_ => continue,
}
}
if let Some(r) = module.function_names.get(&index) {
return Some(&**r);
}
for ((_, field, _), idx) in module.imports.iter() {
match idx {
&ImportIndex::Function(fi) if fi == index => return Some(&*field),
_ => continue,
}
}
None
}
}
impl<'a> wasmer_engine::Executable for UniversalExecutableRef<'a> {
fn load(
&self,
engine: &(dyn Engine + 'static),
) -> Result<std::sync::Arc<dyn Artifact>, CompileError> {
engine
.downcast_ref::<crate::UniversalEngine>()
.ok_or_else(|| CompileError::Codegen("can't downcast TODO FIXME".into()))?
.load_universal_executable_ref(self)
.map(|a| Arc::new(a) as _)
}
fn features(&self) -> Features {
unrkyv(&self.archive.compile_info.features)
}
fn cpu_features(&self) -> EnumSet<CpuFeature> {
EnumSet::from_u64(unrkyv(&self.archive.cpu_features))
}
fn serialize(&self) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
Ok(self.buffer.to_vec())
}
fn function_name(&self, index: FunctionIndex) -> Option<&str> {
let module = &self.compile_info.module;
for (name, idx) in module.exports.iter() {
match idx {
&ExportIndex::Function(fi) if fi == index => return Some(&*name),
_ => continue,
}
}
if let Some(r) = module.function_names.get(&index) {
return Some(&**r);
}
for ((_, field, _), idx) in module.imports.iter() {
match idx {
&ImportIndex::Function(fi) if fi == index => return Some(&*field),
_ => continue,
}
}
None
}
}
pub(crate) fn unrkyv<T>(archive: &T::Archived) -> T
where
T: rkyv::Archive,
T::Archived: rkyv::Deserialize<T, rkyv::Infallible>,
{
Result::<_, std::convert::Infallible>::unwrap(rkyv::Deserialize::deserialize(
archive,
&mut rkyv::Infallible,
))
}