Skip to main content

StandaloneEmulator

Struct StandaloneEmulator 

Source
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 StandaloneEmulator<EmulatedMemory>

Source

pub fn new(entry: BlockId) -> Self

Builds an emulator over the default flat memory.

Source

pub fn from_address(ctx: &Context<'_>, addr: u64) -> Self

Builds an emulator positioned at addr, over the default flat memory.

Source§

impl<M: EmulatorMemory + Default> StandaloneEmulator<M>

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn set_address_index(&mut self, address_index: AddressIndex)

Source

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.

Source

pub fn from_address_in(ctx: &Context<'_>, addr: u64) -> Self

Builds an emulator positioned at addr, over an explicit backend.

Source

pub fn set_varnode( &mut self, ctx: &Context<'_>, id: VarnodeId, value: u64, ) -> Result<(), EmulatorErrorKind>

Source

pub fn set_varnode_u128( &mut self, ctx: &Context<'_>, id: VarnodeId, value: u128, ) -> Result<(), EmulatorErrorKind>

Source

pub fn read_varnode(&self, ctx: &Context<'_>, id: VarnodeId) -> Option<u64>

Source

pub fn read_varnode_u128( &self, ctx: &Context<'_>, id: VarnodeId, ) -> Option<u128>

Source

pub fn get_value(&mut self, ctx: &Context<'_>, id: ValueId) -> Option<u64>

Source

pub fn set_varnode_bytes( &mut self, ctx: &Context<'_>, id: VarnodeId, bytes: &[u8], ) -> Result<(), EmulatorErrorKind>

Source

pub fn set_varnode_by_name( &mut self, ctx: &Context<'_>, name: &str, value: u64, ) -> Result<bool, EmulatorErrorKind>

Source

pub fn set_varnode_by_name_u128( &mut self, ctx: &Context<'_>, name: &str, value: u128, ) -> Result<bool, EmulatorErrorKind>

Source

pub fn set_varnode_by_name_bytes( &mut self, ctx: &Context<'_>, name: &str, bytes: &[u8], ) -> Result<bool, EmulatorErrorKind>

Source

pub fn read_varnode_by_name( &mut self, ctx: &Context<'_>, name: &str, ) -> Option<u64>

Source

pub fn read_varnode_by_name_u128( &mut self, ctx: &Context<'_>, name: &str, ) -> Option<u128>

Source

pub fn read_varnode_bytes( &mut self, ctx: &Context<'_>, id: VarnodeId, ) -> Vec<u8>

Source

pub fn read_varnode_by_name_bytes( &mut self, ctx: &Context<'_>, name: &str, ) -> Option<Vec<u8>>

Source

pub fn get_value_bytes( &mut self, ctx: &Context<'_>, id: ValueId, ) -> Option<Vec<u8>>

Source

pub fn current_block(&self) -> BlockId

Source

pub fn set_call_interceptor( &mut self, interceptor: impl FnMut(&Context<'_>, &mut StandaloneEmulator<M>, &CallSite) -> Result<CallInterception, Box<str>> + Send + Sync + 'static, )

Source

pub fn clear_call_interceptor(&mut self)

Source

pub fn read_memory( &mut self, ctx: &Context<'_>, space: impl Into<MemorySpaceId>, addr: u64, size: usize, ) -> Result<Vec<u8>, EmulatorErrorKind>

Source

pub fn write_memory( &mut self, ctx: &Context<'_>, space: impl Into<MemorySpaceId>, addr: u64, value: &[u8], ) -> Result<(), EmulatorErrorKind>

Source

pub fn step(&mut self, ctx: &Context<'_>) -> Result<()>

Source

pub fn run_block(&mut self, ctx: &Context<'_>) -> Result<()>

Source

pub fn run_until(&mut self, ctx: &Context<'_>, addr: u64) -> Result<()>

Runs blocks until the current block starts at addr.

Source

pub fn run_function( &mut self, ctx: &Context<'_>, func: FunctionId, ) -> Result<()>

Source

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.

Source

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).

Source

pub fn run_map_body( &mut self, ctx: &Context<'_>, func: FunctionId, args: &[BodyArg], max_steps: usize, ) -> Result<()>

Auto Trait Implementations§

§

impl<M = EmulatedMemory> !RefUnwindSafe for StandaloneEmulator<M>

§

impl<M = EmulatedMemory> !UnwindSafe for StandaloneEmulator<M>

§

impl<M> Freeze for StandaloneEmulator<M>

§

impl<M> Send for StandaloneEmulator<M>

§

impl<M> Sync for StandaloneEmulator<M>

§

impl<M> Unpin for StandaloneEmulator<M>

§

impl<M> UnsafeUnpin for StandaloneEmulator<M>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.