lightbeam/
module.rs

1use crate::backend::TranslatedCodeSection;
2use crate::error::Error;
3use crate::microwasm;
4use crate::translate_sections;
5use cranelift_codegen::{
6    ir::{self, AbiParam, Signature as CraneliftSignature},
7    isa,
8};
9use memoffset::offset_of;
10
11use std::{convert::TryInto, mem};
12use thiserror::Error;
13use wasmparser::{FuncType, Parser, Payload, Type};
14
15pub trait AsValueType {
16    const TYPE: Type;
17}
18
19pub trait TypeList {
20    const TYPE_LIST: &'static [Type];
21}
22
23impl<T> TypeList for T
24where
25    T: AsValueType,
26{
27    const TYPE_LIST: &'static [Type] = &[T::TYPE];
28}
29
30impl AsValueType for i32 {
31    const TYPE: Type = Type::I32;
32}
33impl AsValueType for i64 {
34    const TYPE: Type = Type::I64;
35}
36impl AsValueType for u32 {
37    const TYPE: Type = Type::I32;
38}
39impl AsValueType for u64 {
40    const TYPE: Type = Type::I64;
41}
42impl AsValueType for f32 {
43    const TYPE: Type = Type::F32;
44}
45impl AsValueType for f64 {
46    const TYPE: Type = Type::F64;
47}
48
49pub trait FunctionArgs<O> {
50    type FuncType;
51
52    unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> O;
53    fn into_func(start: *const u8) -> Self::FuncType;
54}
55
56type VmCtxPtr = u64;
57
58macro_rules! impl_function_args {
59    ($first:ident $(, $rest:ident)*) => {
60        impl<Output, $first, $($rest),*> FunctionArgs<Output> for ($first, $($rest),*) {
61            type FuncType = unsafe extern "sysv64" fn(VmCtxPtr, $first $(, $rest)*) -> Output;
62
63            #[allow(non_snake_case)]
64            unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> Output {
65                let ($first, $($rest),*) = self;
66                func(vm_ctx as VmCtxPtr, $first $(, $rest)*)
67            }
68
69            fn into_func(start: *const u8) -> Self::FuncType {
70                unsafe { mem::transmute(start) }
71            }
72        }
73
74        impl<$first: AsValueType, $($rest: AsValueType),*> TypeList for ($first, $($rest),*) {
75            const TYPE_LIST: &'static [Type] = &[$first::TYPE, $($rest::TYPE),*];
76        }
77
78        impl_function_args!($($rest),*);
79    };
80    () => {
81        impl<Output> FunctionArgs<Output> for () {
82            type FuncType = unsafe extern "sysv64" fn(VmCtxPtr) -> Output;
83
84            unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> Output {
85                func(vm_ctx as VmCtxPtr)
86            }
87
88            fn into_func(start: *const u8) -> Self::FuncType {
89                unsafe { mem::transmute(start) }
90            }
91        }
92
93        impl TypeList for () {
94            const TYPE_LIST: &'static [Type] = &[];
95        }
96    };
97}
98
99impl_function_args!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
100
101#[derive(Default)]
102pub struct TranslatedModule {
103    translated_code_section: Option<TranslatedCodeSection>,
104    ctx: SimpleContext,
105    // TODO: Should we wrap this in a `Mutex` so that calling functions from multiple
106    //       threads doesn't cause data races?
107    memory: Option<(u64, Option<u64>)>,
108}
109
110impl TranslatedModule {
111    pub fn instantiate(self) -> ExecutableModule {
112        let mem_size = self.memory.map(|limits| limits.0 as u32).unwrap_or(0) as usize;
113        let mem: BoxSlice<_> = vec![0u8; mem_size * WASM_PAGE_SIZE]
114            .into_boxed_slice()
115            .into();
116
117        let ctx = if mem.len > 0 {
118            Some(Box::new(VmCtx { mem }) as Box<VmCtx>)
119        } else {
120            None
121        };
122
123        ExecutableModule {
124            module: self,
125            context: ctx,
126        }
127    }
128
129    pub fn disassemble(&self) {
130        self.translated_code_section
131            .as_ref()
132            .expect("no code section")
133            .disassemble();
134    }
135}
136
137#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)]
138pub enum ExecutionError {
139    #[error("function index out of bounds")]
140    FuncIndexOutOfBounds,
141    #[error("type mismatch")]
142    TypeMismatch,
143}
144
145pub struct ExecutableModule {
146    module: TranslatedModule,
147    context: Option<Box<VmCtx>>,
148}
149
150impl ExecutableModule {
151    /// Executes the function identified by `func_idx`.
152    ///
153    /// # Safety
154    ///
155    /// Executes the function _without_ checking the argument types.
156    /// This can cause undefined memory to be accessed.
157    pub unsafe fn execute_func_unchecked<Args: FunctionArgs<T>, T>(
158        &self,
159        func_idx: u32,
160        args: Args,
161    ) -> T {
162        let code_section = self
163            .module
164            .translated_code_section
165            .as_ref()
166            .expect("no code section");
167        let start_buf = code_section.func_start(func_idx as usize);
168
169        args.call(
170            Args::into_func(start_buf),
171            self.context
172                .as_ref()
173                .map(|ctx| (&**ctx) as *const VmCtx as *const u8)
174                .unwrap_or(std::ptr::null()),
175        )
176    }
177
178    pub fn execute_func<Args: FunctionArgs<T> + TypeList, T: TypeList>(
179        &self,
180        func_idx: u32,
181        args: Args,
182    ) -> Result<T, ExecutionError> {
183        let module = &self.module;
184
185        if func_idx as usize >= module.ctx.func_ty_indices.len() {
186            return Err(ExecutionError::FuncIndexOutOfBounds);
187        }
188
189        let type_ = module.ctx.func_type(func_idx);
190
191        // TODO: Handle "compatible" types (i.e. f32 and i32)
192        if (&type_.params[..], &type_.returns[..]) != (Args::TYPE_LIST, T::TYPE_LIST) {
193            return Err(ExecutionError::TypeMismatch);
194        }
195
196        Ok(unsafe { self.execute_func_unchecked(func_idx, args) })
197    }
198
199    pub fn disassemble(&self) {
200        self.module.disassemble();
201    }
202}
203
204struct BoxSlice<T> {
205    len: usize,
206    ptr: *mut T,
207}
208
209impl<T> From<Box<[T]>> for BoxSlice<T> {
210    fn from(mut other: Box<[T]>) -> Self {
211        let out = BoxSlice {
212            len: other.len(),
213            ptr: other.as_mut_ptr(),
214        };
215        mem::forget(other);
216        out
217    }
218}
219
220unsafe impl<T: Send> Send for BoxSlice<T> {}
221unsafe impl<T: Sync> Sync for BoxSlice<T> {}
222
223impl<T> Drop for BoxSlice<T> {
224    fn drop(&mut self) {
225        unsafe { Vec::from_raw_parts(self.ptr, self.len, self.len) };
226    }
227}
228
229type BoxByteSlice = BoxSlice<u8>;
230
231pub struct VmCtx {
232    mem: BoxByteSlice,
233}
234
235impl VmCtx {
236    pub fn offset_of_memory_ptr() -> u32 {
237        (offset_of!(VmCtx, mem) + offset_of!(BoxByteSlice, ptr))
238            .try_into()
239            .expect("Offset exceeded size of u32")
240    }
241
242    pub fn offset_of_memory_len() -> u32 {
243        (offset_of!(VmCtx, mem) + offset_of!(BoxByteSlice, len))
244            .try_into()
245            .expect("Offset exceeded size of u32")
246    }
247}
248
249#[derive(Default, Debug)]
250pub struct SimpleContext {
251    types: Vec<FuncType>,
252    func_ty_indices: Vec<u32>,
253}
254
255pub const WASM_PAGE_SIZE: usize = 65_536;
256
257pub trait Signature {
258    type Type: SigType;
259
260    fn params(&self) -> &[Self::Type];
261    fn returns(&self) -> &[Self::Type];
262}
263
264pub trait SigType {
265    fn to_microwasm_type(&self) -> microwasm::SignlessType;
266}
267
268impl SigType for ir::Type {
269    fn to_microwasm_type(&self) -> microwasm::SignlessType {
270        use crate::microwasm::{Size::*, Type::*};
271
272        if self.is_int() {
273            match self.bits() {
274                32 => Int(_32),
275                64 => Int(_64),
276                _ => unimplemented!(),
277            }
278        } else if self.is_float() {
279            match self.bits() {
280                32 => Float(_32),
281                64 => Float(_64),
282                _ => unimplemented!(),
283            }
284        } else {
285            unimplemented!()
286        }
287    }
288}
289
290impl SigType for AbiParam {
291    fn to_microwasm_type(&self) -> microwasm::SignlessType {
292        self.value_type.to_microwasm_type()
293    }
294}
295
296impl Signature for CraneliftSignature {
297    type Type = AbiParam;
298
299    fn params(&self) -> &[Self::Type] {
300        // TODO: We want to instead add the `VMContext` to the signature used by
301        //       cranelift, removing the special-casing from the internals.
302        assert_eq!(self.params[0].purpose, ir::ArgumentPurpose::VMContext);
303        // `self.params[1]` should be caller vmctx
304        assert_eq!(self.call_conv, isa::CallConv::SystemV);
305        &self.params[2..]
306    }
307
308    fn returns(&self) -> &[Self::Type] {
309        assert_eq!(self.call_conv, isa::CallConv::SystemV);
310        &self.returns
311    }
312}
313
314impl SigType for wasmparser::Type {
315    fn to_microwasm_type(&self) -> microwasm::SignlessType {
316        microwasm::Type::from_wasm(*self).unwrap()
317    }
318}
319
320impl Signature for FuncType {
321    type Type = wasmparser::Type;
322
323    fn params(&self) -> &[Self::Type] {
324        &*self.params
325    }
326
327    fn returns(&self) -> &[Self::Type] {
328        &*self.returns
329    }
330}
331
332pub trait ModuleContext {
333    type Signature: Signature;
334    type GlobalType: SigType;
335
336    fn vmctx_builtin_function(&self, index: u32) -> u32;
337    fn vmctx_vmglobal_definition(&self, index: u32) -> u32;
338    fn vmctx_vmglobal_import_from(&self, index: u32) -> u32;
339    fn vmctx_vmmemory_import_from(&self, memory_index: u32) -> u32;
340    fn vmctx_vmmemory_definition(&self, defined_memory_index: u32) -> u32;
341    fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32;
342    fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32;
343    fn vmmemory_definition_base(&self) -> u8;
344    fn vmmemory_definition_current_length(&self) -> u8;
345    fn vmctx_vmtable_import_from(&self, table_index: u32) -> u32;
346    fn vmctx_vmtable_definition(&self, defined_table_index: u32) -> u32;
347    fn vmctx_vmtable_definition_base(&self, defined_table_index: u32) -> u32;
348    fn vmctx_vmtable_definition_current_elements(&self, defined_table_index: u32) -> u32;
349    fn vmctx_vmfunction_import_body(&self, func_index: u32) -> u32;
350    fn vmctx_vmfunction_import_vmctx(&self, func_index: u32) -> u32;
351    fn vmtable_definition_base(&self) -> u8;
352    fn vmtable_definition_current_elements(&self) -> u8;
353    fn vmctx_vmshared_signature_id(&self, signature_idx: u32) -> u32;
354    fn vmcaller_checked_anyfunc_type_index(&self) -> u8;
355    fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8;
356    fn vmcaller_checked_anyfunc_vmctx(&self) -> u8;
357    fn size_of_vmcaller_checked_anyfunc(&self) -> u8;
358
359    fn defined_table_index(&self, table_index: u32) -> Option<u32>;
360    fn defined_memory_index(&self, index: u32) -> Option<u32>;
361
362    fn defined_global_index(&self, global_index: u32) -> Option<u32>;
363    fn global_type(&self, global_index: u32) -> &Self::GlobalType;
364
365    fn func_type_index(&self, func_idx: u32) -> u32;
366    fn signature(&self, index: u32) -> &Self::Signature;
367
368    fn func_index(&self, defined_func_index: u32) -> u32;
369    fn defined_func_index(&self, func_index: u32) -> Option<u32>;
370
371    fn defined_func_type(&self, defined_func_idx: u32) -> &Self::Signature {
372        self.func_type(self.func_index(defined_func_idx))
373    }
374
375    fn func_type(&self, func_idx: u32) -> &Self::Signature {
376        self.signature(self.func_type_index(func_idx))
377    }
378
379    fn emit_memory_bounds_check(&self) -> bool {
380        true
381    }
382}
383
384impl ModuleContext for SimpleContext {
385    type Signature = FuncType;
386    type GlobalType = wasmparser::Type;
387
388    // TODO: We don't support external functions yet
389    fn func_index(&self, func_idx: u32) -> u32 {
390        func_idx
391    }
392
393    fn defined_func_index(&self, func_idx: u32) -> Option<u32> {
394        Some(func_idx)
395    }
396
397    fn func_type_index(&self, func_idx: u32) -> u32 {
398        self.func_ty_indices[func_idx as usize]
399    }
400
401    fn defined_global_index(&self, _index: u32) -> Option<u32> {
402        unimplemented!()
403    }
404
405    fn global_type(&self, _global_index: u32) -> &Self::GlobalType {
406        unimplemented!()
407    }
408
409    fn signature(&self, index: u32) -> &Self::Signature {
410        &self.types[index as usize]
411    }
412
413    fn vmctx_vmglobal_definition(&self, _index: u32) -> u32 {
414        unimplemented!()
415    }
416
417    fn vmctx_vmglobal_import_from(&self, _index: u32) -> u32 {
418        unimplemented!()
419    }
420
421    fn defined_memory_index(&self, _index: u32) -> Option<u32> {
422        unimplemented!()
423    }
424
425    fn defined_table_index(&self, index: u32) -> Option<u32> {
426        Some(index)
427    }
428
429    fn vmctx_builtin_function(&self, _index: u32) -> u32 {
430        unimplemented!()
431    }
432
433    fn vmctx_vmfunction_import_body(&self, _func_index: u32) -> u32 {
434        unimplemented!()
435    }
436    fn vmctx_vmfunction_import_vmctx(&self, _func_index: u32) -> u32 {
437        unimplemented!()
438    }
439
440    fn vmctx_vmtable_import_from(&self, _table_index: u32) -> u32 {
441        unimplemented!()
442    }
443
444    fn vmctx_vmmemory_definition(&self, _defined_memory_index: u32) -> u32 {
445        unimplemented!()
446    }
447    fn vmctx_vmmemory_import_from(&self, _memory_index: u32) -> u32 {
448        unimplemented!()
449    }
450    fn vmmemory_definition_base(&self) -> u8 {
451        unimplemented!()
452    }
453    fn vmmemory_definition_current_length(&self) -> u8 {
454        unimplemented!()
455    }
456    fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32 {
457        assert_eq!(defined_memory_index, 0);
458        VmCtx::offset_of_memory_ptr()
459    }
460
461    fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32 {
462        assert_eq!(defined_memory_index, 0);
463        VmCtx::offset_of_memory_len()
464    }
465
466    fn vmctx_vmtable_definition(&self, _defined_table_index: u32) -> u32 {
467        unimplemented!()
468    }
469
470    fn vmctx_vmtable_definition_base(&self, _defined_table_index: u32) -> u32 {
471        unimplemented!()
472    }
473
474    fn vmctx_vmtable_definition_current_elements(&self, _defined_table_index: u32) -> u32 {
475        unimplemented!()
476    }
477
478    fn vmtable_definition_base(&self) -> u8 {
479        unimplemented!()
480    }
481
482    fn vmtable_definition_current_elements(&self) -> u8 {
483        unimplemented!()
484    }
485
486    fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
487        unimplemented!()
488    }
489
490    fn vmcaller_checked_anyfunc_type_index(&self) -> u8 {
491        unimplemented!()
492    }
493
494    fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
495        unimplemented!()
496    }
497
498    fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
499        unimplemented!()
500    }
501
502    fn vmctx_vmshared_signature_id(&self, _signature_idx: u32) -> u32 {
503        unimplemented!()
504    }
505
506    // TODO: type of a global
507}
508
509pub fn translate(data: &[u8]) -> Result<ExecutableModule, Error> {
510    translate_only(data).map(|m| m.instantiate())
511}
512
513/// Translate from a slice of bytes holding a wasm module.
514pub fn translate_only(data: &[u8]) -> Result<TranslatedModule, Error> {
515    let mut output = TranslatedModule::default();
516
517    for payload in Parser::new(0).parse_all(data) {
518        match payload? {
519            Payload::TypeSection(s) => output.ctx.types = translate_sections::type_(s)?,
520            Payload::ImportSection(s) => translate_sections::import(s)?,
521            Payload::FunctionSection(s) => {
522                output.ctx.func_ty_indices = translate_sections::function(s)?;
523            }
524            Payload::TableSection(s) => {
525                translate_sections::table(s)?;
526            }
527            Payload::MemorySection(s) => {
528                let mem = translate_sections::memory(s)?;
529
530                if mem.len() > 1 {
531                    return Err(Error::Input(
532                        "Multiple memory sections not yet implemented".to_string(),
533                    ));
534                }
535
536                if !mem.is_empty() {
537                    let mem = mem[0];
538                    if Some(mem.initial) != mem.maximum {
539                        return Err(Error::Input(
540                            "Custom memory limits not supported in lightbeam".to_string(),
541                        ));
542                    }
543                    output.memory = Some((mem.initial, mem.maximum));
544                }
545            }
546            Payload::GlobalSection(s) => {
547                translate_sections::global(s)?;
548            }
549            Payload::ExportSection(s) => {
550                translate_sections::export(s)?;
551            }
552            Payload::StartSection { func, .. } => {
553                translate_sections::start(func)?;
554            }
555            Payload::ElementSection(s) => {
556                translate_sections::element(s)?;
557            }
558            Payload::DataSection(s) => {
559                translate_sections::data(s)?;
560            }
561            Payload::CodeSectionStart { .. }
562            | Payload::CustomSection { .. }
563            | Payload::Version { .. } => {}
564
565            other => unimplemented!("can't translate {:?}", other),
566        }
567    }
568
569    Ok(output)
570}