use dashmap::DashMap;
use once_cell::sync::Lazy;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum SymbolError {
#[error("Symbol not found: {0}")]
NotFound(String),
#[error("CString error")]
StringError,
}
static CACHE: Lazy<DashMap<String, usize>> = Lazy::new(DashMap::new);
pub fn resolve_symbol(symbol: &str) -> Result<usize, SymbolError> {
if let Some(entry) = CACHE.get(symbol) {
return Ok(*entry);
}
let addr = platform::raw_resolve(symbol)?;
CACHE.insert(symbol.into(), addr);
Ok(addr)
}
pub fn cache_symbol(s: &str, a: usize) {
CACHE.insert(s.into(), a);
}
pub fn clear_cache() {
CACHE.clear();
}
pub fn promote_library_to_global(library_name: &str) {
platform::ensure_global_visibility(library_name);
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
mod platform {
use super::SymbolError;
use std::ffi::CString;
pub fn raw_resolve(symbol: &str) -> Result<usize, SymbolError> {
let c_str = CString::new(symbol).map_err(|_| SymbolError::StringError)?;
unsafe {
let addr_ptr = libc::dlsym(libc::RTLD_DEFAULT, c_str.as_ptr());
if addr_ptr.is_null() {
Err(SymbolError::NotFound(symbol.into()))
} else {
Ok(addr_ptr as usize)
}
}
}
pub fn ensure_global_visibility(_library_name: &str) {}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
mod platform {
use super::SymbolError;
use std::ffi::{CStr, CString};
use std::sync::Once;
static PROMOTE_ONCE: Once = Once::new();
pub fn ensure_global_visibility(library_name: &str) {
PROMOTE_ONCE.call_once(|| {
let path = match crate::memory::image::get_image_path(library_name) {
Some(p) => p,
None => {
crate::logger::warning(&format!(
"Could not find loaded library matching '{}'; symbol promotion skipped",
library_name
));
return;
}
};
let c_path = match CString::new(path.as_str()) {
Ok(c) => c,
Err(_) => return,
};
unsafe {
let handle = libc::dlopen(
c_path.as_ptr(),
libc::RTLD_NOW | libc::RTLD_GLOBAL | libc::RTLD_NOLOAD,
);
if handle.is_null() {
let err = libc::dlerror();
let msg = if err.is_null() {
"unknown error".to_string()
} else {
CStr::from_ptr(err).to_string_lossy().into_owned()
};
crate::logger::warning(&format!(
"dlopen promotion failed for '{}': {}",
path, msg
));
} else {
crate::logger::info(&format!(
"Promoted '{}' symbols to global visibility",
path
));
}
}
});
}
pub fn raw_resolve(symbol: &str) -> Result<usize, SymbolError> {
let c_str = CString::new(symbol).map_err(|_| SymbolError::StringError)?;
unsafe {
let addr_ptr = libc::dlsym(libc::RTLD_DEFAULT, c_str.as_ptr());
if addr_ptr.is_null() {
Err(SymbolError::NotFound(symbol.into()))
} else {
Ok(addr_ptr as usize)
}
}
}
}
#[cfg(target_os = "windows")]
mod platform {
use super::SymbolError;
use std::ffi::CString;
use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};
pub fn raw_resolve(symbol: &str) -> Result<usize, SymbolError> {
let c_str = CString::new(symbol).map_err(|_| SymbolError::StringError)?;
unsafe {
let handle = GetModuleHandleA(std::ptr::null());
if handle == 0 {
return Err(SymbolError::NotFound(symbol.into()));
}
let addr = GetProcAddress(handle, c_str.as_ptr() as *const u8);
match addr {
Some(f) => Ok(f as usize),
None => Err(SymbolError::NotFound(symbol.into())),
}
}
}
pub fn ensure_global_visibility(_library_name: &str) {}
}
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "linux",
target_os = "android",
target_os = "windows",
)))]
mod platform {
use super::SymbolError;
pub fn raw_resolve(symbol: &str) -> Result<usize, SymbolError> {
Err(SymbolError::NotFound(format!(
"{} (unsupported platform)",
symbol
)))
}
pub fn ensure_global_visibility(_library_name: &str) {}
}