Skip to main content

il2cpp_bridge_rs/memory/info/
symbol.rs

1//! Symbol resolution and caching utilities
2
3use dashmap::DashMap;
4use once_cell::sync::Lazy;
5use thiserror::Error;
6
7#[derive(Error, Debug)]
8/// Errors that can occur during symbol resolution
9pub enum SymbolError {
10    /// The specified symbol was not found
11    #[error("Symbol not found: {0}")]
12    NotFound(String),
13    /// Failed to convert the symbol name to a CString
14    #[error("CString error")]
15    StringError,
16}
17
18static CACHE: Lazy<DashMap<String, usize>> = Lazy::new(DashMap::new);
19
20/// Resolves a symbol to its address using platform-specific lookup.
21///
22/// # Arguments
23/// * `symbol` - The name of the symbol to resolve (e.g., "MGCopyAnswer")
24///
25/// # Returns
26/// * `Result<usize, SymbolError>` - The address of the symbol or an error
27pub fn resolve_symbol(symbol: &str) -> Result<usize, SymbolError> {
28    if let Some(entry) = CACHE.get(symbol) {
29        return Ok(*entry);
30    }
31
32    let addr = platform::raw_resolve(symbol)?;
33    CACHE.insert(symbol.into(), addr);
34    Ok(addr)
35}
36
37/// Manually caches a symbol address
38///
39/// Use this if you have resolved a symbol via other means and want to store it for future lookups.
40///
41/// # Arguments
42/// * `s` - The symbol name
43/// * `a` - The symbol address
44pub fn cache_symbol(s: &str, a: usize) {
45    CACHE.insert(s.into(), a);
46}
47
48/// Clears the symbol cache
49pub fn clear_cache() {
50    CACHE.clear();
51}
52
53// macOS / iOS: use dlsym with RTLD_DEFAULT to search all loaded dylibs.
54#[cfg(any(target_os = "macos", target_os = "ios"))]
55mod platform {
56    use super::SymbolError;
57    use std::ffi::CString;
58
59    pub fn raw_resolve(symbol: &str) -> Result<usize, SymbolError> {
60        let c_str = CString::new(symbol).map_err(|_| SymbolError::StringError)?;
61        unsafe {
62            let addr_ptr = libc::dlsym(libc::RTLD_DEFAULT, c_str.as_ptr());
63            if addr_ptr.is_null() {
64                Err(SymbolError::NotFound(symbol.into()))
65            } else {
66                Ok(addr_ptr as usize)
67            }
68        }
69    }
70}
71
72// Linux / Android: use dlsym with RTLD_DEFAULT to search all loaded shared objects.
73#[cfg(any(target_os = "linux", target_os = "android"))]
74mod platform {
75    use super::SymbolError;
76    use std::ffi::CString;
77
78    pub fn raw_resolve(symbol: &str) -> Result<usize, SymbolError> {
79        let c_str = CString::new(symbol).map_err(|_| SymbolError::StringError)?;
80        unsafe {
81            let addr_ptr = libc::dlsym(libc::RTLD_DEFAULT, c_str.as_ptr());
82            if addr_ptr.is_null() {
83                Err(SymbolError::NotFound(symbol.into()))
84            } else {
85                Ok(addr_ptr as usize)
86            }
87        }
88    }
89}
90
91// Windows: use GetProcAddress with a null module handle to search the main executable.
92#[cfg(target_os = "windows")]
93mod platform {
94    use super::SymbolError;
95    use std::ffi::CString;
96    use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};
97
98    pub fn raw_resolve(symbol: &str) -> Result<usize, SymbolError> {
99        let c_str = CString::new(symbol).map_err(|_| SymbolError::StringError)?;
100        unsafe {
101            // Null handle returns the calling process module, similar to RTLD_DEFAULT on Unix.
102            let handle = GetModuleHandleA(std::ptr::null());
103            if handle == 0 {
104                return Err(SymbolError::NotFound(symbol.into()));
105            }
106            let addr = GetProcAddress(handle, c_str.as_ptr() as *const u8);
107            match addr {
108                Some(f) => Ok(f as usize),
109                None => Err(SymbolError::NotFound(symbol.into())),
110            }
111        }
112    }
113}
114
115// Unsupported platforms: always return an error.
116#[cfg(not(any(
117    target_os = "macos",
118    target_os = "ios",
119    target_os = "linux",
120    target_os = "android",
121    target_os = "windows",
122)))]
123mod platform {
124    use super::SymbolError;
125
126    pub fn raw_resolve(symbol: &str) -> Result<usize, SymbolError> {
127        Err(SymbolError::NotFound(format!(
128            "{} (unsupported platform)",
129            symbol
130        )))
131    }
132}