Skip to main content

Crate qcode_vm

Crate qcode_vm 

Source
Expand description

A virtual machine layer over the QCode interpreter.

qcode_emulator interprets QCode over a pre-lifted, immutable module: it answers “what does this IR compute”. This crate adds what a machine needs on top of that — mapped memory with permissions, faults delivered as values rather than aborts, code discovered on demand as the guest reaches it, and snapshots — so that a guest program can be run rather than merely evaluated.

§Faults are values, not aborts

A bad guest access is returned to the caller, so a harness can observe it and carry on rather than dying:

use qcode_vm::{VmMemory, FaultKind, perm};

let mut memory = VmMemory::new();
// One read-only page of initialised memory.
memory.mmu.map(0x1000, 0x1000, perm::MAP | perm::READ | perm::INIT).unwrap();

let mut buffer = [0u8; 4];
assert!(memory.mmu.read(0x1000, &mut buffer).is_ok());

// Writing it faults, and says why and where.
let fault = memory.mmu.write(0x1000, &[0xff]).unwrap_err();
assert_eq!(fault.kind, FaultKind::WritePerm);
assert_eq!(fault.addr, 0x1000);

// So does touching an address that was never mapped.
let fault = memory.mmu.read(0x9000, &mut buffer).unwrap_err();
assert_eq!(fault.kind, FaultKind::ReadUnmapped);

Execution strategy is pluggable through set_block_executor, which is how qcode_jit is installed on a machine.

Re-exports§

pub use hook::AddressHook;
pub use hook::BlockEntryHook;
pub use hook::BlockView;
pub use hook::CompareHook;
pub use hook::Emitter;
pub use hook::Hook;
pub use hook::HookInjector;
pub use hook::Site;
pub use hook::WriteWatch;
pub use inject::CodeInjector;
pub use jit_abi::ACCESS_FAULT;
pub use jit_abi::ACCESS_OK;
pub use jit_abi::qcode_jit_load;
pub use jit_abi::qcode_jit_sdiv128;
pub use jit_abi::qcode_jit_srem128;
pub use jit_abi::qcode_jit_store;
pub use jit_abi::qcode_jit_udiv128;
pub use jit_abi::qcode_jit_urem128;
pub use memory::VmMemory;
pub use mmu::FaultKind;
pub use mmu::MemFault;
pub use mmu::Mmu;
pub use mmu::MmuSnapshot;
pub use mmu::PAGE_PERM_OFFSET;
pub use mmu::PAGE_SIZE;
pub use mmu::PageData;
pub use mmu::Perm;
pub use mmu::perm;
pub use optimize::Cleanup;
pub use optimize::forward_temp_stores;
pub use stats::Stats;
pub use table::HookAction;
pub use table::HookId;
pub use table::InsnAction;
pub use table::MemAccess;
pub use table::TABLE_CODES;
pub use tlb::TLB_ENTRIES;
pub use tlb::TLB_INDEX_BITS;
pub use tlb::TlbEntry;
pub use tlb::TranslationCache;
pub use vm::BlockExecutor;
pub use vm::CodeError;
pub use vm::CodeSource;
pub use vm::Executed;
pub use vm::Interrupt;
pub use vm::InterruptKind;
pub use vm::ResumeError;
pub use vm::Vm;
pub use vm::VmExit;

Modules§

flat
Flat storage for the spaces a guest does not address.
hook
Hooks as rewrites of the lifted code.
inject
Rewriting lifted code before it runs: the mechanism every hook is built on.
jit_abi
The calling interface between compiled code and this VM’s memory.
memory
The bridge between the interpreter’s memory interface and the Mmu.
mmu
Software MMU: page-granular mapping with per-byte permissions.
optimize
A cheap cleanup round for freshly lifted code.
stats
Where a run’s time and work actually go.
table
Unicorn-shaped hooks: callbacks the machine calls from inside Vm::run.
tlb
A software TLB: the shape compiled code needs to reach guest memory without calling back into Rust.
vm
The run loop: a machine that owns its code, discovers more of it as the guest reaches it, and stops with a reason instead of an error.