Small improvements for Qemu (#2938)

* Remove uneeded Executor trait from handlers functions + set inproc_qemu_crash_handler to pub

* Add some documentation for QemuHooks

---------

Co-authored-by: celian <cglenaz>
This commit is contained in:
Celian G. 2025-02-07 13:28:11 +01:00 committed by GitHub
parent 00582d8494
commit 64554d68bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 91 additions and 11 deletions

View File

@ -127,7 +127,7 @@ pub mod unix_signal_handler {
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, I, S, Z> + HasInProcessHooks<I, S> + HasObservers,
E: HasInProcessHooks<I, S> + HasObservers,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,

View File

@ -379,7 +379,7 @@ pub fn run_observers_and_save_state<E, EM, I, OF, S, Z>(
event_mgr: &mut EM,
exitkind: ExitKind,
) where
E: Executor<EM, I, S, Z> + HasObservers,
E: HasObservers,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,

View File

@ -60,7 +60,7 @@ pub struct QemuExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, Z> {
///
/// This should be used as a crash handler, and nothing else.
#[cfg(feature = "usermode")]
unsafe fn inproc_qemu_crash_handler<E, EM, ET, I, OF, S, Z>(
pub unsafe fn inproc_qemu_crash_handler<E, EM, ET, I, OF, S, Z>(
signal: Signal,
info: &mut siginfo_t,
mut context: Option<&mut ucontext_t>,
@ -161,7 +161,7 @@ pub unsafe fn inproc_qemu_timeout_handler<E, EM, ET, I, OF, S, Z>(
context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: HasObservers + HasInProcessHooks<I, S> + Executor<EM, I, S, Z>,
E: HasObservers + HasInProcessHooks<I, S>,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
ET: EmulatorModuleTuple<I, S>,

View File

@ -1,4 +1,5 @@
//! The high-level hooks
#![allow(clippy::type_complexity)]
#![allow(clippy::missing_transmute_annotations)]
#![allow(clippy::too_many_arguments)]
@ -928,6 +929,38 @@ pub type CrashHookClosure<ET, I, S> = Box<dyn FnMut(Qemu, &mut EmulatorModules<E
/// The thin wrapper around QEMU hooks.
/// It is considered unsafe to use it directly.
///
/// There are several types of hooks in place:
///
/// • **Instruction** hooks: as the name suggests, to hook a specific
/// instruction given its address;
///
/// • **Blocks** hooks: to run code before the execution of each
/// translation block in the target; Be aware that a translation
/// block consist of a unique sequence of contiguous instructions encountered
/// during execution, whereas a basic-block is a sequence of contiguous
/// instructions without jumps AND with no incoming edge.
/// For this reason two translation blocks can overlap.
///
/// • **Edges** hooks: to run code between two translation blocks, for
/// instance, to log the execution of an edge in the CFG. In
/// detail, it is implemented by emitting an intermediate block
/// when chaining 1 two blocks with more than one exit;
///
/// • **Read and write** hooks: executed every memory read or
/// write;
///
/// • **Comparisons** hooks: executed before every comparison
/// instruction, carrying information about the operands;
///
/// • **Thread creation** hook: triggered when a new thread is
/// spawned in user mode;
///
/// • **Syscalls** and **post-syscalls** hooks: they are triggered before
/// or after syscalls in user mode and can be used as filters;
///
/// • **Crash** hooks: to hook crashes in the virtual CPU in user
/// mode;
#[derive(Clone, Copy, Debug)]
pub struct QemuHooks {
_private: (),
@ -952,6 +985,13 @@ impl QemuHooks {
Some(Qemu::get()?.hooks())
}
/// Add `callback` in the instruction hooks.
///
/// `addr` is the address of the instruction hooked.
///
/// `callback` gets passed `data` and the current instruction address.
///
/// Set `invalidate_block` to invalidate the virtual pages related to the translation block.
// TODO set T lifetime to be like Emulator
pub fn add_instruction_hooks<T: Into<HookData>>(
&self,
@ -973,6 +1013,9 @@ impl QemuHooks {
}
}
/// Remove all instruction hooks for the address `addr`.
///
/// Set `invalidate_block` to invalidate the virtual pages related to the translation block.
#[must_use]
pub fn remove_instruction_hooks_at(&self, addr: GuestAddr, invalidate_block: bool) -> usize {
unsafe {
@ -983,6 +1026,12 @@ impl QemuHooks {
}
}
/// Add `gen` in the edge generation hooks and `exec` in the edge execution hooks.
///
/// `gen` gets passed `data` and the source/destination translation blocks addresses
/// when this edge is reached for the first time.
///
/// `exec` gets passed `data` and the return value of `gen` every time this edge is reached.
pub fn add_edge_hooks<T: Into<HookData>>(
&self,
data: T,
@ -999,6 +1048,16 @@ impl QemuHooks {
}
}
/// Add `gen` in the translation block (pre-)generation hooks, `post_gen` in post-generation hooks and `exec`
/// in the execution hooks.
///
/// `gen` gets passed `data` and the block start address, when this block is translated
/// for the first time.
///
/// `post_gen` gets passed `data`, the block start address and the block size in bytes,
/// at the end of the block generation.
///
/// `exec` gets passed `data` and the return value of `gen`, every time this block is reached.
pub fn add_block_hooks<T: Into<HookData>>(
&self,
data: T,
@ -1017,6 +1076,11 @@ impl QemuHooks {
}
}
/// Add `pre_exec` in the (pre-)execution hooks, `post_exec` in the post-execution hooks.
///
/// `pre_exec` gets passed a pointer to the cpu state before the code is run.
///
/// `post_exec` gets passed a pointer to the cpu state after the code is run.
pub fn add_cpu_run_hooks<T: Into<HookData>>(
&self,
data: T,
@ -1032,18 +1096,20 @@ impl QemuHooks {
}
}
/// `data` can be used to pass data that can be accessed as the first argument in the `gen` and the `exec` functions
/// Add hooks for memory read access.
///
/// `gen` gets passed the current programm counter, mutable access to a `TCGTemp` and information about the memory
/// access being performed.
/// The `u64` return value is an id that gets passed to the `exec` functions as their second argument.
/// `data` can be used to pass data that can be accessed as the first argument in the `gen` and the `exec` functions.
///
/// `exec` hooks get invoked on every read performed by the guest
/// `gen` gets passed `data`, the current program counter, mutable access to the address accessed and
/// information about the memory access being performed.
///
/// `exec1`-`exec8` special case accesses of width 1-8
/// `exec` hooks get invoked on every read performed by the guest with `data`, the return value of `gen`,
/// the current instruction index and the address of the memory accessed.
///
/// `exec1`-`exec8` are called for special case accesses of width 1-8.
///
/// If there is no specialized hook for a given read width, the `exec_n` will be
/// called and its last argument will specify the access width
/// called and its last argument will specify the access width.
pub fn add_read_hooks<T: Into<HookData>>(
&self,
data: T,
@ -1081,6 +1147,20 @@ impl QemuHooks {
}
}
/// Add hooks for memory write access.
///
/// `data` can be used to pass data that can be accessed as the first argument in the `gen` and the `exec` functions.
///
/// `gen` gets passed `data`, the current program counter, mutable access to the address written and
/// information about the memory access being performed.
///
/// `exec` hooks get invoked on every write performed by the guest with `data`, the return value of `gen`,
/// the current instruction index and the address of the memory written.
///
/// `exec1`-`exec8` are called for special case write of width 1-8.
///
/// If there is no specialized hook for a given write width, the `exec_n` will be
/// called and its last argument will specify the write width.
// TODO add MemOp info
pub fn add_write_hooks<T: Into<HookData>>(
&self,