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/// Returns the full filesystem path of the loaded image matching `name`, if found.
20///
21/// On Linux/Android this walks loaded shared objects via `dl_iterate_phdr`.
22/// On other platforms this always returns `None`.
23pub fn get_image_path(name: &str) -> Option<String> {
24    platform::find_image_path(name)
25}
26
27// macOS / iOS: iterate loaded dylibs via dyld to find the matching Mach-O header.
28#[cfg(any(target_os = "macos", target_os = "ios"))]
29mod platform {
30    use mach2::dyld::{_dyld_get_image_header, _dyld_get_image_name, _dyld_image_count};
31    use std::ffi::CStr;
32
33    pub fn find_image_base(name: &str) -> Option<usize> {
34        unsafe {
35            let count = _dyld_image_count();
36            for i in 0..count {
37                let c_name = _dyld_get_image_name(i);
38                if c_name.is_null() {
39                    continue;
40                }
41                let path = CStr::from_ptr(c_name).to_string_lossy();
42                if path.contains(name) {
43                    let header = _dyld_get_image_header(i);
44                    if !header.is_null() {
45                        return Some(header as usize);
46                    }
47                }
48            }
49        }
50        None
51    }
52
53    pub fn find_image_path(_name: &str) -> Option<String> {
54        None
55    }
56}
57
58// Linux / Android: iterate loaded shared objects via dl_iterate_phdr to find the matching base address.
59#[cfg(any(target_os = "linux", target_os = "android"))]
60mod platform {
61    use std::ffi::CStr;
62
63    pub fn find_image_base(name: &str) -> Option<usize> {
64        struct CallbackData {
65            name: String,
66            result: Option<usize>,
67        }
68
69        unsafe extern "C" fn callback(
70            info: *mut libc::dl_phdr_info,
71            _size: libc::size_t,
72            data: *mut libc::c_void,
73        ) -> libc::c_int {
74            let data = &mut *(data as *mut CallbackData);
75            let dlpi_name = (*info).dlpi_name;
76            if dlpi_name.is_null() {
77                return 0;
78            }
79            let path = CStr::from_ptr(dlpi_name).to_string_lossy();
80            if path.contains(&data.name) {
81                data.result = Some((*info).dlpi_addr as usize);
82                return 1;
83            }
84            0
85        }
86
87        let mut data = CallbackData {
88            name: name.to_string(),
89            result: None,
90        };
91
92        unsafe {
93            libc::dl_iterate_phdr(Some(callback), &mut data as *mut _ as *mut libc::c_void);
94        }
95
96        data.result
97    }
98
99    pub fn find_image_path(name: &str) -> Option<String> {
100        struct CallbackData {
101            name: String,
102            result: Option<String>,
103        }
104
105        unsafe extern "C" fn callback(
106            info: *mut libc::dl_phdr_info,
107            _size: libc::size_t,
108            data: *mut libc::c_void,
109        ) -> libc::c_int {
110            let data = &mut *(data as *mut CallbackData);
111            let dlpi_name = (*info).dlpi_name;
112            if dlpi_name.is_null() {
113                return 0;
114            }
115            let path = CStr::from_ptr(dlpi_name).to_string_lossy();
116            if path.contains(&data.name) {
117                data.result = Some(path.into_owned());
118                return 1;
119            }
120            0
121        }
122
123        let mut data = CallbackData {
124            name: name.to_string(),
125            result: None,
126        };
127
128        unsafe {
129            libc::dl_iterate_phdr(Some(callback), &mut data as *mut _ as *mut libc::c_void);
130        }
131
132        data.result
133    }
134}
135
136// Windows: use GetModuleHandleA to retrieve the base address of a loaded module by name.
137#[cfg(target_os = "windows")]
138mod platform {
139    use std::ffi::CString;
140    use windows_sys::Win32::System::LibraryLoader::GetModuleHandleA;
141
142    pub fn find_image_base(name: &str) -> Option<usize> {
143        let c_name = CString::new(name).ok()?;
144        unsafe {
145            let handle = GetModuleHandleA(c_name.as_ptr() as *const u8);
146            if handle == 0 {
147                None
148            } else {
149                Some(handle as usize)
150            }
151        }
152    }
153
154    pub fn find_image_path(_name: &str) -> Option<String> {
155        None
156    }
157}
158
159// Unsupported platforms: always return None.
160#[cfg(not(any(
161    target_os = "macos",
162    target_os = "ios",
163    target_os = "linux",
164    target_os = "android",
165    target_os = "windows",
166)))]
167mod platform {
168    pub fn find_image_base(_name: &str) -> Option<usize> {
169        None
170    }
171
172    pub fn find_image_path(_name: &str) -> Option<String> {
173        None
174    }
175}