Skip to main content

il2cpp_bridge_rs/memory/info/
image.rs

1use dashmap::DashMap;
2use once_cell::sync::Lazy;
3
4static CACHE: Lazy<DashMap<String, usize>> = Lazy::new(DashMap::new);
5
6/// Returns the base address of the image with the given name, if loaded.
7///
8/// Results are cached after the first successful lookup.
9pub fn get_image_base(name: &str) -> Option<usize> {
10    if let Some(entry) = CACHE.get(name) {
11        return Some(*entry);
12    }
13
14    let addr = platform::find_image_base(name)?;
15    CACHE.insert(name.into(), addr);
16    Some(addr)
17}
18
19// macOS / iOS: iterate loaded dylibs via dyld to find the matching Mach-O header.
20#[cfg(any(target_os = "macos", target_os = "ios"))]
21mod platform {
22    use mach2::dyld::{_dyld_get_image_header, _dyld_get_image_name, _dyld_image_count};
23    use std::ffi::CStr;
24
25    pub fn find_image_base(name: &str) -> Option<usize> {
26        unsafe {
27            let count = _dyld_image_count();
28            for i in 0..count {
29                let c_name = _dyld_get_image_name(i);
30                if c_name.is_null() {
31                    continue;
32                }
33                let path = CStr::from_ptr(c_name).to_string_lossy();
34                if path.contains(name) {
35                    let header = _dyld_get_image_header(i);
36                    if !header.is_null() {
37                        return Some(header as usize);
38                    }
39                }
40            }
41        }
42        None
43    }
44}
45
46// Linux / Android: iterate loaded shared objects via dl_iterate_phdr to find the matching base address.
47#[cfg(any(target_os = "linux", target_os = "android"))]
48mod platform {
49    use std::ffi::CStr;
50
51    pub fn find_image_base(name: &str) -> Option<usize> {
52        struct CallbackData {
53            name: String,
54            result: Option<usize>,
55        }
56
57        unsafe extern "C" fn callback(
58            info: *mut libc::dl_phdr_info,
59            _size: libc::size_t,
60            data: *mut libc::c_void,
61        ) -> libc::c_int {
62            let data = &mut *(data as *mut CallbackData);
63            let dlpi_name = (*info).dlpi_name;
64            if dlpi_name.is_null() {
65                return 0;
66            }
67            let path = CStr::from_ptr(dlpi_name).to_string_lossy();
68            if path.contains(&data.name) {
69                data.result = Some((*info).dlpi_addr as usize);
70                return 1;
71            }
72            0
73        }
74
75        let mut data = CallbackData {
76            name: name.to_string(),
77            result: None,
78        };
79
80        unsafe {
81            libc::dl_iterate_phdr(Some(callback), &mut data as *mut _ as *mut libc::c_void);
82        }
83
84        data.result
85    }
86}
87
88// Windows: use GetModuleHandleA to retrieve the base address of a loaded module by name.
89#[cfg(target_os = "windows")]
90mod platform {
91    use std::ffi::CString;
92    use windows_sys::Win32::System::LibraryLoader::GetModuleHandleA;
93
94    pub fn find_image_base(name: &str) -> Option<usize> {
95        let c_name = CString::new(name).ok()?;
96        unsafe {
97            let handle = GetModuleHandleA(c_name.as_ptr() as *const u8);
98            if handle == 0 {
99                None
100            } else {
101                Some(handle as usize)
102            }
103        }
104    }
105}
106
107// Unsupported platforms: always return None.
108#[cfg(not(any(
109    target_os = "macos",
110    target_os = "ios",
111    target_os = "linux",
112    target_os = "android",
113    target_os = "windows",
114)))]
115mod platform {
116    pub fn find_image_base(_name: &str) -> Option<usize> {
117        None
118    }
119}