panda/plugins/
mod.rs

1//! Bindings for various built-in PANDA plugins
2
3use crate::{sys::panda_require, ARCH_NAME};
4use libloading::Symbol;
5use once_cell::sync::OnceCell;
6use std::ffi::CString;
7use std::path::{Path, PathBuf};
8
9pub mod cosi;
10pub mod glib;
11pub mod guest_plugin_manager;
12pub mod hooks;
13pub mod hooks2;
14pub mod osi;
15pub mod proc_start_linux;
16
17#[cfg(not(feature = "ppc"))]
18pub mod syscalls2;
19
20/// A macro for importing an external PANDA plugin to use
21///
22/// **Note:** it is recommended that, if the plugin you want to use already has
23/// panda-rs bindings, they should be used instead. Those are located in the
24/// [`plugins`](crate::plugins) module, and typically include a note about where
25/// the high-level bindings for the given plugin are located.
26///
27/// ## Example Usage
28///
29/// ### Declaring bindings for free function in an external plugin:
30///
31/// ```
32/// plugin_import!{
33///     static OSI: Osi = extern "osi" {
34///         fn get_process_handles(cpu: *mut CPUState) -> GBoxedSlice<OsiProcHandle>;
35///         fn get_current_thread(cpu: *mut CPUState) -> GBox<OsiThread>;
36///         fn get_modules(cpu: *mut CPUState) -> GBoxedSlice<OsiModule>;
37///         fn get_mappings(cpu: *mut CPUState, p: *mut OsiProc) -> GBoxedSlice<OsiModule>;
38///         fn get_processes(cpu: *mut CPUState) -> GBoxedSlice<OsiProc>;
39///         fn get_current_process(cpu: *mut CPUState) -> GBox<OsiProc>;
40///     };
41/// }
42/// ```
43///
44/// This will create a lazy initialized static variable named `OSI` in the current
45/// scope. This static will include all of the functions listed as methods, when
46/// any function is run the plugin (the name of which is specified by `extern "osi"`)
47/// will be loaded on the fly before executing the method.
48///
49/// To load a plugin without running any function, `plugin_import` also automatically
50/// creates an `ensure_init` method which initializes the plugin without any other
51/// side effects.
52///
53/// ### Plugin Callbacks
54///
55/// Plugin-to-Plugin callbacks in PANDA are typically quite verbose to make bindings for
56/// by hand, so the `plugin_import` macro provides a shorthand for defining a function
57/// prototype for the callback and it will generate all the code needed to add and remove
58/// callbacks for it.
59///
60/// ```
61/// plugin_import! {
62///     static PROC_START_LINUX: ProcStartLinux = extern "proc_start_linux" {
63///         callbacks {
64///             fn on_rec_auxv(cpu: &mut CPUState, tb: &mut TranslationBlock, auxv: &AuxvValues);
65///         }
66///     };
67/// }
68/// ```
69///
70/// the above creates another lazy static which has the following methods for working with
71/// the `on_rec_auxv` callback:
72///
73/// * `add_callback_on_rec_auxv` - add a callback by function pointer
74/// * `remove_callback_on_rec_auxv` - remove a callback by function pointer
75///
76/// One requirement of these function pointers is that they must use the C ABI. So the
77/// argument for both methods would be of the type:
78///
79/// ```
80/// extern "C" fn (&mut CPUState, &mut TranslationBlock, &AuxvValues)
81/// ```
82///
83/// This macro will also generate a trait allowing any plugin-to-plugin callbacks to be
84/// used via the [`PppCallback`] API. So the above would generate
85/// a trait called `ProcStartLinuxCallbacks` which would have a method called `on_rec_auxv`,
86/// which is automatically implemented for [`PppCallback`].
87///
88/// [`PppCallback`]: crate::PppCallback
89#[macro_export]
90macro_rules! plugin_import {
91    {
92        $(
93            #[ $type_meta:meta ]
94        )*
95        static $static:ident : $ty:ident = extern $name:literal {
96        $(
97            $(
98                #[$meta:meta]
99             )*
100            fn $fn_name:ident
101                $(
102                    <
103                        $(
104                            $lifetimes:lifetime
105                        ),*
106                        $(,)?
107                    >
108                )?
109            (
110                $(
111                    $arg_name:ident : $arg_ty:ty
112                 ),*
113                $(,)?
114            ) $(-> $fn_ret:ty)?;
115         )*
116        $(
117            callbacks {
118                $(
119                    fn $cb_fn_name:ident(
120                        $(
121                            $cb_arg_name:ident : $cb_arg_ty:ty
122                         ),*
123                        $(,)?
124                    ) $(-> $cb_fn_ret:ty)?;
125                )*
126            }
127        )?
128        };
129    } => {
130        $(
131            #[ $type_meta ]
132        )*
133        pub struct $ty {
134            plugin: $crate::plugins::Plugin
135        }
136
137        impl $ty {
138            /// Create a new handle to this plugin
139            pub fn new() -> Self {
140                Self {
141                    plugin: $crate::plugins::Plugin::new($name)
142                }
143            }
144
145            /// Load the plugin and initialize it if it hasn't been loaded already.
146            pub fn ensure_init(&self) {}
147
148            $(
149                $(
150                    #[$meta]
151                 )*
152                pub fn $fn_name $(< $($lifetimes),* >)? (&self $(, $arg_name : $arg_ty )*) $(-> $fn_ret)? {
153                    unsafe {
154                        self.plugin.get::<unsafe extern "C" fn($($arg_ty),*) $(-> $fn_ret)?>(
155                            stringify!($fn_name)
156                        )(
157                            $(
158                                $arg_name
159                            ),*
160                        )
161                    }
162                }
163             )*
164
165            $($(
166                $crate::paste::paste!{
167                    pub fn [<add_callback_ $cb_fn_name>](
168                        &self,
169                        callback: extern "C" fn(
170                            $($cb_arg_name: $cb_arg_ty),*
171                        )
172                    )
173                    {
174                        let add_cb = self.plugin.get::<
175                            extern "C" fn(
176                                extern "C" fn(
177                                    $($cb_arg_ty),*
178                                ) $(-> $cb_fn_ret)?
179                            )
180                        >(
181                            concat!("ppp_add_cb_", stringify!($cb_fn_name))
182                        );
183
184                        add_cb(callback);
185                    }
186
187                    pub fn [<remove_callback_ $cb_fn_name>](
188                        &self,
189                        callback: extern "C" fn(
190                            $($cb_arg_name: $cb_arg_ty),*
191                        )
192                    )
193                    {
194                        let remove_cb = self.plugin.get::<
195                            extern "C" fn(
196                                extern "C" fn(
197                                    $($cb_arg_ty),*
198                                ) $(-> $cb_fn_ret)?
199                            )
200                        >(
201                            concat!("ppp_remove_cb_", stringify!($cb_fn_name))
202                        );
203
204                        remove_cb(callback);
205                    }
206
207                    #[doc(hidden)]
208                    pub fn [<add_callback_ $cb_fn_name _with_context>](
209                        &self,
210                        callback: unsafe extern "C" fn(
211                            *mut std::ffi::c_void, $($cb_arg_name: $cb_arg_ty),*
212                        ),
213                        context: *mut std::ffi::c_void,
214                    )
215                    {
216                        let add_cb = self.plugin.get::<
217                            extern "C" fn(
218                                unsafe extern "C" fn(
219                                    *mut std::ffi::c_void, $($cb_arg_ty),*
220                                ) $(-> $cb_fn_ret)?,
221                                *mut std::ffi::c_void,
222                            )
223                        >(
224                            concat!("ppp_add_cb_", stringify!($cb_fn_name), "_with_context")
225                        );
226
227                        add_cb(callback, context);
228                    }
229
230                    #[doc(hidden)]
231                    pub fn [<remove_callback_ $cb_fn_name _with_context>](
232                        &self,
233                        callback: unsafe extern "C" fn(
234                            *mut std::ffi::c_void, $($cb_arg_name: $cb_arg_ty),*
235                        ),
236                        context: *mut std::ffi::c_void,
237                    )
238                    {
239                        let remove_cb = self.plugin.get::<
240                            extern "C" fn(
241                                unsafe extern "C" fn(
242                                    *mut std::ffi::c_void, $($cb_arg_ty),*
243                                ) $(-> $cb_fn_ret)?,
244                                *mut std::ffi::c_void,
245                            )
246                        >(
247                            concat!("ppp_remove_cb_", stringify!($cb_fn_name), "_with_context")
248                        );
249
250                        remove_cb(callback, context);
251                    }
252                }
253            )*)?
254        }
255
256        $crate::lazy_static::lazy_static!{
257            $(
258                #[ $type_meta ]
259            )*
260            pub static ref $static: $ty = $ty::new();
261        }
262
263        $(
264            $crate::paste::paste!{
265                /// A trait for expressing the plugin-to-plugin callbacks provided by
266                /// the given plugin. See `panda::PppCallback` for more information,
267                /// as this is intended to be used as an extension trait for it.
268                pub trait [<$ty Callbacks>] {
269                    $(
270                        /// Installs the given closure over the callback slot provided
271                        /// by the `panda::PppCallback` this is called on, setting it to
272                        /// be run whenever the `
273                        #[doc = stringify!($cb_fn_name)]
274                        ///` callback is hit.
275                        ///
276                        /// ## Arguments
277                        ///
278                        $(
279                            #[doc = "* `"]
280                            #[doc = stringify!($cb_arg_name)]
281                            #[doc = "` - `"]
282                            #[doc = stringify!($cb_arg_ty)]
283                            #[doc = "`"]
284                            #[doc = ""]
285                        )*
286                        /// ## Example
287                        ///
288                        /// ```
289                        /// use panda::PppCallback;
290                        /// use panda::prelude::*;
291                        #[doc = concat!(
292                            "use /*...*/::",
293                            stringify!($ty),
294                            "Callbacks;"
295                        )]
296                        ///
297                        #[doc = concat!(
298                            "PppCallbacks::new()\n    .",
299                            stringify!($cb_fn_name),
300                            "(|",
301                            $(
302                                stringify!($cb_arg_name),
303                                ": ",
304                                stringify!($cb_arg_ty),
305                                ", ",
306                            )*
307                            "|{\n        // callback code\n    });"
308                        )]
309                        /// ```
310                        fn $cb_fn_name<CallbackFn>(self, callback: CallbackFn)
311                            where CallbackFn: FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)? + 'static;
312                    )*
313                }
314
315                impl [<$ty Callbacks>] for $crate::PppCallback {
316                    $(
317                        fn $cb_fn_name<CallbackFn>(self, callback: CallbackFn)
318                            where CallbackFn: FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)? + 'static
319                        {
320                            use std::ffi::c_void;
321                            let closure_ref: *mut c_void = unsafe {
322                                let x: Box<Box<
323                                    dyn FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)?
324                                >> = Box::new(
325                                    Box::new(callback) as Box<_>
326                                );
327                                core::mem::transmute(x)
328                            };
329
330                            unsafe extern "C" fn trampoline(
331                                context: *mut c_void, $($cb_arg_name : $cb_arg_ty),*
332                            ) $(-> $cb_fn_ret)?
333                            {
334                                let closure: &mut &mut (
335                                    dyn FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)?
336                                ) = core::mem::transmute(
337                                    context
338                                );
339
340                                closure($($cb_arg_name),*)
341                            }
342
343                            unsafe fn drop_fn(this: *mut c_void) {
344                                let _: Box<Box<
345                                    dyn FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)?
346                                >> = core::mem::transmute(this);
347                            }
348
349                            unsafe fn enable(this: *mut c_void) {
350                                $static.[<add_callback_ $cb_fn_name _with_context>](
351                                    trampoline,
352                                    this
353                                );
354                            }
355
356                            unsafe fn disable(this: *mut c_void) {
357                                $static.[<remove_callback_ $cb_fn_name _with_context>](
358                                    trampoline,
359                                    this
360                                );
361                            }
362
363                            let callback = $crate::InternalPppClosureCallback {
364                                closure_ref,
365                                drop_fn,
366                                enable,
367                                disable,
368                                is_enabled: false,
369                            };
370                            $crate::Panda::run_after_init(move || {
371                                unsafe {
372                                    $crate::__internal_install_ppp_closure_callback(
373                                        self,
374                                        callback
375                                    );
376                                }
377                            });
378                        }
379                    )*
380                }
381            }
382        )?
383    }
384}
385
386/// A wrapper for a dynamic library loaded as a PANDA plugin. Is used internally by
387/// the [`plugin_import`] macro to manage loading/unloading PANDA plugins lazily.
388pub struct Plugin {
389    lib: libloading::Library,
390}
391
392const PANDA_GLOBAL_INSTALLS: &[&str] = &["/usr/local/lib/panda", "/usr/lib/panda"];
393
394fn get_panda_path() -> Option<&'static Path> {
395    static PANDA_PATH: OnceCell<Option<PathBuf>> = OnceCell::new();
396
397    PANDA_PATH
398        .get_or_init(|| {
399            if let Ok(path) = std::env::var("PANDA_PATH") {
400                Some(PathBuf::from(path))
401            } else {
402                for possible_path in PANDA_GLOBAL_INSTALLS {
403                    let path = PathBuf::from(possible_path);
404
405                    if path.exists() {
406                        return Some(path);
407                    }
408                }
409
410                None
411            }
412        })
413        .as_deref()
414}
415
416fn get_panda_plugin_dir() -> Option<&'static Path> {
417    static PANDA_PLUGIN_DIR: OnceCell<Option<PathBuf>> = OnceCell::new();
418
419    PANDA_PLUGIN_DIR
420        .get_or_init(|| {
421            let panda_path = get_panda_path()?;
422
423            if let Ok(path) = std::env::var("PANDA_PLUGIN_DIR") {
424                Some(panda_path.join(path))
425            } else {
426                let path = panda_path.join(&format!("{}/panda/plugins", ARCH_NAME));
427                if path.exists() {
428                    return Some(path);
429                }
430
431                let path = panda_path.join(&format!("{}-softmmu/panda/plugins", ARCH_NAME));
432                if path.exists() {
433                    return Some(path);
434                }
435
436                let path = panda_path.join(ARCH_NAME);
437                if path.exists() {
438                    return Some(path);
439                }
440
441                None
442            }
443        })
444        .as_deref()
445}
446
447impl Plugin {
448    pub fn new(name: &str) -> Self {
449        let panda_path =
450            get_panda_path().expect("PANDA_PATH not set and PANDA is not installed globally");
451
452        unsafe {
453            std::env::set_var("PANDA_DIR", &panda_path);
454
455            let c_name = CString::new(name).unwrap();
456            panda_require(c_name.as_ptr());
457        }
458
459        let path = get_panda_plugin_dir()
460            .expect("Could not find panda plugin dir, consider setting PANDA_PLUGIN_DIR")
461            .join(&format!("panda_{}.so", name));
462
463        if !path.exists() {
464            panic!("Could not find plugin {} at {}", name, path.display());
465        }
466
467        Self {
468            lib: libloading::Library::new(path).expect("Failed to load plugin"),
469        }
470    }
471
472    pub fn get<T>(&self, sym: &str) -> Symbol<T> {
473        let symbol: Vec<_> = sym.bytes().chain(std::iter::once(0)).collect();
474        unsafe { self.lib.get(&symbol).expect("Could not find symbol") }
475    }
476}