use std::sync::LazyLock; pub static LIBG_BASE: LazyLock = LazyLock::new(|| get_module_base("libg.so").expect("failed to get libg.so base address")); #[repr(transparent)] pub struct Nullable(*const T); impl Nullable { pub fn get(&self) -> Option<&T> { (!self.0.is_null()).then(|| unsafe { &*self.0 }) } pub fn get_mut(&mut self) -> Option<&mut T> { (!self.0.is_null()).then(|| unsafe { &mut *self.0.cast_mut() }) } pub fn set(&mut self, value: *const T) { self.0 = value; } } macro_rules! import { ($name:ident($($arg_name:ident: $arg_type:ty),*) -> $ret_type:ty = $rva:expr) => { pub fn $name($($arg_name: $arg_type,)*) -> $ret_type { unsafe { type FuncType = unsafe extern "cdecl" fn($($arg_type,)*) -> $ret_type; ::std::mem::transmute::(*crate::ffi_util::LIBG_BASE + $rva)($($arg_name,)*) } } }; } pub(crate) use import; pub fn get_module_base(shared_object_name: &str) -> Option { use std::{ fs::File, io::{self, BufRead}, }; let file = File::open("/proc/self/maps").expect("failed to open /proc/self/maps"); let reader = io::BufReader::new(file); for line in reader.lines() { let line = line.ok()?; if line.contains(shared_object_name) { let address_str = line.split_whitespace().next().unwrap_or(""); let address = usize::from_str_radix(&address_str.split('-').next().unwrap_or(""), 16).ok()?; return Some(address); } } None }