pub struct StandaloneEmulator<M = EmulatedMemory> {
pub memory: M,
pub insn_values: InsnValues,
pub block_param_values: FxHashMap<BlockParamId, SizedValue>,
pub poison_params: FxHashSet<BlockParamId>,
pub aggregate_values: FxHashMap<InstructionId, Vec<SizedValue>>,
pub block_param_aggregates: FxHashMap<BlockParamId, Vec<SizedValue>>,
pub array_values: FxHashMap<InstructionId, Vec<u8>>,
pub block: BlockId,
pub idx: usize,
pub call_stack: Vec<FunctionId>,
pub instruction_hook: Option<Box<dyn Fn(&InstructionRef<'_, '_>, &StandaloneEmulator<M>) + Send + Sync>>,
/* private fields */
}Expand description
A lifetime-free emulator that takes &Context<'_> explicitly on each call.
Use this when you need to store an emulator without a lifetime (e.g., across an FFI boundary).
Fields§
§memory: M§insn_values: InsnValues§block_param_values: FxHashMap<BlockParamId, SizedValue>§poison_params: FxHashSet<BlockParamId>Block params bound to poison (argpromote v2): a symbolic pure-call
argument whose bits are undefined. Reading one during emulation is a hard
error (PoisonRead), so a pure-call fold whose result actually depends on
a symbolic argument bails instead of computing on a bogus concrete value.
aggregate_values: FxHashMap<InstructionId, Vec<SizedValue>>Field values of aggregate-typed instruction results (Tuple results and,
on return, the call instruction that produced them). Extract projects a
field back out. Keeps the scalar SizedValue domain unchanged — the
functional argpromote write-set is the only producer/consumer.
block_param_aggregates: FxHashMap<BlockParamId, Vec<SizedValue>>Field values of aggregate-typed block params seeded by
run_map_body (the enumerate (index, elem) lane
fed to a map body). Extract on such a param projects a field back out.
array_values: FxHashMap<InstructionId, Vec<u8>>Little-endian byte buffers of array-typed instruction results — the value
domain for the sequence intrinsics (iota/singleton/insert/concat),
Scan, and array-typed Store. Kept out of the scalar SizedValue
domain the same way aggregate_values keeps
tuples out of it; a scalar at(arr, i) reads one lane back into
insn_values.
block: BlockId§idx: usize§call_stack: Vec<FunctionId>Call stack maintained by run_function (outermost function first).
instruction_hook: Option<Box<dyn Fn(&InstructionRef<'_, '_>, &StandaloneEmulator<M>) + Send + Sync>>Implementations§
Source§impl<M: EmulatorMemory + Default> StandaloneEmulator<M>
impl<M: EmulatorMemory + Default> StandaloneEmulator<M>
Sourcepub fn new_in(entry: BlockId) -> Self
pub fn new_in(entry: BlockId) -> Self
Builds an emulator over an explicit memory backend.
new is the one to reach for with the default
flat memory: Rust’s default type parameters do not participate in
inference, so a generic new would force every call site to name its
backend.
Sourcepub fn invalidate_block_cache(&mut self)
pub fn invalidate_block_cache(&mut self)
Drops the cached instruction list for the current block.
The cache assumes a block’s contents only change while nothing is part-way through it. A caller that runs a block’s body by some other means and then positions the emulator inside that block breaks the assumption, and must say so.
Sourcepub fn take_address_index(&mut self) -> Option<AddressIndex>
pub fn take_address_index(&mut self) -> Option<AddressIndex>
Takes the cached address lookup, leaving the emulator without one.
The index is derived state, built once from what was assumed to be an
immutable module. A VM that lifts code on demand makes the module
mutable, so it has to keep the index current instead. Moving the index
out, updating it in place as blocks are added, and moving it back with
set_address_index keeps discovery O(1) —
rebuilding it per lift is quadratic in the size of the module.
Sourcepub fn address_index(&self) -> Option<&AddressIndex>
pub fn address_index(&self) -> Option<&AddressIndex>
Installs an address lookup, replacing any cached one. Borrows the cached address index, if one has been built.
pub fn set_address_index(&mut self, address_index: AddressIndex)
Sourcepub fn block_at_address(
&mut self,
ctx: &Context<'_>,
address: u64,
) -> Option<BlockId>
pub fn block_at_address( &mut self, ctx: &Context<'_>, address: u64, ) -> Option<BlockId>
Resolves a guest address to the block that covers it, building the cached index if there is not one yet.
Sourcepub fn from_address_in(ctx: &Context<'_>, addr: u64) -> Self
pub fn from_address_in(ctx: &Context<'_>, addr: u64) -> Self
Builds an emulator positioned at addr, over an explicit backend.
pub fn set_varnode( &mut self, ctx: &Context<'_>, id: VarnodeId, value: u64, ) -> Result<(), EmulatorErrorKind>
pub fn set_varnode_u128( &mut self, ctx: &Context<'_>, id: VarnodeId, value: u128, ) -> Result<(), EmulatorErrorKind>
pub fn read_varnode(&self, ctx: &Context<'_>, id: VarnodeId) -> Option<u64>
pub fn read_varnode_u128( &self, ctx: &Context<'_>, id: VarnodeId, ) -> Option<u128>
pub fn get_value(&mut self, ctx: &Context<'_>, id: ValueId) -> Option<u64>
pub fn set_varnode_bytes( &mut self, ctx: &Context<'_>, id: VarnodeId, bytes: &[u8], ) -> Result<(), EmulatorErrorKind>
pub fn set_varnode_by_name( &mut self, ctx: &Context<'_>, name: &str, value: u64, ) -> Result<bool, EmulatorErrorKind>
pub fn set_varnode_by_name_u128( &mut self, ctx: &Context<'_>, name: &str, value: u128, ) -> Result<bool, EmulatorErrorKind>
pub fn set_varnode_by_name_bytes( &mut self, ctx: &Context<'_>, name: &str, bytes: &[u8], ) -> Result<bool, EmulatorErrorKind>
pub fn read_varnode_by_name( &mut self, ctx: &Context<'_>, name: &str, ) -> Option<u64>
pub fn read_varnode_by_name_u128( &mut self, ctx: &Context<'_>, name: &str, ) -> Option<u128>
pub fn read_varnode_bytes( &mut self, ctx: &Context<'_>, id: VarnodeId, ) -> Vec<u8> ⓘ
pub fn read_varnode_by_name_bytes( &mut self, ctx: &Context<'_>, name: &str, ) -> Option<Vec<u8>>
pub fn get_value_bytes( &mut self, ctx: &Context<'_>, id: ValueId, ) -> Option<Vec<u8>>
pub fn current_block(&self) -> BlockId
pub fn set_call_interceptor( &mut self, interceptor: impl FnMut(&Context<'_>, &mut StandaloneEmulator<M>, &CallSite) -> Result<CallInterception, Box<str>> + Send + Sync + 'static, )
pub fn clear_call_interceptor(&mut self)
pub fn read_memory( &mut self, ctx: &Context<'_>, space: impl Into<MemorySpaceId>, addr: u64, size: usize, ) -> Result<Vec<u8>, EmulatorErrorKind>
pub fn write_memory( &mut self, ctx: &Context<'_>, space: impl Into<MemorySpaceId>, addr: u64, value: &[u8], ) -> Result<(), EmulatorErrorKind>
pub fn step(&mut self, ctx: &Context<'_>) -> Result<()>
pub fn run_block(&mut self, ctx: &Context<'_>) -> Result<()>
Sourcepub fn run_until(&mut self, ctx: &Context<'_>, addr: u64) -> Result<()>
pub fn run_until(&mut self, ctx: &Context<'_>, addr: u64) -> Result<()>
Runs blocks until the current block starts at addr.
pub fn run_function( &mut self, ctx: &Context<'_>, func: FunctionId, ) -> Result<()>
Sourcepub fn run_pure(
&mut self,
ctx: &Context<'_>,
func: FunctionId,
args: &[SizedValue],
max_steps: usize,
) -> Result<()>
pub fn run_pure( &mut self, ctx: &Context<'_>, func: FunctionId, args: &[SizedValue], max_steps: usize, ) -> Result<()>
Emulate a pure function in isolation: bind its root params positionally
from args (so symbolic caller inputs can be passed an arbitrary poison
value) and run to the first top-level Return without executing it,
leaving the body’s computed values readable via get_value
at the block returned by current_block.
args must align with the root params index-for-index (the pure_reg
call interface). The run is bounded by max_steps; exceeding it yields
EmulatorErrorKind::StepBudgetExceeded. Intended for v1 leaf pure
functions (no nested calls), so call bookkeeping is intentionally minimal.
Sourcepub fn run_pure_partial(
&mut self,
ctx: &Context<'_>,
func: FunctionId,
args: &[Option<SizedValue>],
max_steps: usize,
) -> Result<()>
pub fn run_pure_partial( &mut self, ctx: &Context<'_>, func: FunctionId, args: &[Option<SizedValue>], max_steps: usize, ) -> Result<()>
Like run_pure, but each positional argument may be
None to bind that root param to poison (a symbolic value with
undefined bits). Reading a poison param during emulation is a hard error
(PoisonRead), so a consumer such as pure-call folding bails when the
result actually depends on a symbolic argument, rather than computing on a
bogus concrete value (argpromote v2, ARGPROMOTE_REGISTERS_V2.md).