polkavm/
api.rs

1use alloc::boxed::Box;
2use alloc::format;
3use alloc::string::{String, ToString};
4use alloc::sync::Arc;
5use alloc::vec::Vec;
6
7use polkavm_common::abi::{MemoryMap, MemoryMapBuilder, VM_ADDR_RETURN_TO_HOST};
8use polkavm_common::cast::cast;
9use polkavm_common::program::{FrameKind, Imports, InstructionSetKind, Instructions, JumpTable, ProgramBlob, Reg};
10use polkavm_common::utils::{ArcBytes, AsUninitSliceMut, B32, B64};
11
12use crate::config::{BackendKind, Config, GasMeteringKind, ModuleConfig, SandboxKind};
13use crate::error::{bail, Error};
14use crate::gas::{CostModel, CostModelKind, GasVisitor};
15use crate::interpreter::InterpretedInstance;
16use crate::utils::{GuestInit, InterruptKind};
17use crate::{Gas, ProgramCounter};
18
19#[cfg(feature = "module-cache")]
20use crate::module_cache::{ModuleCache, ModuleKey};
21
22#[derive(Copy, Clone, PartialEq, Eq, Debug)]
23pub enum MemoryProtection {
24    Read,
25    ReadWrite,
26}
27
28if_compiler_is_supported! {
29    {
30        use crate::sandbox::{Sandbox, SandboxInstance};
31        use crate::compiler::{CompiledModule, CompilerCache};
32
33        #[cfg(target_os = "linux")]
34        use crate::sandbox::linux::Sandbox as SandboxLinux;
35        #[cfg(feature = "generic-sandbox")]
36        use crate::sandbox::generic::Sandbox as SandboxGeneric;
37
38        pub(crate) struct EngineState {
39            pub(crate) sandboxing_enabled: bool,
40            pub(crate) sandbox_global: Option<crate::sandbox::GlobalStateKind>,
41            compiler_cache: CompilerCache,
42            imperfect_logger_filtering_workaround: bool,
43            #[cfg(feature = "module-cache")]
44            module_cache: ModuleCache,
45        }
46    } else {
47        pub(crate) struct EngineState {
48            imperfect_logger_filtering_workaround: bool,
49            #[cfg(feature = "module-cache")]
50            module_cache: ModuleCache,
51        }
52    }
53}
54
55trait IntoResult<T> {
56    fn into_result(self, message: &str) -> Result<T, Error>;
57}
58
59if_compiler_is_supported! {
60    #[cfg(target_os = "linux")]
61    impl<T> IntoResult<T> for Result<T, polkavm_linux_raw::Error> {
62        fn into_result(self, message: &str) -> Result<T, Error> {
63            self.map_err(|error| Error::from(error).context(message))
64        }
65    }
66
67    #[cfg(feature = "generic-sandbox")]
68    use crate::sandbox::generic;
69
70    #[cfg(feature = "generic-sandbox")]
71    impl<T> IntoResult<T> for Result<T, generic::Error> {
72        fn into_result(self, message: &str) -> Result<T, Error> {
73            self.map_err(|error| Error::from(error).context(message))
74        }
75    }
76}
77
78impl<T> IntoResult<T> for T {
79    fn into_result(self, _message: &str) -> Result<T, Error> {
80        Ok(self)
81    }
82}
83
84pub type RegValue = u64;
85
86#[allow(clippy::exhaustive_structs)]
87#[derive(Copy, Clone, PartialEq, Eq, Debug)]
88pub struct SetCacheSizeLimitArgs {
89    pub max_block_size: u32,
90    pub max_cache_size_bytes: usize,
91}
92
93pub struct Engine {
94    selected_backend: BackendKind,
95    #[allow(dead_code)]
96    selected_sandbox: Option<SandboxKind>,
97    interpreter_enabled: bool,
98    crosscheck: bool,
99    state: Arc<EngineState>,
100    allow_dynamic_paging: bool,
101    allow_experimental: bool,
102    default_cost_model: CostModelKind,
103}
104
105impl Engine {
106    pub fn new(config: &Config) -> Result<Self, Error> {
107        if_compiler_is_supported! {
108            crate::sandbox::init_native_page_size();
109        }
110
111        if let Some(backend) = config.backend {
112            if !backend.is_supported() {
113                bail!("the '{backend}' backend is not supported on this platform")
114            }
115        }
116
117        if !config.allow_experimental && config.crosscheck {
118            bail!("cannot enable execution cross-checking: `set_allow_experimental`/`POLKAVM_ALLOW_EXPERIMENTAL` is not enabled");
119        }
120
121        if !config.sandboxing_enabled {
122            if !config.allow_experimental {
123                bail!("cannot disable security sandboxing: `set_allow_experimental`/`POLKAVM_ALLOW_EXPERIMENTAL` is not enabled");
124            } else {
125                log::warn!("SECURITY SANDBOXING IS DISABLED; THIS IS UNSUPPORTED; YOU HAVE BEEN WARNED");
126            }
127        }
128
129        if !matches!(config.default_cost_model, None | Some(CostModelKind::Full(..))) && !config.allow_experimental {
130            bail!("cannot override the default gas cost model: `set_allow_experimental`/`POLKAVM_ALLOW_EXPERIMENTAL` is not enabled");
131        }
132
133        let crosscheck = config.crosscheck;
134        let default_backend = if BackendKind::Compiler.is_supported() && SandboxKind::Linux.is_supported() {
135            BackendKind::Compiler
136        } else {
137            BackendKind::Interpreter
138        };
139
140        let selected_backend = config.backend.unwrap_or(default_backend);
141        log::debug!("Selected backend: '{selected_backend}'");
142
143        #[cfg(feature = "module-cache")]
144        let module_cache = {
145            log::debug!("Enabling module cache... (LRU cache size = {})", config.lru_cache_size);
146            ModuleCache::new(config.cache_enabled, config.lru_cache_size)
147        };
148
149        #[cfg(not(feature = "module-cache"))]
150        if config.cache_enabled {
151            log::warn!("`cache_enabled` is true, but we were not compiled with the `module-cache` feature; caching will be disabled!");
152        }
153
154        let (selected_sandbox, state) = if_compiler_is_supported! {
155            {
156                if selected_backend == BackendKind::Compiler {
157                    let default_sandbox = if SandboxKind::Linux.is_supported() {
158                        SandboxKind::Linux
159                    } else {
160                        SandboxKind::Generic
161                    };
162
163                    let selected_sandbox = config.sandbox.unwrap_or(default_sandbox);
164                    log::debug!("Selected sandbox: '{selected_sandbox}'");
165
166                    if !selected_sandbox.is_supported() {
167                        bail!("the '{selected_sandbox}' backend is not supported on this platform")
168                    }
169
170                    if selected_sandbox == SandboxKind::Generic && !config.allow_experimental {
171                        bail!("cannot use the '{selected_sandbox}' sandbox: this sandbox is not production ready and may be insecure; you can enabled `set_allow_experimental`/`POLKAVM_ALLOW_EXPERIMENTAL` to be able to use it anyway");
172                    }
173
174                    let sandbox_global = crate::sandbox::GlobalStateKind::new(selected_sandbox, config)?;
175
176                    let state = Arc::new(EngineState {
177                        sandboxing_enabled: config.sandboxing_enabled,
178                        sandbox_global: Some(sandbox_global),
179                        compiler_cache: Default::default(),
180
181                        imperfect_logger_filtering_workaround: config.imperfect_logger_filtering_workaround,
182                        #[cfg(feature = "module-cache")]
183                        module_cache,
184                    });
185
186                    (Some(selected_sandbox), state)
187                } else {
188                    (None, Arc::new(EngineState {
189                        sandboxing_enabled: config.sandboxing_enabled,
190                        sandbox_global: None,
191                        compiler_cache: Default::default(),
192
193                        imperfect_logger_filtering_workaround: config.imperfect_logger_filtering_workaround,
194                        #[cfg(feature = "module-cache")]
195                        module_cache
196                    }))
197                }
198            } else {
199                (None, Arc::new(EngineState {
200                    imperfect_logger_filtering_workaround: config.imperfect_logger_filtering_workaround,
201                    #[cfg(feature = "module-cache")]
202                    module_cache
203                }))
204            }
205        };
206
207        Ok(Engine {
208            selected_backend,
209            selected_sandbox,
210            interpreter_enabled: crosscheck || selected_backend == BackendKind::Interpreter,
211            crosscheck,
212            state,
213            allow_dynamic_paging: config.allow_dynamic_paging(),
214            allow_experimental: config.allow_experimental,
215            default_cost_model: config
216                .default_cost_model
217                .clone()
218                .unwrap_or(CostModelKind::Simple(CostModel::naive_ref())),
219        })
220    }
221
222    /// Returns the backend used by the engine.
223    pub fn backend(&self) -> BackendKind {
224        self.selected_backend
225    }
226
227    /// Returns the PIDs of the idle worker processes. Only useful for debugging.
228    pub fn idle_worker_pids(&self) -> Vec<u32> {
229        if_compiler_is_supported! {
230            {
231                self.state.sandbox_global.as_ref().map(|global| global.idle_worker_pids()).unwrap_or_default()
232            } else {
233                Vec::new()
234            }
235        }
236    }
237}
238
239if_compiler_is_supported! {
240    {
241        pub(crate) enum CompiledModuleKind {
242            #[cfg(target_os = "linux")]
243            Linux(CompiledModule<SandboxLinux>),
244            #[cfg(feature = "generic-sandbox")]
245            Generic(CompiledModule<SandboxGeneric>),
246            Unavailable,
247        }
248    } else {
249        pub(crate) enum CompiledModuleKind {
250            Unavailable,
251        }
252    }
253}
254
255impl CompiledModuleKind {
256    pub fn is_some(&self) -> bool {
257        !matches!(self, CompiledModuleKind::Unavailable)
258    }
259}
260
261pub(crate) struct ModulePrivate {
262    #[allow(dead_code)]
263    engine_state: Option<Arc<EngineState>>,
264    #[allow(dead_code)]
265    crosscheck: bool,
266
267    blob: ProgramBlob,
268    compiled_module: CompiledModuleKind,
269    memory_map: MemoryMap,
270    gas_metering: Option<GasMeteringKind>,
271    is_strict: bool,
272    step_tracing: bool,
273    dynamic_paging: bool,
274    page_size_mask: u32,
275    page_shift: u32,
276    cost_model: CostModelKind,
277    #[cfg(feature = "module-cache")]
278    pub(crate) module_key: Option<ModuleKey>,
279
280    is_per_instruction_metering: bool,
281}
282
283/// A compiled PolkaVM program module.
284#[derive(Clone)]
285pub struct Module(pub(crate) Option<Arc<ModulePrivate>>);
286
287impl Drop for Module {
288    fn drop(&mut self) {
289        #[cfg(feature = "module-cache")]
290        if let Some(state) = self.0.take() {
291            if let Some(ref engine_state) = state.engine_state {
292                let engine_state = Arc::clone(engine_state);
293                engine_state.module_cache.on_drop(state);
294            }
295        }
296    }
297}
298
299impl Module {
300    fn state(&self) -> &ModulePrivate {
301        if let Some(ref private) = self.0 {
302            private
303        } else {
304            // SAFETY: self.0 is only ever `None` in the destructor.
305            unsafe { core::hint::unreachable_unchecked() }
306        }
307    }
308
309    fn engine_state_pointer(&self) -> *const EngineState {
310        self.state().engine_state.as_ref().map(Arc::as_ptr).unwrap_or(core::ptr::null())
311    }
312
313    pub(crate) fn is_per_instruction_metering(&self) -> bool {
314        self.state().is_per_instruction_metering
315    }
316
317    pub(crate) fn is_strict(&self) -> bool {
318        self.state().is_strict
319    }
320
321    pub(crate) fn is_step_tracing(&self) -> bool {
322        self.state().step_tracing
323    }
324
325    pub(crate) fn is_dynamic_paging(&self) -> bool {
326        self.state().dynamic_paging
327    }
328
329    if_compiler_is_supported! {
330        pub(crate) fn compiled_module(&self) -> &CompiledModuleKind {
331            &self.state().compiled_module
332        }
333    }
334
335    pub(crate) fn blob(&self) -> &ProgramBlob {
336        &self.state().blob
337    }
338
339    pub(crate) fn code_len(&self) -> u32 {
340        cast(self.state().blob.code().len()).assert_always_fits_in_u32()
341    }
342
343    pub(crate) fn instructions_bounded_at(&self, offset: ProgramCounter) -> Instructions<InstructionSetKind> {
344        self.state().blob.instructions_bounded_at(offset)
345    }
346
347    pub(crate) fn is_jump_target_valid(&self, offset: ProgramCounter) -> bool {
348        self.state().blob.is_jump_target_valid(self.state().blob.isa(), offset)
349    }
350
351    pub(crate) fn find_start_of_basic_block(&self, offset: ProgramCounter) -> Option<ProgramCounter> {
352        polkavm_common::program::find_start_of_basic_block(
353            self.state().blob.isa(),
354            self.state().blob.code(),
355            self.state().blob.bitmask(),
356            offset.0,
357        )
358        .map(ProgramCounter)
359    }
360
361    pub(crate) fn jump_table(&self) -> JumpTable {
362        self.state().blob.jump_table()
363    }
364
365    pub fn get_debug_string(&self, offset: u32) -> Result<&str, polkavm_common::program::ProgramParseError> {
366        self.state().blob.get_debug_string(offset)
367    }
368
369    pub(crate) fn gas_metering(&self) -> Option<GasMeteringKind> {
370        self.state().gas_metering
371    }
372
373    pub(crate) fn is_multiple_of_page_size(&self, value: u32) -> bool {
374        (value & self.state().page_size_mask) == 0
375    }
376
377    pub(crate) fn round_to_page_size_down(&self, value: u32) -> u32 {
378        value & !self.state().page_size_mask
379    }
380
381    pub(crate) fn round_to_page_size_up(&self, value: u32) -> u32 {
382        self.round_to_page_size_down(value) + (u32::from((value & self.state().page_size_mask) != 0) << self.state().page_shift)
383    }
384
385    /// Returns the cost model associated with this module.
386    pub fn cost_model(&self) -> &CostModelKind {
387        &self.state().cost_model
388    }
389
390    if_compiler_is_supported! {
391        pub(crate) fn address_to_page(&self, address: u32) -> u32 {
392            address >> self.state().page_shift
393        }
394    }
395
396    /// Creates a new module by deserializing the program from the given `bytes`.
397    pub fn new(engine: &Engine, config: &ModuleConfig, bytes: ArcBytes) -> Result<Self, CompileError> {
398        let blob = ProgramBlob::parse(bytes).map_err(|error| CompileError::ValidationFailed(error.into()))?;
399        Self::from_blob(engine, config, blob)
400    }
401
402    /// Creates a new module from a deserialized program `blob`.
403    pub fn from_blob(engine: &Engine, config: &ModuleConfig, blob: ProgramBlob) -> Result<Self, CompileError> {
404        if config.dynamic_paging() && !engine.allow_dynamic_paging {
405            return Err(
406                Error::from_static_str("dynamic paging was not enabled; use `Config::set_allow_dynamic_paging` to enable it").into(),
407            );
408        }
409
410        if config.custom_codegen.is_some() && !engine.allow_experimental {
411            return Err(Error::from_static_str(
412                "cannot use custom codegen: `set_allow_experimental`/`POLKAVM_ALLOW_EXPERIMENTAL` is not enabled",
413            )
414            .into());
415        }
416
417        if config.is_per_instruction_metering && engine.selected_backend == BackendKind::Compiler {
418            return Err(Error::from_static_str("per instruction metering is not supported with the recompiler").into());
419        }
420
421        log::trace!(
422            "Creating new module from a {}-bit program blob",
423            if blob.is_64_bit() { 64 } else { 32 }
424        );
425
426        let cost_model = config.cost_model.clone().unwrap_or_else(|| engine.default_cost_model.clone());
427        if config.is_per_instruction_metering && !cost_model.is_naive() {
428            return Err(Error::from_static_str("per instruction metering is not supported with a non-naive gas cost model").into());
429        }
430
431        // TODO: Use cpuid instead so that we don't have to gate this to 'std'-only.
432        #[cfg(all(target_arch = "x86_64", feature = "std"))]
433        if engine.selected_backend == BackendKind::Compiler && !std::is_x86_feature_detected!("bmi1") {
434            return Err(Error::from_static_str("on AMD64 the compiler backend requires a CPU with BMI1 support").into());
435        }
436
437        #[cfg(all(target_arch = "x86_64", feature = "std"))]
438        if matches!(cost_model, CostModelKind::Full(..)) && !std::is_x86_feature_detected!("avx2") {
439            return Err(Error::from_static_str("on AMD64 the full gas cost model is only supported on CPUs with AVX2 support").into());
440        }
441
442        if engine.selected_backend == BackendKind::Interpreter && matches!(blob.isa(), InstructionSetKind::JamV1) {
443            if let Err(pc) = blob.validate_code_with_isa(polkavm_common::program::ISA_JamV1) {
444                return Err(CompileError::ValidationFailed(format!("validation failed at offset {pc}")));
445            }
446        }
447
448        #[cfg(feature = "module-cache")]
449        let module_key = {
450            let (module_key, module) = engine.state.module_cache.get(config, &blob, &cost_model);
451            if let Some(module) = module {
452                return Ok(module);
453            }
454            module_key
455        };
456
457        // Do an early check for memory config validity.
458        MemoryMapBuilder::new(config.page_size)
459            .ro_data_size(blob.ro_data_size())
460            .rw_data_size(blob.rw_data_size())
461            .stack_size(blob.stack_size())
462            .aux_data_size(config.aux_data_size())
463            .build()
464            .map_err(|error| CompileError::ValidationFailed(error.into()))?;
465
466        if config.is_strict || cfg!(debug_assertions) {
467            log::trace!("Checking imports...");
468            for (nth_import, import) in blob.imports().into_iter().enumerate() {
469                if let Some(ref import) = import {
470                    log::trace!("  Import #{}: {}", nth_import, import);
471                } else {
472                    log::trace!("  Import #{}: INVALID", nth_import);
473                    if config.is_strict {
474                        return Err(Error::from_static_str("found an invalid import").into());
475                    }
476                }
477            }
478
479            log::trace!("Checking jump table...");
480            for (nth_entry, code_offset) in blob.jump_table().iter().enumerate() {
481                if cast(code_offset.0).to_usize() >= blob.code().len() {
482                    log::trace!(
483                        "  Invalid jump table entry #{nth_entry}: {code_offset} (should be less than {})",
484                        blob.code().len()
485                    );
486                    if config.is_strict {
487                        return Err(Error::from_static_str("out of range jump table entry found").into());
488                    }
489                }
490            }
491        };
492
493        if_compiler_is_supported! {
494            let exports = {
495                log::trace!("Parsing exports...");
496                let mut exports = Vec::with_capacity(1);
497                for export in blob.exports() {
498                    log::trace!("  Export at {}: {}", export.program_counter(), export.symbol());
499                    if config.is_strict && cast(export.program_counter().0).to_usize() >= blob.code().len() {
500                        return Err(Error::from_display(format!(
501                            "out of range export found; export {} points to code offset {}, while the code blob is only {} bytes",
502                            export.symbol(),
503                            export.program_counter(),
504                            blob.code().len(),
505                        )).into());
506                    }
507
508                    exports.push(export);
509                }
510                exports
511            };
512        }
513
514        let init = GuestInit {
515            page_size: config.page_size,
516            ro_data: blob.ro_data(),
517            rw_data: blob.rw_data(),
518            ro_data_size: blob.ro_data_size(),
519            rw_data_size: blob.rw_data_size(),
520            stack_size: blob.stack_size(),
521            aux_data_size: config.aux_data_size(),
522        };
523
524        #[allow(unused_macros)]
525        macro_rules! compile_module {
526            ($sandbox_kind:ident, $bitness_kind:ident, $build_static_dispatch_table:ident, $visitor_name:ident, $module_kind:ident) => {
527                match cost_model {
528                    CostModelKind::Simple(ref cost_model) => {
529                        compile_module!(
530                            $sandbox_kind,
531                            $bitness_kind,
532                            $build_static_dispatch_table,
533                            $visitor_name,
534                            $module_kind,
535                            GasVisitor,
536                            GasVisitor,
537                            GasVisitor::new(cost_model.clone())
538                        )
539                    }
540                    CostModelKind::Full(cost_model) => {
541                        use polkavm_common::simulator::Simulator;
542                        let gas_visitor = Simulator::<$bitness_kind, ()>::new(blob.code(), blob.isa(), cost_model, ());
543                        compile_module!(
544                            $sandbox_kind,
545                            $bitness_kind,
546                            $build_static_dispatch_table,
547                            $visitor_name,
548                            $module_kind,
549                            Simulator::<'a, $bitness_kind, ()>,
550                            Simulator::<$bitness_kind, ()>,
551                            gas_visitor
552                        )
553                    }
554                }
555            };
556
557            ($sandbox_kind:ident, $bitness_kind:ident, $build_static_dispatch_table:ident, $visitor_name:ident, $module_kind:ident, $gas_kind:ty, $gas_kind_no_lifetime:ty, $gas_visitor:expr) => {{
558                type VisitorTy<'a> = crate::compiler::CompilerVisitor<'a, $sandbox_kind, $bitness_kind, $gas_kind>;
559                let (mut visitor, aux) = crate::compiler::CompilerVisitor::<$sandbox_kind, $bitness_kind, $gas_kind_no_lifetime>::new(
560                    &engine.state.compiler_cache,
561                    config,
562                    blob.isa(),
563                    blob.jump_table(),
564                    blob.code(),
565                    blob.bitmask(),
566                    &exports,
567                    config.step_tracing || engine.crosscheck,
568                    cast(blob.code().len()).assert_always_fits_in_u32(),
569                    init,
570                    $gas_visitor,
571                )?;
572
573                blob.visit(
574                    polkavm_common::program::$build_static_dispatch_table!($visitor_name, VisitorTy<'a>),
575                    &mut visitor,
576                );
577
578                let global = $sandbox_kind::downcast_global_state(engine.state.sandbox_global.as_ref().unwrap());
579                let module = visitor.finish_compilation(global, &engine.state.compiler_cache, aux)?;
580                Some(CompiledModuleKind::$module_kind(module))
581            }};
582        }
583
584        let compiled_module: Option<CompiledModuleKind> = if_compiler_is_supported! {
585            {
586                if engine.selected_backend == BackendKind::Compiler {
587                    if let Some(selected_sandbox) = engine.selected_sandbox {
588                        match selected_sandbox {
589                            SandboxKind::Linux => {
590                                #[cfg(target_os = "linux")]
591                                match blob.isa() {
592                                    InstructionSetKind::ReviveV1 => compile_module!(SandboxLinux, B64, build_static_dispatch_table_revive_v1, COMPILER_VISITOR_LINUX, Linux),
593                                    InstructionSetKind::JamV1 => compile_module!(SandboxLinux, B64, build_static_dispatch_table_jam_v1, COMPILER_VISITOR_LINUX, Linux),
594                                    InstructionSetKind::Latest32 => compile_module!(SandboxLinux, B32, build_static_dispatch_table_latest32, COMPILER_VISITOR_LINUX, Linux),
595                                    InstructionSetKind::Latest64 => compile_module!(SandboxLinux, B64, build_static_dispatch_table_latest64, COMPILER_VISITOR_LINUX, Linux),
596                                }
597
598                                #[cfg(not(target_os = "linux"))]
599                                {
600                                    log::debug!("Selecetd sandbox unavailable: 'linux'");
601                                    None
602                                }
603                            },
604                            SandboxKind::Generic => {
605                                #[cfg(feature = "generic-sandbox")]
606                                match blob.isa() {
607                                    InstructionSetKind::ReviveV1 => compile_module!(SandboxGeneric, B64, build_static_dispatch_table_revive_v1, COMPILER_VISITOR_GENERIC, Generic),
608                                    InstructionSetKind::JamV1 => compile_module!(SandboxGeneric, B64, build_static_dispatch_table_jam_v1, COMPILER_VISITOR_GENERIC, Generic),
609                                    InstructionSetKind::Latest32 => compile_module!(SandboxGeneric, B32, build_static_dispatch_table_latest32, COMPILER_VISITOR_GENERIC, Generic),
610                                    InstructionSetKind::Latest64 => compile_module!(SandboxGeneric, B64, build_static_dispatch_table_latest64, COMPILER_VISITOR_GENERIC, Generic),
611                                }
612
613                                #[cfg(not(feature = "generic-sandbox"))]
614                                {
615                                    log::debug!("Selected sandbox unavailable: 'generic'");
616                                    None
617                                }
618                            },
619                        }
620                    } else {
621                        None
622                    }
623                } else {
624                    None
625                }
626            } else {{
627                None
628            }}
629        };
630
631        let compiled_module = compiled_module.unwrap_or(CompiledModuleKind::Unavailable);
632        log::trace!("Processing finished!");
633
634        assert!(compiled_module.is_some() || engine.interpreter_enabled);
635        if compiled_module.is_some() {
636            log::debug!("Backend used: 'compiled'");
637        } else {
638            log::debug!("Backend used: 'interpreted'");
639        }
640
641        let memory_map = init.memory_map().map_err(|error| CompileError::ValidationFailed(error.into()))?;
642        log::debug!(
643            "  Memory map: RO data: 0x{:08x}..0x{:08x} ({}/{} bytes, non-zero until 0x{:08x})",
644            memory_map.ro_data_range().start,
645            memory_map.ro_data_range().end,
646            blob.ro_data().len(),
647            memory_map.ro_data_range().len(),
648            cast(memory_map.ro_data_range().start).to_usize() + blob.ro_data().len(),
649        );
650        log::debug!(
651            "  Memory map: RW data: 0x{:08x}..0x{:08x} ({}/{} bytes, non-zero until 0x{:08x})",
652            memory_map.rw_data_range().start,
653            memory_map.rw_data_range().end,
654            blob.rw_data().len(),
655            memory_map.rw_data_range().len(),
656            cast(memory_map.rw_data_range().start).to_usize() + blob.rw_data().len(),
657        );
658        log::debug!(
659            "  Memory map:   Stack: 0x{:08x}..0x{:08x} ({}/{} bytes)",
660            memory_map.stack_range().start,
661            memory_map.stack_range().end,
662            blob.stack_size(),
663            memory_map.stack_range().len(),
664        );
665        log::debug!(
666            "  Memory map:     Aux: 0x{:08x}..0x{:08x} ({}/{} bytes requested)",
667            memory_map.aux_data_range().start,
668            memory_map.aux_data_range().end,
669            config.aux_data_size(),
670            memory_map.aux_data_range().len(),
671        );
672
673        let page_shift = memory_map.page_size().ilog2();
674        let page_size_mask = (1 << page_shift) - 1;
675
676        let module = Arc::new(ModulePrivate {
677            engine_state: Some(Arc::clone(&engine.state)),
678
679            blob,
680            compiled_module,
681            memory_map,
682            gas_metering: config.gas_metering,
683            is_strict: config.is_strict,
684            step_tracing: config.step_tracing,
685            dynamic_paging: config.dynamic_paging,
686            crosscheck: engine.crosscheck,
687            page_size_mask,
688            page_shift,
689            cost_model,
690            is_per_instruction_metering: config.is_per_instruction_metering,
691
692            #[cfg(feature = "module-cache")]
693            module_key,
694        });
695
696        #[cfg(feature = "module-cache")]
697        if let Some(module_key) = module_key {
698            return Ok(engine.state.module_cache.insert(module_key, module));
699        }
700
701        Ok(Module(Some(module)))
702    }
703
704    /// Returns whether the module is 64-bit.
705    pub fn is_64_bit(&self) -> bool {
706        self.state().blob.is_64_bit()
707    }
708
709    /// Fetches a cached module for the given `blob`.
710    #[cfg_attr(not(feature = "module-cache"), allow(unused_variables))]
711    pub fn from_cache(engine: &Engine, config: &ModuleConfig, blob: &ProgramBlob) -> Option<Self> {
712        #[cfg(feature = "module-cache")]
713        {
714            let cost_model = config.cost_model.clone().unwrap_or_else(|| engine.default_cost_model.clone());
715            let (_, module) = engine.state.module_cache.get(config, blob, &cost_model);
716            module
717        }
718
719        #[cfg(not(feature = "module-cache"))]
720        None
721    }
722
723    /// Instantiates a new module.
724    pub fn instantiate(&self) -> Result<RawInstance, Error> {
725        self.instantiate_impl(None)
726    }
727
728    /// Instantiates a new module with a given outer instance.
729    ///
730    /// This should be used over plain `instantiate` in cases where the given `outer_instance`
731    /// is going to control the newly spawned instance.
732    ///
733    /// Currently this makes no functional difference and only affects performance.
734    ///
735    /// The outer instance *must* come from the same `Engine`.
736    pub fn instantiate_nested(&self, outer_instance: &RawInstance) -> Result<RawInstance, Error> {
737        self.instantiate_impl(Some(outer_instance))
738    }
739
740    fn instantiate_impl(&self, outer_instance: Option<&RawInstance>) -> Result<RawInstance, Error> {
741        let compiled_module = &self.state().compiled_module;
742        let Some(engine_state) = self.state().engine_state.as_ref() else {
743            return Err(Error::from_static_str("failed to instantiate module: empty module"));
744        };
745
746        let backend = if_compiler_is_supported! {
747            {{
748                match compiled_module {
749                    #[cfg(target_os = "linux")]
750                    CompiledModuleKind::Linux(..) => {
751                        let outer_instance = match outer_instance {
752                            Some(outer_instance) => {
753                                let outer_module = &outer_instance.module;
754                                #[allow(clippy::match_wildcard_for_single_variants)]
755                                match outer_instance.backend {
756                                    InstanceBackend::CompiledLinux(ref outer_instance) if outer_module.engine_state_pointer() == self.engine_state_pointer() => Some(outer_instance),
757                                    _ => return Err(Error::from_static_str("failed to instantiate module: received incompatible outer instance")),
758                                }
759                            },
760                            None => None,
761                        };
762                        let compiled_instance = SandboxInstance::<SandboxLinux>::spawn_and_load_module(Arc::clone(engine_state), self, outer_instance)?;
763                        Some(InstanceBackend::CompiledLinux(compiled_instance))
764                    },
765                    #[cfg(feature = "generic-sandbox")]
766                    CompiledModuleKind::Generic(..) => {
767                        let outer_instance = match outer_instance {
768                            Some(outer_instance) => {
769                                let outer_module = &outer_instance.module;
770                                #[allow(clippy::match_wildcard_for_single_variants)]
771                                match outer_instance.backend {
772                                    InstanceBackend::CompiledGeneric(ref outer_instance) if outer_module.engine_state_pointer() == self.engine_state_pointer() => Some(outer_instance),
773                                    _ => return Err(Error::from_static_str("failed to instantiate module: received incompatible outer instance")),
774                                }
775                            },
776                            None => None,
777                        };
778                        let compiled_instance = SandboxInstance::<SandboxGeneric>::spawn_and_load_module(Arc::clone(engine_state), self, outer_instance)?;
779                        Some(InstanceBackend::CompiledGeneric(compiled_instance))
780                    },
781                    CompiledModuleKind::Unavailable => None
782                }
783            }} else {
784                match compiled_module {
785                    CompiledModuleKind::Unavailable => None
786                }
787            }
788        };
789
790        let backend = match backend {
791            Some(backend) => backend,
792            None => {
793                if let Some(outer_instance) = outer_instance {
794                    let outer_module = &outer_instance.module;
795                    #[allow(clippy::match_wildcard_for_single_variants)]
796                    match outer_instance.backend {
797                        InstanceBackend::Interpreted(..) if outer_module.engine_state_pointer() == self.engine_state_pointer() => {}
798                        _ => {
799                            return Err(Error::from_static_str(
800                                "failed to instantiate module: received incompatible outer instance",
801                            ))
802                        }
803                    }
804                }
805
806                InstanceBackend::Interpreted(InterpretedInstance::new_from_module(
807                    self.clone(),
808                    false,
809                    engine_state.imperfect_logger_filtering_workaround,
810                ))
811            }
812        };
813
814        let crosscheck_instance = if_compiler_is_supported! {
815            {
816                if self.state().crosscheck && !matches!(backend, InstanceBackend::Interpreted(..)) {
817                    Some(Box::new(InterpretedInstance::new_from_module(self.clone(), true, false)))
818                } else {
819                    None
820                }
821            } else {
822                None
823            }
824        };
825
826        Ok(RawInstance {
827            module: self.clone(),
828            backend,
829            crosscheck_instance,
830            host_side_aux_write_protect: false,
831        })
832    }
833
834    /// The program's memory map.
835    pub fn memory_map(&self) -> &MemoryMap {
836        &self.state().memory_map
837    }
838
839    /// The default stack pointer for the module.
840    pub fn default_sp(&self) -> RegValue {
841        u64::from(self.memory_map().stack_address_high())
842    }
843
844    /// Returns the module's exports.
845    pub fn exports(&self) -> impl Iterator<Item = crate::program::ProgramExport<&[u8]>> + Clone {
846        self.state().blob.exports()
847    }
848
849    /// Returns the module's imports.
850    pub fn imports(&self) -> Imports {
851        self.state().blob.imports()
852    }
853
854    /// The raw machine code of the compiled module.
855    ///
856    /// Will return `None` when running under an interpreter.
857    /// Mostly only useful for debugging.
858    pub fn machine_code(&self) -> Option<&[u8]> {
859        if_compiler_is_supported! {
860            {
861                match self.state().compiled_module {
862                    #[cfg(target_os = "linux")]
863                    CompiledModuleKind::Linux(ref module) => Some(module.machine_code()),
864                    #[cfg(feature = "generic-sandbox")]
865                    CompiledModuleKind::Generic(ref module) => Some(module.machine_code()),
866                    CompiledModuleKind::Unavailable => None,
867                }
868            } else {
869                None
870            }
871        }
872    }
873
874    /// The address at which the raw machine code will be loaded.
875    ///
876    /// Will return `None` unless compiled for the Linux sandbox.
877    /// Mostly only useful for debugging.
878    pub fn machine_code_origin(&self) -> Option<u64> {
879        if_compiler_is_supported! {
880            {
881                match self.state().compiled_module {
882                    #[cfg(target_os = "linux")]
883                    CompiledModuleKind::Linux(..) => Some(polkavm_common::zygote::VM_ADDR_NATIVE_CODE),
884                    #[cfg(feature = "generic-sandbox")]
885                    CompiledModuleKind::Generic(..) => None,
886                    CompiledModuleKind::Unavailable => None,
887                }
888            } else {
889                None
890            }
891        }
892    }
893
894    /// A slice which contains pairs of PolkaVM bytecode offsets and native machine code offsets.
895    ///
896    /// This makes it possible to map a position within the guest program into the
897    /// exact range of native machine code instructions.
898    ///
899    /// The returned slice has as many elements as there were instructions in the
900    /// original guest program, plus one extra to make it possible to figure out
901    /// the length of the machine code corresponding to the very last instruction.
902    ///
903    /// This slice is guaranteed to be sorted, so you can binary search through it.
904    ///
905    /// Will return `None` when running under an interpreter.
906    /// Mostly only useful for debugging.
907    pub fn program_counter_to_machine_code_offset(&self) -> Option<&[(ProgramCounter, u32)]> {
908        if_compiler_is_supported! {
909            {
910                match self.state().compiled_module {
911                    #[cfg(target_os = "linux")]
912                    CompiledModuleKind::Linux(ref module) => Some(module.program_counter_to_machine_code_offset()),
913                    #[cfg(feature = "generic-sandbox")]
914                    CompiledModuleKind::Generic(ref module) => Some(module.program_counter_to_machine_code_offset()),
915                    CompiledModuleKind::Unavailable => None,
916                }
917            } else {
918                None
919            }
920        }
921    }
922
923    /// Calculates the gas cost for a given basic block starting at `code_offset`.
924    ///
925    /// Will return `None` if the given `code_offset` is invalid.
926    /// Mostly only useful for debugging.
927    pub fn calculate_gas_cost_for(&self, code_offset: ProgramCounter) -> Option<Gas> {
928        if !self.is_jump_target_valid(code_offset) && code_offset.0 < self.code_len() {
929            return None;
930        }
931
932        let gas = match self.state().cost_model {
933            CostModelKind::Simple(ref cost_model) => {
934                let gas_visitor = GasVisitor::new(cost_model.clone());
935                let instructions = self.instructions_bounded_at(code_offset);
936                crate::gas::calculate_for_block(gas_visitor, instructions)
937            }
938            CostModelKind::Full(cost_model) => {
939                use polkavm_common::simulator::Simulator;
940                let instructions = self.instructions_bounded_at(code_offset);
941                if self.is_64_bit() {
942                    let gas_visitor = Simulator::<B64, ()>::new(self.blob().code(), self.blob().isa(), cost_model, ());
943                    crate::gas::calculate_for_block(gas_visitor, instructions)
944                } else {
945                    let gas_visitor = Simulator::<B32, ()>::new(self.blob().code(), self.blob().isa(), cost_model, ());
946                    crate::gas::calculate_for_block(gas_visitor, instructions)
947                }
948            }
949        };
950
951        Some(i64::from(gas.0))
952    }
953
954    #[cold]
955    fn display_instruction_at(&self, program_counter: ProgramCounter) -> impl core::fmt::Display {
956        let state = self.state();
957        Self::display_instruction_at_impl(
958            state.blob.isa(),
959            state.blob.code(),
960            state.blob.bitmask(),
961            state.blob.is_64_bit(),
962            program_counter,
963        )
964    }
965
966    #[cold]
967    pub(crate) fn display_instruction_at_impl(
968        instruction_set: InstructionSetKind,
969        code: &[u8],
970        bitmask: &[u8],
971        is_64_bit: bool,
972        program_counter: ProgramCounter,
973    ) -> impl core::fmt::Display {
974        struct MaybeInstruction(Option<polkavm_common::program::ParsedInstruction>, bool);
975        impl core::fmt::Display for MaybeInstruction {
976            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
977                if let Some(instruction) = self.0 {
978                    let mut format = polkavm_common::program::InstructionFormat::default();
979                    format.is_64_bit = self.1;
980                    instruction.display(&format).fmt(fmt)?;
981                    Ok(())
982                } else {
983                    write!(fmt, "<NONE>")
984                }
985            }
986        }
987
988        MaybeInstruction(
989            Instructions::new_bounded(instruction_set, code, bitmask, program_counter.0).next(),
990            is_64_bit,
991        )
992    }
993
994    pub(crate) fn debug_print_location(&self, log_level: log::Level, pc: ProgramCounter) {
995        log::log!(log_level, "  Location: #{pc}: {}", self.display_instruction_at(pc));
996
997        let Ok(Some(mut line_program)) = self.state().blob.get_debug_line_program_at(pc) else {
998            return;
999        };
1000
1001        log::log!(log_level, "  Source location:");
1002        for _ in 0..128 {
1003            // Have an upper bound on the number of iterations, just in case.
1004            let Ok(Some(region_info)) = line_program.run() else { break };
1005
1006            if !region_info.instruction_range().contains(&pc) {
1007                continue;
1008            }
1009
1010            for frame in region_info.frames() {
1011                let kind = match frame.kind() {
1012                    FrameKind::Enter => 'f',
1013                    FrameKind::Call => 'c',
1014                    FrameKind::Line => 'l',
1015                };
1016
1017                if let Ok(full_name) = frame.full_name() {
1018                    if let Ok(Some(location)) = frame.location() {
1019                        log::log!(log_level, "    ({kind}) '{full_name}' [{location}]");
1020                    } else {
1021                        log::log!(log_level, "    ({kind}) '{full_name}'");
1022                    }
1023                }
1024            }
1025        }
1026    }
1027}
1028
1029if_compiler_is_supported! {
1030    {
1031        enum InstanceBackend {
1032            #[cfg(target_os = "linux")]
1033            CompiledLinux(SandboxInstance<SandboxLinux>),
1034            #[cfg(feature = "generic-sandbox")]
1035            CompiledGeneric(SandboxInstance<SandboxGeneric>),
1036            Interpreted(InterpretedInstance),
1037        }
1038    } else {
1039        enum InstanceBackend {
1040            Interpreted(InterpretedInstance),
1041        }
1042    }
1043}
1044
1045/// The host failed to access the guest's memory.
1046#[derive(Debug)]
1047pub enum MemoryAccessError {
1048    OutOfRangeAccess { address: u32, length: u64 },
1049    MemoryLimitReached,
1050    Error(Error),
1051}
1052
1053impl core::error::Error for MemoryAccessError {}
1054
1055impl core::fmt::Display for MemoryAccessError {
1056    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1057        match self {
1058            MemoryAccessError::OutOfRangeAccess { address, length } => {
1059                write!(
1060                    fmt,
1061                    "out of range memory access in 0x{:x}-0x{:x} ({} bytes)",
1062                    address,
1063                    u64::from(*address) + length,
1064                    length
1065                )
1066            }
1067            MemoryAccessError::MemoryLimitReached => {
1068                write!(fmt, "memory limit reached")
1069            }
1070            MemoryAccessError::Error(error) => {
1071                write!(fmt, "memory access failed: {error}")
1072            }
1073        }
1074    }
1075}
1076
1077impl From<MemoryAccessError> for alloc::string::String {
1078    fn from(error: MemoryAccessError) -> alloc::string::String {
1079        alloc::string::ToString::to_string(&error)
1080    }
1081}
1082
1083/// Compilation failed.
1084#[derive(Debug)]
1085pub enum CompileError {
1086    /// The module has failed validation.
1087    ValidationFailed(String),
1088    Error(Error),
1089}
1090
1091impl From<Error> for CompileError {
1092    fn from(error: Error) -> Self {
1093        Self::Error(error)
1094    }
1095}
1096
1097impl From<CompileError> for Error {
1098    fn from(error: CompileError) -> Self {
1099        match error {
1100            CompileError::Error(error) => error,
1101            error @ CompileError::ValidationFailed(..) => Error::from_display(error),
1102        }
1103    }
1104}
1105
1106impl From<CompileError> for String {
1107    fn from(error: CompileError) -> Self {
1108        error.to_string()
1109    }
1110}
1111
1112impl core::error::Error for CompileError {}
1113
1114impl core::fmt::Display for CompileError {
1115    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1116        match self {
1117            CompileError::ValidationFailed(error) => {
1118                write!(fmt, "module validation failed: {error}")
1119            }
1120            CompileError::Error(error) => error.fmt(fmt),
1121        }
1122    }
1123}
1124
1125if_compiler_is_supported! {
1126    {
1127        macro_rules! access_backend {
1128            ($itself:expr, |$backend:ident| $e:expr) => {
1129                match $itself {
1130                    #[cfg(target_os = "linux")]
1131                    InstanceBackend::CompiledLinux(ref $backend) => {
1132                        let $backend = $backend.sandbox();
1133                        $e
1134                    },
1135                    #[cfg(feature = "generic-sandbox")]
1136                    InstanceBackend::CompiledGeneric(ref $backend) => {
1137                        let $backend = $backend.sandbox();
1138                        $e
1139                    },
1140                    InstanceBackend::Interpreted(ref $backend) => $e,
1141                }
1142            };
1143
1144            ($itself:expr, |mut $backend:ident| $e:expr) => {
1145                match $itself {
1146                    #[cfg(target_os = "linux")]
1147                    InstanceBackend::CompiledLinux(ref mut $backend) => {
1148                        let $backend = $backend.sandbox_mut();
1149                        $e
1150                    },
1151                    #[cfg(feature = "generic-sandbox")]
1152                    InstanceBackend::CompiledGeneric(ref mut $backend) => {
1153                        let $backend = $backend.sandbox_mut();
1154                        $e
1155                    },
1156                    InstanceBackend::Interpreted(ref mut $backend) => $e,
1157                }
1158            };
1159        }
1160    } else {
1161        macro_rules! access_backend {
1162            ($itself:expr, |$backend:ident| $e:expr) => {
1163                match $itself {
1164                    InstanceBackend::Interpreted(ref $backend) => $e,
1165                }
1166            };
1167
1168            ($itself:expr, |mut $backend:ident| $e:expr) => {
1169                match $itself {
1170                    InstanceBackend::Interpreted(ref mut $backend) => $e,
1171                }
1172            };
1173        }
1174    }
1175}
1176
1177pub struct RawInstance {
1178    module: Module,
1179    backend: InstanceBackend,
1180    crosscheck_instance: Option<Box<InterpretedInstance>>,
1181    host_side_aux_write_protect: bool,
1182}
1183
1184impl RawInstance {
1185    /// Returns the module from which this instance was created.
1186    pub fn module(&self) -> &Module {
1187        &self.module
1188    }
1189
1190    /// Returns whether we're running a 64-bit program.
1191    pub fn is_64_bit(&self) -> bool {
1192        self.module.is_64_bit()
1193    }
1194
1195    #[cold]
1196    #[inline(never)]
1197    fn on_trap(&self) {
1198        use crate::program::Instruction;
1199
1200        if !log::log_enabled!(log::Level::Debug) {
1201            return;
1202        }
1203
1204        if let Some(program_counter) = self.program_counter() {
1205            self.module.debug_print_location(log::Level::Debug, program_counter);
1206            if let Some(instruction) = self.module.instructions_bounded_at(program_counter).next() {
1207                let (base, offset, length) = match instruction.kind {
1208                    Instruction::load_indirect_u8(_, base, offset)
1209                    | Instruction::load_indirect_i8(_, base, offset)
1210                    | Instruction::store_indirect_u8(_, base, offset)
1211                    | Instruction::store_imm_indirect_u8(base, offset, _) => (Some(base), offset, 1),
1212                    Instruction::load_indirect_u16(_, base, offset)
1213                    | Instruction::load_indirect_i16(_, base, offset)
1214                    | Instruction::store_indirect_u16(_, base, offset)
1215                    | Instruction::store_imm_indirect_u16(base, offset, _) => (Some(base), offset, 2),
1216                    Instruction::load_indirect_u32(_, base, offset)
1217                    | Instruction::load_indirect_i32(_, base, offset)
1218                    | Instruction::store_indirect_u32(_, base, offset)
1219                    | Instruction::store_imm_indirect_u32(base, offset, _) => (Some(base), offset, 4),
1220                    Instruction::load_indirect_u64(_, base, offset)
1221                    | Instruction::store_indirect_u64(_, base, offset)
1222                    | Instruction::store_imm_indirect_u64(base, offset, _) => (Some(base), offset, 8),
1223                    Instruction::load_u8(_, offset)
1224                    | Instruction::load_i8(_, offset)
1225                    | Instruction::store_u8(_, offset)
1226                    | Instruction::store_imm_u8(offset, _) => (None, offset, 1),
1227                    Instruction::load_u16(_, offset)
1228                    | Instruction::load_i16(_, offset)
1229                    | Instruction::store_u16(_, offset)
1230                    | Instruction::store_imm_u16(offset, _) => (None, offset, 2),
1231                    Instruction::load_u32(_, offset)
1232                    | Instruction::load_i32(_, offset)
1233                    | Instruction::store_u32(_, offset)
1234                    | Instruction::store_imm_u32(offset, _) => (None, offset, 4),
1235                    Instruction::load_u64(_, offset) | Instruction::store_u64(_, offset) | Instruction::store_imm_u64(offset, _) => {
1236                        (None, offset, 8)
1237                    }
1238                    _ => return,
1239                };
1240
1241                let mut offset = u64::from(offset);
1242                if let Some(base) = base {
1243                    offset = offset.wrapping_add(self.reg(base.get()));
1244                }
1245
1246                offset &= 0xffffffff;
1247                let offset_end = offset.wrapping_add(length) & 0xffffffff;
1248
1249                log::debug!("Trapped when trying to access address: 0x{offset:08x}-0x{offset_end:08x}");
1250                if !self.module.is_dynamic_paging() {
1251                    let aux_address = u64::from(self.module.memory_map().aux_data_address());
1252                    let aux_size = u64::from(self.module.memory_map().aux_data_size());
1253                    let stack_address_hi = u64::from(self.module.memory_map().stack_address_high());
1254                    let stack_address_lo = u64::from(self.module.memory_map().stack_address_low());
1255                    if offset >= aux_address {
1256                        if aux_size > 0 {
1257                            let aux_address_end = aux_address + aux_size;
1258                            log::debug!("  Auxiliary data range: 0x{aux_address:08x}..0x{aux_address_end:08x}");
1259                        }
1260                    } else if offset < stack_address_hi && offset >= stack_address_lo.wrapping_sub(32 * 1024 * 1024) {
1261                        log::debug!("  Current stack range: 0x{stack_address_lo:08x}-0x{stack_address_hi:08x}");
1262                        log::debug!("  Hint: try increasing your stack size with: 'polkavm_derive::min_stack_size'");
1263                    }
1264                }
1265            }
1266        }
1267    }
1268
1269    /// Starts or resumes the execution.
1270    #[inline(always)]
1271    pub fn run(&mut self) -> Result<InterruptKind, Error> {
1272        if self.next_program_counter().is_none() {
1273            return Err(Error::from_static_str("failed to run: next program counter is not set"));
1274        }
1275
1276        if self.gas() < 0 {
1277            return Ok(InterruptKind::NotEnoughGas);
1278        }
1279
1280        if self.crosscheck_instance.is_some() || log::log_enabled!(log::Level::Debug) {
1281            self.run_impl_debug()
1282        } else {
1283            self.run_impl::<false>()
1284        }
1285    }
1286
1287    #[inline(never)]
1288    #[cold]
1289    fn run_impl_debug(&mut self) -> Result<InterruptKind, Error> {
1290        self.run_impl::<true>()
1291    }
1292
1293    #[inline(always)]
1294    fn run_impl<const DEBUG: bool>(&mut self) -> Result<InterruptKind, Error> {
1295        #[allow(clippy::never_loop)]
1296        loop {
1297            let (interruption, gas) = access_backend!(self.backend, |mut backend| {
1298                let interruption = backend.run().map_err(|error| format!("execution failed: {error}"));
1299                let gas = backend.gas();
1300                (interruption, gas)
1301            });
1302
1303            let mut interruption = interruption?;
1304            if DEBUG {
1305                log::trace!("Interrupted: {:?}", interruption);
1306                if matches!(interruption, InterruptKind::Trap) {
1307                    self.on_trap();
1308                }
1309            }
1310
1311            if_compiler_is_supported! {
1312                if DEBUG && self.crosscheck_instance.is_some() {
1313                    let is_step = matches!(interruption, InterruptKind::Step);
1314                    self.crosscheck(interruption.clone());
1315
1316                    if is_step && !self.module().state().step_tracing {
1317                        continue;
1318                    }
1319                }
1320            }
1321
1322            if gas < 0 {
1323                interruption = InterruptKind::NotEnoughGas;
1324            }
1325
1326            break Ok(interruption);
1327        }
1328    }
1329
1330    #[allow(dead_code)]
1331    #[inline(never)]
1332    #[cold]
1333    fn crosscheck(&mut self, interruption: InterruptKind) {
1334        let Some(ref mut crosscheck) = self.crosscheck_instance else {
1335            unreachable!()
1336        };
1337        let expected_interruption = crosscheck.run().expect("crosscheck failed");
1338        if interruption != expected_interruption {
1339            panic!("run: crosscheck mismatch, interpreter = {expected_interruption:?}, backend = {interruption:?}");
1340        }
1341
1342        if self.module.gas_metering() != Some(GasMeteringKind::Async) {
1343            for reg in Reg::ALL {
1344                let value = access_backend!(self.backend, |backend| backend.reg(reg));
1345                let expected_value = crosscheck.reg(reg);
1346                if value != expected_value {
1347                    panic!("run: crosscheck mismatch for {reg}, interpreter = 0x{expected_value:x}, backend = 0x{value:x}");
1348                }
1349            }
1350        }
1351
1352        let crosscheck_gas = crosscheck.gas();
1353        let crosscheck_program_counter = crosscheck.program_counter();
1354        let crosscheck_next_program_counter = crosscheck.next_program_counter();
1355        if self.module.gas_metering() != Some(GasMeteringKind::Async) {
1356            let gas = self.gas();
1357            if gas != crosscheck_gas {
1358                panic!("run: crosscheck mismatch for gas, interpreter = {crosscheck_gas}, backend = {gas}");
1359            }
1360        }
1361
1362        if self.program_counter() != crosscheck_program_counter {
1363            panic!(
1364                "run: crosscheck mismatch for program counter, interpreter = {crosscheck_program_counter:?}, backend = {:?}",
1365                self.program_counter()
1366            );
1367        }
1368
1369        if self.next_program_counter() != crosscheck_next_program_counter {
1370            panic!(
1371                "run: crosscheck mismatch for next program counter, interpreter = {crosscheck_next_program_counter:?}, backend = {:?}",
1372                self.next_program_counter()
1373            );
1374        }
1375    }
1376
1377    /// Gets the value of a given register.
1378    pub fn reg(&self, reg: Reg) -> RegValue {
1379        access_backend!(self.backend, |backend| backend.reg(reg))
1380    }
1381
1382    /// Sets the value of a given register.
1383    pub fn set_reg(&mut self, reg: Reg, value: RegValue) {
1384        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1385            crosscheck.set_reg(reg, value);
1386        }
1387
1388        access_backend!(self.backend, |mut backend| backend.set_reg(reg, value))
1389    }
1390
1391    /// Gets the amount of gas remaining.
1392    ///
1393    /// Note that this being zero doesn't necessarily mean that the execution ran out of gas,
1394    /// if the program ended up consuming *exactly* the amount of gas that it was provided with!
1395    pub fn gas(&self) -> Gas {
1396        access_backend!(self.backend, |backend| backend.gas())
1397    }
1398
1399    /// Sets the amount of gas remaining.
1400    pub fn set_gas(&mut self, gas: Gas) {
1401        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1402            crosscheck.set_gas(gas);
1403        }
1404
1405        access_backend!(self.backend, |mut backend| backend.set_gas(gas))
1406    }
1407
1408    /// Gets the current program counter.
1409    pub fn program_counter(&self) -> Option<ProgramCounter> {
1410        access_backend!(self.backend, |backend| backend.program_counter())
1411    }
1412
1413    /// Gets the next program counter.
1414    ///
1415    /// This is where the program will resume execution when [`RawInstance::run`] is called.
1416    pub fn next_program_counter(&self) -> Option<ProgramCounter> {
1417        access_backend!(self.backend, |backend| backend.next_program_counter())
1418    }
1419
1420    /// Sets the next program counter.
1421    pub fn set_next_program_counter(&mut self, pc: ProgramCounter) {
1422        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1423            crosscheck.set_next_program_counter(pc);
1424        }
1425
1426        access_backend!(self.backend, |mut backend| backend.set_next_program_counter(pc))
1427    }
1428
1429    /// A convenience function which sets all of the registers to zero.
1430    pub fn clear_regs(&mut self) {
1431        for reg in Reg::ALL {
1432            self.set_reg(reg, 0);
1433        }
1434    }
1435
1436    /// Sets the accessible region of the aux data, rounded up to the nearest page size.
1437    pub fn set_accessible_aux_size(&mut self, size: u32) -> Result<(), Error> {
1438        if self.module.is_dynamic_paging() {
1439            return Err("setting accessible aux size is only possible on modules without dynamic paging".into());
1440        }
1441
1442        if size > self.module.memory_map().aux_data_size() {
1443            return Err(format!(
1444                "cannot set accessible aux size: the maximum is {}, while tried to set {}",
1445                self.module.memory_map().aux_data_size(),
1446                size
1447            )
1448            .into());
1449        }
1450
1451        let size = self.module.round_to_page_size_up(size);
1452        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1453            crosscheck.set_accessible_aux_size(size);
1454        }
1455
1456        access_backend!(self.backend, |mut backend| backend
1457            .set_accessible_aux_size(size)
1458            .into_result("failed to set accessible aux size"))?;
1459
1460        debug_assert_eq!(access_backend!(self.backend, |backend| backend.accessible_aux_size()), size);
1461        Ok(())
1462    }
1463
1464    /// Sets whether the aux data region is write-protected on the host-side.
1465    ///
1466    /// This affects `write_memory`, `zero_memory` and `is_memory_accessible`.
1467    ///
1468    /// Will return an error if called on an instance with dynamic paging enabled.
1469    /// Infallible otherwise.
1470    pub fn set_host_side_aux_write_protect(&mut self, is_write_protected: bool) -> Result<(), Error> {
1471        if self.module.is_dynamic_paging() {
1472            return Err("write-protecting the aux data region is only possible on modules without dynamic paging".into());
1473        }
1474
1475        self.host_side_aux_write_protect = is_write_protected;
1476        Ok(())
1477    }
1478
1479    /// Resets the VM's memory to its initial state.
1480    pub fn reset_memory(&mut self) -> Result<(), Error> {
1481        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1482            crosscheck.reset_memory();
1483        }
1484
1485        access_backend!(self.backend, |mut backend| backend
1486            .reset_memory()
1487            .into_result("failed to reset the instance's memory"))
1488    }
1489
1490    /// Returns whether a given chunk of memory is accessible.
1491    ///
1492    /// If `size` is zero then this will always return `true`.
1493    pub fn is_memory_accessible(&self, address: u32, size: u32, minimum_protection: MemoryProtection) -> bool {
1494        if size == 0 {
1495            return true;
1496        }
1497
1498        if address < 0x10000 {
1499            return false;
1500        }
1501
1502        let upper_limit = match minimum_protection {
1503            MemoryProtection::Read => 0x100000000,
1504            MemoryProtection::ReadWrite => self.get_write_upper_limit(),
1505        };
1506
1507        if u64::from(address) + cast(size).to_u64() > upper_limit {
1508            return false;
1509        }
1510
1511        #[inline]
1512        fn is_within(range: core::ops::Range<u32>, address: u32, size: u32) -> bool {
1513            let address_end = u64::from(address) + cast(size).to_u64();
1514            address >= range.start && address_end <= u64::from(range.end)
1515        }
1516
1517        if !self.module.is_dynamic_paging() {
1518            let map = self.module.memory_map();
1519            if is_within(map.stack_range(), address, size) {
1520                return true;
1521            }
1522
1523            let heap_size = self.heap_size();
1524            let heap_top = map.heap_base() + heap_size;
1525            let heap_top = self.module.round_to_page_size_up(heap_top);
1526            if is_within(map.rw_data_address()..heap_top, address, size) {
1527                return true;
1528            }
1529
1530            let aux_size = access_backend!(self.backend, |backend| backend.accessible_aux_size());
1531            if is_within(map.aux_data_address()..map.aux_data_address() + aux_size, address, size) {
1532                return true;
1533            }
1534
1535            if matches!(minimum_protection, MemoryProtection::Read) && is_within(map.ro_data_range(), address, size) {
1536                return true;
1537            }
1538
1539            false
1540        } else {
1541            access_backend!(self.backend, |backend| backend.is_memory_accessible(
1542                address,
1543                size,
1544                minimum_protection
1545            ))
1546        }
1547    }
1548
1549    /// Reads the VM's memory.
1550    ///
1551    /// The whole memory region must be readable.
1552    pub fn read_memory_into<'slice, B>(&mut self, address: u32, buffer: &'slice mut B) -> Result<&'slice mut [u8], MemoryAccessError>
1553    where
1554        B: ?Sized + AsUninitSliceMut,
1555    {
1556        let slice = buffer.as_uninit_slice_mut();
1557        if slice.is_empty() {
1558            // SAFETY: The slice is empty so it's always safe to assume it's initialized.
1559            unsafe {
1560                return Ok(polkavm_common::utils::slice_assume_init_mut(slice));
1561            }
1562        }
1563
1564        if address < 0x10000 {
1565            return Err(MemoryAccessError::OutOfRangeAccess {
1566                address,
1567                length: cast(slice.len()).to_u64(),
1568            });
1569        }
1570
1571        if u64::from(address) + cast(slice.len()).to_u64() > 0x100000000 {
1572            return Err(MemoryAccessError::OutOfRangeAccess {
1573                address,
1574                length: cast(slice.len()).to_u64(),
1575            });
1576        }
1577
1578        let length = slice.len();
1579        let result = access_backend!(self.backend, |mut backend| backend.read_memory_into(address, slice));
1580        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1581            let mut expected_data: Vec<core::mem::MaybeUninit<u8>> = alloc::vec![core::mem::MaybeUninit::new(0xfa); length];
1582            let expected_result = crosscheck.read_memory_into(address, &mut expected_data);
1583            let expected_success = expected_result.is_ok();
1584            let success = result.is_ok();
1585            let results_match = match (&result, &expected_result) {
1586                (Ok(result), Ok(expected_result)) => result == expected_result,
1587                (Err(_), Err(_)) => true,
1588                _ => false,
1589            };
1590            if !results_match {
1591                let address_end = u64::from(address) + cast(length).to_u64();
1592                if cfg!(debug_assertions) {
1593                    if let (Ok(result), Ok(expected_result)) = (result, expected_result) {
1594                        log::trace!("read_memory result (interpreter): {expected_result:?}");
1595                        log::trace!("read_memory result (backend):     {result:?}");
1596                    }
1597                }
1598                panic!("read_memory: crosscheck mismatch, range = 0x{address:x}..0x{address_end:x}, interpreter = {expected_success}, backend = {success}");
1599            }
1600        }
1601
1602        if cfg!(debug_assertions) {
1603            let is_inaccessible = !self.is_memory_accessible(address, cast(length).assert_always_fits_in_u32(), MemoryProtection::Read);
1604            if is_inaccessible != matches!(result, Err(MemoryAccessError::OutOfRangeAccess { .. })) {
1605                panic!(
1606                    "'read_memory_into' doesn't match with 'is_memory_accessible' for 0x{:x}-0x{:x} (read_memory_into = {:?}, is_memory_accessible = {})",
1607                    address,
1608                    cast(address).to_usize() + length,
1609                    result.map(|_| ()),
1610                    !is_inaccessible,
1611                );
1612            }
1613        }
1614
1615        result
1616    }
1617
1618    fn get_write_upper_limit(&self) -> u64 {
1619        if self.host_side_aux_write_protect {
1620            debug_assert!(!self.module.is_dynamic_paging());
1621            u64::from(self.module.memory_map().stack_address_high())
1622        } else {
1623            0x100000000
1624        }
1625    }
1626
1627    /// Writes into the VM's memory.
1628    ///
1629    /// The whole memory region must be writable.
1630    pub fn write_memory(&mut self, address: u32, data: &[u8]) -> Result<(), MemoryAccessError> {
1631        if data.is_empty() {
1632            return Ok(());
1633        }
1634
1635        if address < 0x10000 {
1636            return Err(MemoryAccessError::OutOfRangeAccess {
1637                address,
1638                length: cast(data.len()).to_u64(),
1639            });
1640        }
1641
1642        if u64::from(address) + cast(data.len()).to_u64() > self.get_write_upper_limit() {
1643            return Err(MemoryAccessError::OutOfRangeAccess {
1644                address,
1645                length: cast(data.len()).to_u64(),
1646            });
1647        }
1648
1649        let result = access_backend!(self.backend, |mut backend| backend.write_memory(address, data));
1650        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1651            let expected_result = crosscheck.write_memory(address, data);
1652            let expected_success = expected_result.is_ok();
1653            let success = result.is_ok();
1654            if success != expected_success {
1655                let address_end = u64::from(address) + cast(data.len()).to_u64();
1656                panic!("write_memory: crosscheck mismatch, range = 0x{address:x}..0x{address_end:x}, interpreter = {expected_success}, backend = {success}");
1657            }
1658        }
1659
1660        if cfg!(debug_assertions) {
1661            let is_inaccessible =
1662                !self.is_memory_accessible(address, cast(data.len()).assert_always_fits_in_u32(), MemoryProtection::ReadWrite);
1663            if is_inaccessible != matches!(result, Err(MemoryAccessError::OutOfRangeAccess { .. })) {
1664                panic!(
1665                    "'write_memory' doesn't match with 'is_memory_accessible' for 0x{:x}-0x{:x} (write_memory = {:?}, is_memory_accessible = {})",
1666                    address,
1667                    cast(address).to_usize() + data.len(),
1668                    result,
1669                    !is_inaccessible,
1670                );
1671            }
1672        }
1673
1674        result
1675    }
1676
1677    /// Reads the VM's memory.
1678    ///
1679    /// The whole memory region must be readable.
1680    pub fn read_memory(&mut self, address: u32, length: u32) -> Result<Vec<u8>, MemoryAccessError> {
1681        let mut buffer = Vec::new();
1682        buffer.reserve_exact(cast(length).to_usize());
1683
1684        let pointer = buffer.as_ptr();
1685        let slice = self.read_memory_into(address, buffer.spare_capacity_mut())?;
1686
1687        // Since `read_memory_into_slice` returns a `&mut [u8]` we can be sure it initialized the buffer
1688        // we've passed to it, as long as it's actually the same buffer we gave it.
1689        assert_eq!(slice.as_ptr(), pointer);
1690        assert_eq!(slice.len(), cast(length).to_usize());
1691
1692        #[allow(unsafe_code)]
1693        // SAFETY: `read_memory_into_slice` initialized this buffer, and we've verified this with `assert`s.
1694        unsafe {
1695            buffer.set_len(cast(length).to_usize());
1696        }
1697
1698        Ok(buffer)
1699    }
1700
1701    /// A convenience function to read an `u64` from the VM's memory.
1702    ///
1703    /// This is equivalent to calling [`RawInstance::read_memory_into`].
1704    pub fn read_u64(&mut self, address: u32) -> Result<u64, MemoryAccessError> {
1705        let mut buffer = [0; 8];
1706        self.read_memory_into(address, &mut buffer)?;
1707
1708        Ok(u64::from_le_bytes(buffer))
1709    }
1710
1711    /// A convenience function to write an `u64` into the VM's memory.
1712    ///
1713    /// This is equivalent to calling [`RawInstance::write_memory`].
1714    pub fn write_u64(&mut self, address: u32, value: u64) -> Result<(), MemoryAccessError> {
1715        self.write_memory(address, &value.to_le_bytes())
1716    }
1717
1718    /// A convenience function to read an `u32` from the VM's memory.
1719    ///
1720    /// This is equivalent to calling [`RawInstance::read_memory_into`].
1721    pub fn read_u32(&mut self, address: u32) -> Result<u32, MemoryAccessError> {
1722        let mut buffer = [0; 4];
1723        self.read_memory_into(address, &mut buffer)?;
1724
1725        Ok(u32::from_le_bytes(buffer))
1726    }
1727
1728    /// A convenience function to write an `u32` into the VM's memory.
1729    ///
1730    /// This is equivalent to calling [`RawInstance::write_memory`].
1731    pub fn write_u32(&mut self, address: u32, value: u32) -> Result<(), MemoryAccessError> {
1732        self.write_memory(address, &value.to_le_bytes())
1733    }
1734
1735    /// A convenience function to read an `u16` from the VM's memory.
1736    ///
1737    /// This is equivalent to calling [`RawInstance::read_memory_into`].
1738    pub fn read_u16(&mut self, address: u32) -> Result<u16, MemoryAccessError> {
1739        let mut buffer = [0; 2];
1740        self.read_memory_into(address, &mut buffer)?;
1741
1742        Ok(u16::from_le_bytes(buffer))
1743    }
1744
1745    /// A convenience function to write an `u16` into the VM's memory.
1746    ///
1747    /// This is equivalent to calling [`RawInstance::write_memory`].
1748    pub fn write_u16(&mut self, address: u32, value: u16) -> Result<(), MemoryAccessError> {
1749        self.write_memory(address, &value.to_le_bytes())
1750    }
1751
1752    /// A convenience function to read an `u8` from the VM's memory.
1753    ///
1754    /// This is equivalent to calling [`RawInstance::read_memory_into`].
1755    pub fn read_u8(&mut self, address: u32) -> Result<u8, MemoryAccessError> {
1756        let mut buffer = [0; 1];
1757        self.read_memory_into(address, &mut buffer)?;
1758
1759        Ok(buffer[0])
1760    }
1761
1762    /// A convenience function to write an `u8` into the VM's memory.
1763    ///
1764    /// This is equivalent to calling [`RawInstance::write_memory`].
1765    pub fn write_u8(&mut self, address: u32, value: u8) -> Result<(), MemoryAccessError> {
1766        self.write_memory(address, &[value])
1767    }
1768
1769    /// Fills the given memory region with zeros and changes memory protection flags. Similar to [`RawInstance::zero_memory`], but can only be called when dynamic paging is enabled.
1770    ///
1771    /// `address` must be a multiple of the page size. The value of `length` will be rounded up to the nearest multiple of the page size.
1772    /// If `length` is zero then this call has no effect.
1773    ///
1774    /// Can be used to resolve a segfault. It can also be used to preemptively initialize pages for which no segfault is currently triggered.
1775    pub fn zero_memory_with_memory_protection(
1776        &mut self,
1777        address: u32,
1778        length: u32,
1779        memory_protection: MemoryProtection,
1780    ) -> Result<(), MemoryAccessError> {
1781        if !self.module.is_dynamic_paging() {
1782            return Err(MemoryAccessError::Error(
1783                "'zero_memory_with_memory_protection' is only possible on modules with dynamic paging".into(),
1784            ));
1785        }
1786
1787        if length == 0 {
1788            return Ok(());
1789        }
1790
1791        if !self.module.is_multiple_of_page_size(address) {
1792            return Err(MemoryAccessError::Error("address not a multiple of page size".into()));
1793        }
1794
1795        self.zero_memory_impl(address, length, Some(memory_protection))
1796    }
1797
1798    /// Fills the given memory region with zeros.
1799    ///
1800    /// The whole memory region must be writable.
1801    ///
1802    /// `address` must be greater or equal to 0x10000 and `address + length` cannot be greater than 0x100000000.
1803    /// If `length` is zero then this call has no effect and will always succeed.
1804    pub fn zero_memory(&mut self, address: u32, length: u32) -> Result<(), MemoryAccessError> {
1805        self.zero_memory_impl(address, length, None)
1806    }
1807
1808    fn zero_memory_impl(
1809        &mut self,
1810        address: u32,
1811        length: u32,
1812        memory_protection: Option<MemoryProtection>,
1813    ) -> Result<(), MemoryAccessError> {
1814        if length == 0 {
1815            return Ok(());
1816        }
1817
1818        if address < 0x10000 {
1819            return Err(MemoryAccessError::OutOfRangeAccess {
1820                address,
1821                length: u64::from(length),
1822            });
1823        }
1824
1825        if u64::from(address) + u64::from(length) > self.get_write_upper_limit() {
1826            return Err(MemoryAccessError::OutOfRangeAccess {
1827                address,
1828                length: u64::from(length),
1829            });
1830        }
1831
1832        let length = if memory_protection.is_none() {
1833            length
1834        } else {
1835            self.module().round_to_page_size_up(length)
1836        };
1837
1838        let result = access_backend!(self.backend, |mut backend| backend.zero_memory(address, length, memory_protection));
1839        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1840            let expected_result = crosscheck.zero_memory(address, length, memory_protection);
1841            let expected_success = expected_result.is_ok();
1842            let success = result.is_ok();
1843            if success != expected_success {
1844                let address_end = u64::from(address) + u64::from(length);
1845                panic!("zero_memory: crosscheck mismatch, range = 0x{address:x}..0x{address_end:x}, interpreter = {expected_success}, backend = {success}");
1846            }
1847        }
1848
1849        if cfg!(debug_assertions) && memory_protection.is_none() {
1850            let is_inaccessible = !self.is_memory_accessible(address, length, MemoryProtection::ReadWrite);
1851            if is_inaccessible != matches!(result, Err(MemoryAccessError::OutOfRangeAccess { .. })) {
1852                panic!(
1853                    "'zero_memory' doesn't match with 'is_memory_accessible' for 0x{:x}-0x{:x} (zero_memory = {:?}, is_memory_accessible = {})",
1854                    address,
1855                    cast(address).to_usize() + cast(length).to_usize(),
1856                    result,
1857                    !is_inaccessible,
1858                );
1859            }
1860        }
1861
1862        result
1863    }
1864
1865    /// Read-only protects a given memory region.
1866    ///
1867    /// Is only supported when dynamic paging is enabled.
1868    pub fn protect_memory(&mut self, address: u32, length: u32) -> Result<(), MemoryAccessError> {
1869        self.change_memory_protection(address, length, MemoryProtection::Read)
1870    }
1871
1872    /// Removes read-only protection from a given memory region.
1873    ///
1874    /// Is only supported when dynamic paging is enabled.
1875    pub fn unprotect_memory(&mut self, address: u32, length: u32) -> Result<(), MemoryAccessError> {
1876        self.change_memory_protection(address, length, MemoryProtection::ReadWrite)
1877    }
1878
1879    fn change_memory_protection(&mut self, address: u32, length: u32, protection: MemoryProtection) -> Result<(), MemoryAccessError> {
1880        if !self.module.is_dynamic_paging() {
1881            return Err(MemoryAccessError::Error(
1882                "protecting/unprotecting memory is only possible on modules with dynamic paging".into(),
1883            ));
1884        }
1885
1886        if length == 0 {
1887            return Ok(());
1888        }
1889
1890        if address < 0x10000 {
1891            return Err(MemoryAccessError::OutOfRangeAccess {
1892                address,
1893                length: u64::from(length),
1894            });
1895        }
1896
1897        if u64::from(address) + u64::from(length) > 0x100000000 {
1898            return Err(MemoryAccessError::OutOfRangeAccess {
1899                address,
1900                length: u64::from(length),
1901            });
1902        }
1903
1904        access_backend!(self.backend, |mut backend| backend
1905            .change_memory_protection(address, length, protection))
1906    }
1907
1908    /// Frees the given page(s).
1909    ///
1910    /// `address` must be a multiple of the page size. The value of `length` will be rounded up to the nearest multiple of the page size.
1911    /// If `length` is zero then this call has no effect and will always succeed.
1912    pub fn free_pages(&mut self, address: u32, length: u32) -> Result<(), Error> {
1913        if length == 0 {
1914            return Ok(());
1915        }
1916
1917        if !self.module.is_multiple_of_page_size(address) {
1918            return Err("address not a multiple of page size".into());
1919        }
1920
1921        access_backend!(self.backend, |mut backend| backend
1922            .free_pages(address, length)
1923            .into_result("free pages failed"))?;
1924        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1925            crosscheck.free_pages(address, length);
1926        }
1927
1928        Ok(())
1929    }
1930
1931    /// Returns the current size of the program's heap.
1932    pub fn heap_size(&self) -> u32 {
1933        access_backend!(self.backend, |backend| backend.heap_size())
1934    }
1935
1936    pub fn sbrk(&mut self, size: u32) -> Result<Option<u32>, Error> {
1937        let result = access_backend!(self.backend, |mut backend| backend.sbrk(size).into_result("sbrk failed"))?;
1938        if let Some(ref mut crosscheck) = self.crosscheck_instance {
1939            let expected_result = crosscheck.sbrk(size);
1940            let expected_success = expected_result.is_some();
1941            let success = result.is_some();
1942            if success != expected_success {
1943                panic!("sbrk: crosscheck mismatch, size = {size}, interpreter = {expected_success}, backend = {success}");
1944            }
1945        }
1946
1947        Ok(result)
1948    }
1949
1950    /// A convenience function which sets up a fuction call according to the default ABI.
1951    ///
1952    /// This function will:
1953    ///   1) clear all registers to zero,
1954    ///   2) initialize `RA` to `0xffff0000`,
1955    ///   3) initialize `SP` to its default value,
1956    ///   4) set the program counter.
1957    ///
1958    /// Will panic if `args` has more than 9 elements.
1959    pub fn prepare_call_untyped(&mut self, pc: ProgramCounter, args: &[RegValue]) {
1960        assert!(args.len() <= Reg::ARG_REGS.len(), "too many arguments");
1961
1962        self.clear_regs();
1963        self.set_reg(Reg::SP, self.module.default_sp());
1964        self.set_reg(Reg::RA, u64::from(VM_ADDR_RETURN_TO_HOST));
1965        self.set_next_program_counter(pc);
1966
1967        for (reg, &value) in Reg::ARG_REGS.into_iter().zip(args) {
1968            self.set_reg(reg, value);
1969        }
1970    }
1971
1972    /// A convenience function which sets up a fuction call according to the default ABI.
1973    ///
1974    /// This is equivalent to calling [`RawInstance::prepare_call_untyped`].
1975    ///
1976    /// Will panic if marshalling `args` through the FFI boundary requires too many registers.
1977    pub fn prepare_call_typed<FnArgs>(&mut self, pc: ProgramCounter, args: FnArgs)
1978    where
1979        FnArgs: crate::linker::FuncArgs,
1980    {
1981        let mut regs = [0; Reg::ARG_REGS.len()];
1982        let mut input_count = 0;
1983        args._set(self.module().blob().is_64_bit(), |value| {
1984            assert!(input_count <= Reg::ARG_REGS.len(), "too many arguments");
1985            regs[input_count] = value;
1986            input_count += 1;
1987        });
1988
1989        self.prepare_call_untyped(pc, &regs);
1990    }
1991
1992    /// Extracts a return value from the argument registers according to the default ABI.
1993    ///
1994    /// This is equivalent to manually calling [`RawInstance::reg`].
1995    pub fn get_result_typed<FnResult>(&self) -> FnResult
1996    where
1997        FnResult: crate::linker::FuncResult,
1998    {
1999        let mut output_count = 0;
2000        FnResult::_get(self.module().blob().is_64_bit(), || {
2001            let value = access_backend!(self.backend, |backend| backend.reg(Reg::ARG_REGS[output_count]));
2002            output_count += 1;
2003            value
2004        })
2005    }
2006
2007    /// Returns the PID of the sandbox corresponding to this instance.
2008    ///
2009    /// Will be `None` if the instance doesn't run in a separate process.
2010    /// Mostly only useful for debugging.
2011    pub fn pid(&self) -> Option<u32> {
2012        access_backend!(self.backend, |backend| backend.pid())
2013    }
2014
2015    /// Gets the next native program counter.
2016    ///
2017    /// Will return `None` when running under an interpreter.
2018    /// Mostly only useful for debugging.
2019    pub fn next_native_program_counter(&self) -> Option<usize> {
2020        access_backend!(self.backend, |backend| backend.next_native_program_counter())
2021    }
2022
2023    /// Reset cache and therefore reclaim cache backed memory.
2024    pub fn reset_interpreter_cache(&mut self) {
2025        #[allow(irrefutable_let_patterns)]
2026        if let InstanceBackend::Interpreted(ref mut backend) = self.backend {
2027            backend.reset_interpreter_cache();
2028        }
2029    }
2030
2031    /// Set a tight upper limit on the interpreter cache size (in bytes).
2032    pub fn set_interpreter_cache_size_limit(&mut self, cache_info: Option<SetCacheSizeLimitArgs>) -> Result<(), Error> {
2033        #[allow(irrefutable_let_patterns)]
2034        if let InstanceBackend::Interpreted(ref mut backend) = self.backend {
2035            backend.set_interpreter_cache_size_limit(cache_info)?
2036        }
2037        Ok(())
2038    }
2039
2040    /// Sets the maximum size of a single non-read-only guest memory region.
2041    ///
2042    /// When set this will enforce a strict single-allocation size limit on the interpreter
2043    /// for RW data, stack and aux data regions.
2044    ///
2045    /// For example, if you set this to 1MB then the guest will be allowed to access at most
2046    /// 1MB of address space for stack and RW data, regardless of how much space the module
2047    /// itself has declared that it wants, and any access over this 1MB limit will trap.
2048    ///
2049    /// Setting this is *not* retroactive and will only affect new allocations or reallocations.
2050    ///
2051    /// Only has an effect on the interpreter, and only for modules which don't use dynamic paging.
2052    ///
2053    /// Default: `None`
2054    pub fn set_interpreter_max_allocation_size(&mut self, value: Option<usize>) {
2055        #[allow(irrefutable_let_patterns)]
2056        if let InstanceBackend::Interpreted(ref mut backend) = self.backend {
2057            backend.set_interpreter_max_allocation_size(value);
2058        }
2059    }
2060
2061    /// Sets the total maximum amount of memory for all of the guest's non-read-only memory regions.
2062    ///
2063    /// Setting this is *not* retroactive and will only affect new allocations or reallocations.
2064    ///
2065    /// Only has an effect on the interpreter, and only for modules which don't use dynamic paging.
2066    ///
2067    /// Default: `None`
2068    pub fn set_interpreter_guest_memory_limit(&mut self, value: Option<usize>) {
2069        #[allow(irrefutable_let_patterns)]
2070        if let InstanceBackend::Interpreted(ref mut backend) = self.backend {
2071            backend.set_interpreter_guest_memory_limit(value);
2072        }
2073    }
2074}