use crate::{sys::panda_require, ARCH_NAME};
use libloading::Symbol;
use once_cell::sync::OnceCell;
use std::ffi::CString;
use std::path::{Path, PathBuf};
pub mod cosi;
pub mod glib;
pub mod guest_plugin_manager;
pub mod hooks;
pub mod hooks2;
pub mod osi;
pub mod proc_start_linux;
#[cfg(not(feature = "ppc"))]
pub mod syscalls2;
#[macro_export]
macro_rules! plugin_import {
{
$(
#[ $type_meta:meta ]
)*
static $static:ident : $ty:ident = extern $name:literal {
$(
$(
#[$meta:meta]
)*
fn $fn_name:ident
$(
<
$(
$lifetimes:lifetime
),*
$(,)?
>
)?
(
$(
$arg_name:ident : $arg_ty:ty
),*
$(,)?
) $(-> $fn_ret:ty)?;
)*
$(
callbacks {
$(
fn $cb_fn_name:ident(
$(
$cb_arg_name:ident : $cb_arg_ty:ty
),*
$(,)?
) $(-> $cb_fn_ret:ty)?;
)*
}
)?
};
} => {
$(
#[ $type_meta ]
)*
pub struct $ty {
plugin: $crate::plugins::Plugin
}
impl $ty {
pub fn new() -> Self {
Self {
plugin: $crate::plugins::Plugin::new($name)
}
}
pub fn ensure_init(&self) {}
$(
$(
#[$meta]
)*
pub fn $fn_name $(< $($lifetimes),* >)? (&self $(, $arg_name : $arg_ty )*) $(-> $fn_ret)? {
unsafe {
self.plugin.get::<unsafe extern "C" fn($($arg_ty),*) $(-> $fn_ret)?>(
stringify!($fn_name)
)(
$(
$arg_name
),*
)
}
}
)*
$($(
$crate::paste::paste!{
pub fn [<add_callback_ $cb_fn_name>](
&self,
callback: extern "C" fn(
$($cb_arg_name: $cb_arg_ty),*
)
)
{
let add_cb = self.plugin.get::<
extern "C" fn(
extern "C" fn(
$($cb_arg_ty),*
) $(-> $cb_fn_ret)?
)
>(
concat!("ppp_add_cb_", stringify!($cb_fn_name))
);
add_cb(callback);
}
pub fn [<remove_callback_ $cb_fn_name>](
&self,
callback: extern "C" fn(
$($cb_arg_name: $cb_arg_ty),*
)
)
{
let remove_cb = self.plugin.get::<
extern "C" fn(
extern "C" fn(
$($cb_arg_ty),*
) $(-> $cb_fn_ret)?
)
>(
concat!("ppp_remove_cb_", stringify!($cb_fn_name))
);
remove_cb(callback);
}
#[doc(hidden)]
pub fn [<add_callback_ $cb_fn_name _with_context>](
&self,
callback: unsafe extern "C" fn(
*mut std::ffi::c_void, $($cb_arg_name: $cb_arg_ty),*
),
context: *mut std::ffi::c_void,
)
{
let add_cb = self.plugin.get::<
extern "C" fn(
unsafe extern "C" fn(
*mut std::ffi::c_void, $($cb_arg_ty),*
) $(-> $cb_fn_ret)?,
*mut std::ffi::c_void,
)
>(
concat!("ppp_add_cb_", stringify!($cb_fn_name), "_with_context")
);
add_cb(callback, context);
}
#[doc(hidden)]
pub fn [<remove_callback_ $cb_fn_name _with_context>](
&self,
callback: unsafe extern "C" fn(
*mut std::ffi::c_void, $($cb_arg_name: $cb_arg_ty),*
),
context: *mut std::ffi::c_void,
)
{
let remove_cb = self.plugin.get::<
extern "C" fn(
unsafe extern "C" fn(
*mut std::ffi::c_void, $($cb_arg_ty),*
) $(-> $cb_fn_ret)?,
*mut std::ffi::c_void,
)
>(
concat!("ppp_remove_cb_", stringify!($cb_fn_name), "_with_context")
);
remove_cb(callback, context);
}
}
)*)?
}
$crate::lazy_static::lazy_static!{
$(
#[ $type_meta ]
)*
pub static ref $static: $ty = $ty::new();
}
$(
$crate::paste::paste!{
pub trait [<$ty Callbacks>] {
$(
#[doc = stringify!($cb_fn_name)]
$(
#[doc = "* `"]
#[doc = stringify!($cb_arg_name)]
#[doc = "` - `"]
#[doc = stringify!($cb_arg_ty)]
#[doc = "`"]
#[doc = ""]
)*
#[doc = concat!(
"use /*...*/::",
stringify!($ty),
"Callbacks;"
)]
#[doc = concat!(
"PppCallbacks::new()\n .",
stringify!($cb_fn_name),
"(|",
$(
stringify!($cb_arg_name),
": ",
stringify!($cb_arg_ty),
", ",
)*
"|{\n // callback code\n });"
)]
fn $cb_fn_name<CallbackFn>(self, callback: CallbackFn)
where CallbackFn: FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)? + 'static;
)*
}
impl [<$ty Callbacks>] for $crate::PppCallback {
$(
fn $cb_fn_name<CallbackFn>(self, callback: CallbackFn)
where CallbackFn: FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)? + 'static
{
use std::ffi::c_void;
let closure_ref: *mut c_void = unsafe {
let x: Box<Box<
dyn FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)?
>> = Box::new(
Box::new(callback) as Box<_>
);
core::mem::transmute(x)
};
unsafe extern "C" fn trampoline(
context: *mut c_void, $($cb_arg_name : $cb_arg_ty),*
) $(-> $cb_fn_ret)?
{
let closure: &mut &mut (
dyn FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)?
) = core::mem::transmute(
context
);
closure($($cb_arg_name),*)
}
unsafe fn drop_fn(this: *mut c_void) {
let _: Box<Box<
dyn FnMut($($cb_arg_ty),*) $(-> $cb_fn_ret)?
>> = core::mem::transmute(this);
}
unsafe fn enable(this: *mut c_void) {
$static.[<add_callback_ $cb_fn_name _with_context>](
trampoline,
this
);
}
unsafe fn disable(this: *mut c_void) {
$static.[<remove_callback_ $cb_fn_name _with_context>](
trampoline,
this
);
}
let callback = $crate::InternalPppClosureCallback {
closure_ref,
drop_fn,
enable,
disable,
is_enabled: false,
};
$crate::Panda::run_after_init(move || {
unsafe {
$crate::__internal_install_ppp_closure_callback(
self,
callback
);
}
});
}
)*
}
}
)?
}
}
pub struct Plugin {
lib: libloading::Library,
}
const PANDA_GLOBAL_INSTALLS: &[&str] = &["/usr/local/lib/panda", "/usr/lib/panda"];
fn get_panda_path() -> Option<&'static Path> {
static PANDA_PATH: OnceCell<Option<PathBuf>> = OnceCell::new();
PANDA_PATH
.get_or_init(|| {
if let Ok(path) = std::env::var("PANDA_PATH") {
Some(PathBuf::from(path))
} else {
for possible_path in PANDA_GLOBAL_INSTALLS {
let path = PathBuf::from(possible_path);
if path.exists() {
return Some(path);
}
}
None
}
})
.as_deref()
}
fn get_panda_plugin_dir() -> Option<&'static Path> {
static PANDA_PLUGIN_DIR: OnceCell<Option<PathBuf>> = OnceCell::new();
PANDA_PLUGIN_DIR
.get_or_init(|| {
let panda_path = get_panda_path()?;
if let Ok(path) = std::env::var("PANDA_PLUGIN_DIR") {
Some(panda_path.join(path))
} else {
let path = panda_path.join(&format!("{}/panda/plugins", ARCH_NAME));
if path.exists() {
return Some(path);
}
let path = panda_path.join(&format!("{}-softmmu/panda/plugins", ARCH_NAME));
if path.exists() {
return Some(path);
}
let path = panda_path.join(ARCH_NAME);
if path.exists() {
return Some(path);
}
None
}
})
.as_deref()
}
impl Plugin {
pub fn new(name: &str) -> Self {
let panda_path =
get_panda_path().expect("PANDA_PATH not set and PANDA is not installed globally");
unsafe {
std::env::set_var("PANDA_DIR", &panda_path);
let c_name = CString::new(name).unwrap();
panda_require(c_name.as_ptr());
}
let path = get_panda_plugin_dir()
.expect("Could not find panda plugin dir, consider setting PANDA_PLUGIN_DIR")
.join(&format!("panda_{}.so", name));
if !path.exists() {
panic!("Could not find plugin {} at {}", name, path.display());
}
Self {
lib: libloading::Library::new(path).expect("Failed to load plugin"),
}
}
pub fn get<T>(&self, sym: &str) -> Symbol<T> {
let symbol: Vec<_> = sym.bytes().chain(std::iter::once(0)).collect();
unsafe { self.lib.get(&symbol).expect("Could not find symbol") }
}
}