1 //! Cranelift instructions modify the state of the machine; the [State] trait describes these
2 //! ways this can happen.
3 use crate::address::{Address, AddressSize};
4 use crate::frame::Frame;
5 use crate::interpreter::LibCallHandler;
6 use cranelift_codegen::data_value::DataValue;
7 use cranelift_codegen::ir::{
8     ExternalName, FuncRef, Function, GlobalValue, LibCall, MemFlags, Signature, StackSlot, Type,
9     Value, types,
10 };
11 use cranelift_codegen::isa::CallConv;
12 use smallvec::SmallVec;
13 use thiserror::Error;
14 
15 /// This trait manages the state necessary to interpret a single Cranelift instruction--it describes
16 /// all of the ways a Cranelift interpreter can interact with its virtual state. This makes it
17 /// possible to use the [Interpreter](crate::interpreter::Interpreter) in a range of situations:
18 /// - when interpretation needs to happen in a way isolated from the host a state which keeps a
19 ///   stack and bound checks memory accesses can be used, like
20 ///   [InterpreterState](crate::interpreter::InterpreterState).
21 /// - when interpretation needs to have access to the host a state which allows direct access to the
22 ///   host memory and native functions can be used.
23 pub trait State<'a> {
24     /// Retrieve a reference to a [Function].
25     fn get_function(&self, func_ref: FuncRef) -> Option<&'a Function>;
26     /// Retrieve a reference to the currently executing [Function].
27     fn get_current_function(&self) -> &'a Function;
28     /// Retrieve the handler callback for a [LibCall]
29     fn get_libcall_handler(&self) -> LibCallHandler;
30 
31     /// Record that an interpreter has called into a new [Function].
32     fn push_frame(&mut self, function: &'a Function);
33     /// Record that an interpreter has returned from a called [Function].
34     fn pop_frame(&mut self);
35 
36     fn current_frame_mut(&mut self) -> &mut Frame<'a>;
37     fn current_frame(&self) -> &Frame<'a>;
38 
39     /// Collect a list of values `V` by their [value references](cranelift_codegen::ir::Value).
40     fn collect_values(&self, names: &[Value]) -> SmallVec<[DataValue; 1]> {
41         let frame = self.current_frame();
42         names.into_iter().map(|n| frame.get(*n).clone()).collect()
43     }
44 
45     /// Computes the stack address for this stack slot, including an offset.
46     fn stack_address(
47         &self,
48         size: AddressSize,
49         slot: StackSlot,
50         offset: u64,
51     ) -> Result<Address, MemoryError>;
52     /// Retrieve a value `V` from memory at the given `address`, checking if it belongs either to the
53     /// stack or to one of the heaps; the number of bytes loaded corresponds to the specified [Type].
54     fn checked_load(
55         &self,
56         address: Address,
57         ty: Type,
58         mem_flags: MemFlags,
59     ) -> Result<DataValue, MemoryError>;
60     /// Store a value `V` into memory at the given `address`, checking if it belongs either to the
61     /// stack or to one of the heaps; the number of bytes stored corresponds to the specified [Type].
62     fn checked_store(
63         &mut self,
64         address: Address,
65         v: DataValue,
66         mem_flags: MemFlags,
67     ) -> Result<(), MemoryError>;
68 
69     /// Compute the address of a function given its name.
70     fn function_address(
71         &self,
72         size: AddressSize,
73         name: &ExternalName,
74     ) -> Result<Address, MemoryError>;
75 
76     /// Retrieve a reference to a [Function] given its address.
77     fn get_function_from_address(&self, address: Address) -> Option<InterpreterFunctionRef<'a>>;
78 
79     /// Given a global value, compute the final value for that global value, applying all operations
80     /// in intermediate global values.
81     fn resolve_global_value(&self, gv: GlobalValue) -> Result<DataValue, MemoryError>;
82 
83     /// Retrieves the current pinned reg value
84     fn get_pinned_reg(&self) -> DataValue;
85     /// Sets a value for the pinned reg
86     fn set_pinned_reg(&mut self, v: DataValue);
87 }
88 
89 pub enum InterpreterFunctionRef<'a> {
90     Function(&'a Function),
91     LibCall(LibCall),
92 }
93 
94 impl<'a> InterpreterFunctionRef<'a> {
95     pub fn signature(&self) -> Signature {
96         match self {
97             InterpreterFunctionRef::Function(f) => f.stencil.signature.clone(),
98             // CallConv here is sort of irrelevant, since we don't use it for anything
99             // FIXME handle non-64bit systems
100             InterpreterFunctionRef::LibCall(lc) => lc.signature(CallConv::SystemV, types::I64),
101         }
102     }
103 }
104 
105 impl<'a> From<&'a Function> for InterpreterFunctionRef<'a> {
106     fn from(f: &'a Function) -> Self {
107         InterpreterFunctionRef::Function(f)
108     }
109 }
110 
111 impl From<LibCall> for InterpreterFunctionRef<'_> {
112     fn from(lc: LibCall) -> Self {
113         InterpreterFunctionRef::LibCall(lc)
114     }
115 }
116 
117 #[derive(Error, Debug)]
118 pub enum MemoryError {
119     #[error("Invalid DataValue passed as an address: {0}")]
120     InvalidAddress(DataValue),
121     #[error("Invalid type for address: {0}")]
122     InvalidAddressType(Type),
123     #[error("Requested an the entry {entry} but only {max} entries are allowed")]
124     InvalidEntry { entry: u64, max: u64 },
125     #[error("Requested an offset of {offset} but max was {max}")]
126     InvalidOffset { offset: u64, max: u64 },
127     #[error("Load of {load_size} bytes is larger than available size at address {addr:?}")]
128     OutOfBoundsLoad {
129         addr: Address,
130         load_size: usize,
131         mem_flags: MemFlags,
132     },
133     #[error("Store of {store_size} bytes is larger than available size at address {addr:?}")]
134     OutOfBoundsStore {
135         addr: Address,
136         store_size: usize,
137         mem_flags: MemFlags,
138     },
139     #[error("Load of {load_size} bytes is misaligned at address {addr:?}")]
140     MisalignedLoad { addr: Address, load_size: usize },
141     #[error("Store of {store_size} bytes is misaligned at address {addr:?}")]
142     MisalignedStore { addr: Address, store_size: usize },
143 }
144