sp_virtualization/
native.rs1use crate::{
19 ExecAction, ExecError, ExecOutcome, InstantiateError, MemoryError, MemoryT, VirtT, LOG_TARGET,
20};
21use polkavm::{
22 Config, Engine, GasMeteringKind, InterruptKind, Module, ModuleConfig, ProgramCounter,
23 RawInstance, Reg,
24};
25use std::{
26 cell::RefCell,
27 rc::{Rc, Weak},
28 sync::OnceLock,
29};
30
31static ENGINE: OnceLock<Engine> = OnceLock::new();
36
37fn engine() -> &'static Engine {
39 ENGINE.get_or_init(|| {
40 let config = Config::from_env().expect("Invalid config.");
41 Engine::new(&config).expect("Failed to initialize PolkaVM.")
42 })
43}
44
45pub struct Virt {
47 instance: Rc<RefCell<RawInstance>>,
54 module: Module,
56 executing: bool,
58}
59
60pub struct Memory(Weak<RefCell<RawInstance>>);
66
67impl MemoryT for Memory {
68 fn read(&mut self, offset: u32, dest: &mut [u8]) -> Result<(), MemoryError> {
69 let instance = self.0.upgrade().ok_or(MemoryError::InvalidInstance)?;
70 let mut guard = instance.borrow_mut();
71 guard
72 .read_memory_into(offset, dest)
73 .map(|_| ())
74 .map_err(|_| MemoryError::OutOfBounds)
75 }
76
77 fn write(&mut self, offset: u32, src: &[u8]) -> Result<(), MemoryError> {
78 let instance = self.0.upgrade().ok_or(MemoryError::InvalidInstance)?;
79 let mut guard = instance.borrow_mut();
80 guard.write_memory(offset, src).map_err(|_| MemoryError::OutOfBounds)
81 }
82}
83
84impl VirtT for Virt {
85 type Memory = Memory;
88
89 fn instantiate(program: &[u8]) -> Result<Self, InstantiateError> {
90 let engine = engine();
91
92 let mut module_config = ModuleConfig::new();
93 module_config.set_gas_metering(Some(GasMeteringKind::Sync));
94 let module = Module::new(&engine, &module_config, program.into()).map_err(|err| {
95 log::debug!(target: LOG_TARGET, "Failed to compile program: {}", err);
96 InstantiateError::InvalidImage
97 })?;
98
99 let instance = Rc::new(RefCell::new(module.instantiate().map_err(|err| {
100 log::debug!(target: LOG_TARGET, "Failed to instantiate program: {err}");
101 InstantiateError::InvalidImage
102 })?));
103 Ok(Self { instance, module, executing: false })
104 }
105
106 fn run(&mut self, gas_left: i64, action: ExecAction<'_>) -> Result<ExecOutcome, ExecError> {
107 {
108 let mut instance = self.instance.borrow_mut();
109 match action {
110 ExecAction::Execute(function) => {
111 if self.executing {
112 return Err(ExecError::InvalidInstance);
113 }
114 let pc = self.find_export(function)?;
115 instance.prepare_call_typed(pc, ());
116 },
117 ExecAction::Resume(return_value) => {
118 if !self.executing {
119 return Err(ExecError::InvalidInstance);
120 }
121 instance.set_reg(Reg::A0, return_value);
122 },
123 }
124 instance.set_gas(gas_left);
125 }
126 self.step()
127 }
128
129 fn memory(&self) -> Self::Memory {
130 Memory(Rc::downgrade(&self.instance))
131 }
132}
133
134impl Virt {
135 fn find_export(&self, function: &str) -> Result<ProgramCounter, ExecError> {
136 self.module
137 .exports()
138 .find(|export| export.symbol().as_bytes() == function.as_bytes())
139 .map(|export| export.program_counter())
140 .ok_or_else(|| {
141 log::debug!(
142 target: LOG_TARGET,
143 "Export not found: {function}"
144 );
145 ExecError::InvalidImage
146 })
147 }
148
149 fn step(&mut self) -> Result<ExecOutcome, ExecError> {
151 let mut instance = self.instance.borrow_mut();
152 let interrupt = instance.run().map_err(|err| {
153 self.executing = false;
154 log::error!(target: LOG_TARGET, "polkavm execution error: {}", err);
155 ExecError::InvalidImage
156 })?;
157
158 match interrupt {
159 InterruptKind::Finished => {
160 self.executing = false;
161 Ok(ExecOutcome::Finished { gas_left: instance.gas() })
162 },
163 InterruptKind::Trap => {
164 self.executing = false;
165 Err(ExecError::Trap)
166 },
167 InterruptKind::NotEnoughGas => {
168 self.executing = false;
169 Err(ExecError::OutOfGas)
170 },
171 InterruptKind::Step | InterruptKind::Segfault(_) => {
172 self.executing = false;
173 Err(ExecError::Trap)
174 },
175 InterruptKind::Ecalli(hostcall_index) => {
176 self.executing = true;
177 let syscall_symbol = self
181 .module
182 .imports()
183 .get(hostcall_index)
184 .expect("hostcall index is valid because it was generated by polkavm; qed");
185 let syscall_id = u32::from_le_bytes(
186 syscall_symbol
187 .as_bytes()
188 .try_into()
189 .expect("syscall symbols are always 4 bytes; qed"),
190 );
191
192 Ok(ExecOutcome::Syscall {
193 gas_left: instance.gas(),
194 syscall_no: syscall_id,
195 a0: instance.reg(Reg::A0),
196 a1: instance.reg(Reg::A1),
197 a2: instance.reg(Reg::A2),
198 a3: instance.reg(Reg::A3),
199 a4: instance.reg(Reg::A4),
200 a5: instance.reg(Reg::A5),
201 })
202 },
203 }
204 }
205}