qemu_plugin/instruction/
mod.rs

1//! Instruction-related functionality for QEMU plugins
2
3use crate::{
4    CallbackFlags, Error, MemRW, MemoryInfo, Result, TranslationBlock, VCPUIndex, g_free,
5    handle_qemu_plugin_register_vcpu_insn_exec_cb, handle_qemu_plugin_register_vcpu_mem_cb,
6    sys::qemu_plugin_insn,
7};
8#[cfg(not(any(
9    feature = "plugin-api-v0",
10    feature = "plugin-api-v1",
11    feature = "plugin-api-v2"
12)))]
13use crate::{PluginCondition, PluginU64};
14use std::{
15    ffi::{CStr, c_void},
16    marker::PhantomData,
17};
18
19/// Wrapper structure for a `qemu_plugin_insn *`
20///
21/// # Safety
22///
23/// This structure is safe to use as long as the pointer is valid. The pointer is
24/// always opaque, and therefore may not be dereferenced.
25///
26/// # Example
27///
28/// ```
29/// struct MyPlugin;
30///
31/// impl qemu_plugin::plugin::Register for MyPlugin {}
32///
33/// impl qemu_plugin::plugin::HasCallbacks for MyPlugin {
34///     fn on_translation_block_translate(
35///         &mut self,
36///         id: qemu_plugin::PluginId,
37///         tb: qemu_plugin::TranslationBlock,
38///     ) -> anyhow::Result<()> {
39///         for insn in tb.instructions() {
40///             let vaddr = insn.vaddr();
41///             let disas = insn.disas()?;
42///             // Register a callback to be run on execution of this instruction
43///             insn.register_execute_callback(move |vcpu_index| {
44///                 println!("{vcpu_index}@{vaddr:#x}: {disas}");
45///             });
46///         }
47///         Ok(())
48///     }
49/// }
50/// ```
51pub struct Instruction<'a> {
52    #[allow(unused)]
53    // NOTE: This field may be useful in the future
54    translation_block: &'a TranslationBlock<'a>,
55    pub(crate) instruction: usize,
56    marker: PhantomData<&'a ()>,
57}
58
59impl<'a> Instruction<'a> {
60    pub(crate) fn new(
61        translation_block: &'a TranslationBlock<'a>,
62        insn: *mut qemu_plugin_insn,
63    ) -> Self {
64        Self {
65            translation_block,
66            instruction: insn as usize,
67            marker: PhantomData,
68        }
69    }
70}
71
72impl<'a> Instruction<'a> {
73    #[cfg(any(
74        feature = "plugin-api-v0",
75        feature = "plugin-api-v1",
76        feature = "plugin-api-v2"
77    ))]
78    /// Returns the data for this instruction. This method may only be called inside the
79    /// callback in which the instruction is obtained, but the resulting data is owned.
80    pub fn data(&self) -> Vec<u8> {
81        let size = self.size();
82        let mut data = Vec::with_capacity(size);
83
84        // NOTE: The name of this API doesn't change, but its parameters and return value *do*
85        let insn_data =
86            unsafe { crate::sys::qemu_plugin_insn_data(self.instruction as *mut qemu_plugin_insn) }
87                as *mut u8;
88
89        unsafe {
90            data.set_len(size);
91            std::ptr::copy_nonoverlapping(insn_data, data.as_mut_ptr(), size);
92        }
93
94        data
95    }
96
97    #[cfg(not(any(
98        feature = "plugin-api-v0",
99        feature = "plugin-api-v1",
100        feature = "plugin-api-v2"
101    )))]
102    /// Reads the data for this instruction returning number of bytes read. This method may only be
103    /// called inside the callback in which the instruction is obtained.
104    pub fn read_data(&self, data: &mut [u8]) -> usize {
105        // NOTE: The name of this API doesn't change, but its parameters and return value *do*
106        unsafe {
107            crate::sys::qemu_plugin_insn_data(
108                self.instruction as *mut qemu_plugin_insn,
109                data.as_mut_ptr() as *mut _,
110                data.len(),
111            )
112        }
113    }
114
115    #[cfg(not(any(
116        feature = "plugin-api-v0",
117        feature = "plugin-api-v1",
118        feature = "plugin-api-v2"
119    )))]
120    /// Returns the data for this instruction. This method may only be called inside the
121    /// callback in which the instruction is obtained, but the resulting data is owned.
122    pub fn data(&self) -> Vec<u8> {
123        let size = self.size();
124        let mut data = vec![0; size];
125
126        let size = self.read_data(&mut data);
127
128        data.truncate(size);
129
130        data
131    }
132
133    /// Returns the size of the data for this instruction
134    pub fn size(&self) -> usize {
135        unsafe { crate::sys::qemu_plugin_insn_size(self.instruction as *mut qemu_plugin_insn) }
136    }
137
138    /// Returns the virtual address of this instruction
139    pub fn vaddr(&self) -> u64 {
140        unsafe { crate::sys::qemu_plugin_insn_vaddr(self.instruction as *mut qemu_plugin_insn) }
141    }
142
143    /// Returns the hardware (physical) address of this instruction
144    pub fn haddr(&self) -> u64 {
145        (unsafe { crate::sys::qemu_plugin_insn_haddr(self.instruction as *mut qemu_plugin_insn) })
146            as usize as u64
147    }
148
149    /// Returns the textual disassembly of this instruction
150    pub fn disas(&self) -> Result<String> {
151        let disas = unsafe {
152            crate::sys::qemu_plugin_insn_disas(self.instruction as *mut qemu_plugin_insn)
153        };
154        if disas.is_null() {
155            Err(Error::NoDisassemblyString)
156        } else {
157            let disas_string = unsafe { CStr::from_ptr(disas) }.to_str()?.to_string();
158
159            // NOTE: The string is allocated, so we free it
160            unsafe { g_free(disas as *mut _) };
161
162            Ok(disas_string)
163        }
164    }
165
166    #[cfg(not(feature = "plugin-api-v0"))]
167    /// Returns the symbol associated with this instruction, if one exists and the
168    /// binary contains a symbol table
169    pub fn symbol(&self) -> Result<Option<String>> {
170        let symbol = unsafe {
171            crate::sys::qemu_plugin_insn_symbol(self.instruction as *mut qemu_plugin_insn)
172        };
173        if symbol.is_null() {
174            Ok(None)
175        } else {
176            let symbol_string = unsafe { CStr::from_ptr(symbol) }.to_str()?.to_string();
177            // NOTE: The string is static, so we do not free it
178            Ok(Some(symbol_string))
179        }
180    }
181
182    /// Register a callback to be run on execution of this instruction with no
183    /// capability to inspect registers
184    ///
185    /// # Arguments
186    ///
187    /// - `cb`: The callback to be run
188    pub fn register_execute_callback<F>(&self, cb: F)
189    where
190        F: FnMut(VCPUIndex) + Send + Sync + 'static,
191    {
192        self.register_execute_callback_flags(cb, CallbackFlags::QEMU_PLUGIN_CB_NO_REGS)
193    }
194
195    /// Register a callback to be run on execution of this instruction with a choice of
196    /// capability whether to inspect or modify registers or not
197    ///
198    /// # Arguments
199    ///
200    /// - `cb`: The callback to be run
201    /// - `flags`: The flags for the callback specifying whether the callback needs
202    ///   permission to read or write registers
203    pub fn register_execute_callback_flags<F>(&self, cb: F, flags: CallbackFlags)
204    where
205        F: FnMut(VCPUIndex) + Send + Sync + 'static,
206    {
207        let callback = Box::new(cb);
208        let callback_box = Box::new(callback);
209        let userdata = Box::into_raw(callback_box) as *mut c_void;
210
211        unsafe {
212            crate::sys::qemu_plugin_register_vcpu_insn_exec_cb(
213                self.instruction as *mut qemu_plugin_insn,
214                Some(handle_qemu_plugin_register_vcpu_insn_exec_cb::<F>),
215                flags,
216                userdata,
217            )
218        };
219    }
220
221    /// Register a callback to be conditionally run on execution of this instruction
222    /// with no capability to inspect registers
223    ///
224    /// # Arguments
225    ///
226    /// - `cb`: The callback to be run
227    /// - `cond`: The condition for the callback to be run
228    /// - `entry`: The entry to increment the scoreboard for
229    /// - `immediate`: The immediate value to use for the callback
230    #[cfg(not(any(
231        feature = "plugin-api-v0",
232        feature = "plugin-api-v1",
233        feature = "plugin-api-v2"
234    )))]
235    pub fn register_conditional_execute_callback<F>(
236        &self,
237        cb: F,
238        cond: PluginCondition,
239        entry: PluginU64,
240        immediate: u64,
241    ) where
242        F: FnMut(VCPUIndex) + Send + Sync + 'static,
243    {
244        self.register_conditional_execute_callback_flags(
245            cb,
246            CallbackFlags::QEMU_PLUGIN_CB_NO_REGS,
247            cond,
248            entry,
249            immediate,
250        )
251    }
252
253    /// Register a callback to be conditionally run on execution of this instruction
254    /// with a choice of capability whether to inspect or modify registers or not
255    ///
256    /// # Arguments
257    ///
258    /// - `cb`: The callback to be run
259    /// - `flags`: The flags for the callback specifying whether the callback needs
260    ///   permission to read or write registers
261    /// - `cond`: The condition for the callback to be run
262    /// - `entry`: The entry to increment the scoreboard for
263    /// - `immediate`: The immediate value to use for the callback
264    #[cfg(not(any(
265        feature = "plugin-api-v0",
266        feature = "plugin-api-v1",
267        feature = "plugin-api-v2"
268    )))]
269    pub fn register_conditional_execute_callback_flags<F>(
270        &self,
271        cb: F,
272        flags: CallbackFlags,
273        cond: PluginCondition,
274        entry: PluginU64,
275        immediate: u64,
276    ) where
277        F: FnMut(VCPUIndex) + Send + Sync + 'static,
278    {
279        let callback = Box::new(cb);
280        let callback_box = Box::new(callback);
281        let userdata = Box::into_raw(callback_box) as *mut c_void;
282
283        unsafe {
284            crate::sys::qemu_plugin_register_vcpu_insn_exec_cond_cb(
285                self.instruction as *mut qemu_plugin_insn,
286                Some(handle_qemu_plugin_register_vcpu_insn_exec_cb::<F>),
287                flags,
288                cond,
289                entry,
290                immediate,
291                userdata,
292            )
293        };
294    }
295
296    /// Register a callback to be run on memory access of this instruction
297    ///
298    /// # Arguments
299    ///
300    /// - `cb`: The callback to be run
301    /// - `rw`: The type of memory access to trigger the callback on
302    pub fn register_memory_access_callback<F>(&self, cb: F, rw: MemRW)
303    where
304        F: for<'b> FnMut(VCPUIndex, MemoryInfo<'b>, u64) + Send + Sync + 'static,
305    {
306        self.register_memory_access_callback_flags(cb, rw, CallbackFlags::QEMU_PLUGIN_CB_NO_REGS)
307    }
308
309    /// Register a callback to be run on memory access of this instruction
310    ///
311    /// # Arguments
312    ///
313    /// - `cb`: The callback to be run
314    /// - `rw`: The type of memory access to trigger the callback on
315    pub fn register_memory_access_callback_flags<F>(&self, cb: F, rw: MemRW, flags: CallbackFlags)
316    where
317        F: for<'b> FnMut(VCPUIndex, MemoryInfo<'b>, u64) + Send + Sync + 'static,
318    {
319        let callback = Box::new(cb);
320        let callback_box = Box::new(callback);
321        let userdata = Box::into_raw(callback_box) as *mut c_void;
322
323        unsafe {
324            crate::sys::qemu_plugin_register_vcpu_mem_cb(
325                self.instruction as *mut qemu_plugin_insn,
326                Some(handle_qemu_plugin_register_vcpu_mem_cb::<F>),
327                flags,
328                rw,
329                userdata,
330            )
331        };
332    }
333}