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