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.