Skip to main content

dlopen_rs/api/
dl_iterate_phdr.rs

1use crate::{ElfLibrary, Error, Result, core_impl::register::MANAGER};
2use alloc::boxed::Box;
3use core::{
4    ffi::{c_char, c_int, c_ulonglong, c_void},
5    ptr::null_mut,
6};
7use elf_loader::{elf::ElfPhdr, tls::DefaultTlsResolver};
8
9/// same as dl_phdr_info in libc
10#[repr(C)]
11pub struct CDlPhdrInfo {
12    pub dlpi_addr: usize,
13    pub dlpi_name: *const c_char,
14    pub dlpi_phdr: *const ElfPhdr,
15    pub dlpi_phnum: u16,
16    pub dlpi_adds: c_ulonglong,
17    pub dlpi_subs: c_ulonglong,
18    pub dlpi_tls_modid: usize,
19    pub dlpi_tls_data: *mut c_void,
20}
21
22pub struct DlPhdrInfo<'lib> {
23    lib_base: usize,
24    lib_name: *const c_char,
25    phdrs: &'lib [ElfPhdr],
26    dlpi_adds: c_ulonglong,
27    dlpi_subs: c_ulonglong,
28    tls_modid: usize,
29    tls_data: Option<&'lib [u8]>,
30}
31
32impl DlPhdrInfo<'_> {
33    /// Get the name of the dynamic library.
34    #[inline]
35    pub fn name(&self) -> &str {
36        if self.lib_name.is_null() {
37            ""
38        } else {
39            unsafe {
40                core::ffi::CStr::from_ptr(self.lib_name)
41                    .to_str()
42                    .unwrap_or("")
43            }
44        }
45    }
46
47    /// Get the C-style name of the dynamic library.
48    #[inline]
49    pub fn cname(&self) -> *const c_char {
50        self.lib_name
51    }
52
53    /// Get the base address of the dynamic library.
54    #[inline]
55    pub fn base(&self) -> usize {
56        self.lib_base
57    }
58
59    /// Get the program headers of the dynamic library.
60    #[inline]
61    pub fn phdrs(&self) -> &[ElfPhdr] {
62        self.phdrs
63    }
64}
65
66impl ElfLibrary {
67    /// Iterate over the program headers of all dynamic libraries.
68    pub fn dl_iterate_phdr<F>(mut callback: F) -> Result<()>
69    where
70        F: FnMut(&DlPhdrInfo) -> Result<()>,
71    {
72        let reader = crate::lock_read!(MANAGER);
73        let dlpi_adds = reader.adds();
74        let dlpi_subs = reader.subs();
75        for lib in reader.all_values() {
76            let dylib = lib.dylib_ref();
77            let extra_data = dylib.user_data();
78            let phdrs = dylib.phdrs().unwrap_or(&[]);
79            if phdrs.is_empty() {
80                continue;
81            }
82            let tls_modid = dylib.tls_mod_id().unwrap_or(0);
83            let info = DlPhdrInfo {
84                lib_base: lib.dylib_ref().base(),
85                lib_name: extra_data
86                    .c_name
87                    .as_ref()
88                    .map(|n| n.as_ptr())
89                    .unwrap_or(b"\0".as_ptr() as _),
90                phdrs,
91                dlpi_adds,
92                dlpi_subs,
93                tls_modid,
94                tls_data: DefaultTlsResolver::get_tls_data(tls_modid),
95            };
96            callback(&info)?;
97        }
98        Ok(())
99    }
100}
101
102pub(crate) type CallBack =
103    unsafe extern "C" fn(info: *mut CDlPhdrInfo, size: usize, data: *mut c_void) -> c_int;
104
105/// # Safety
106/// It is the same as `dl_iterate_phdr`.
107#[unsafe(no_mangle)]
108pub unsafe extern "C" fn dl_iterate_phdr(callback: Option<CallBack>, data: *mut c_void) -> c_int {
109    let Some(callback) = callback else {
110        return 0;
111    };
112    let f = |info: &DlPhdrInfo| {
113        let mut c_info = CDlPhdrInfo {
114            dlpi_addr: info.lib_base,
115            dlpi_name: info.lib_name,
116            dlpi_phdr: info.phdrs.as_ptr(),
117            dlpi_phnum: info.phdrs.len() as _,
118            dlpi_adds: info.dlpi_adds,
119            dlpi_subs: info.dlpi_subs,
120            dlpi_tls_modid: info.tls_modid,
121            dlpi_tls_data: info
122                .tls_data
123                .map(|data| data.as_ptr() as _)
124                .unwrap_or(null_mut()),
125        };
126        unsafe {
127            let ret = callback(&mut c_info, size_of::<CDlPhdrInfo>(), data);
128            if ret != 0 {
129                return Err(Error::IteratorPhdrError { err: Box::new(ret) });
130            }
131        };
132        Ok(())
133    };
134    if let Err(err) = ElfLibrary::dl_iterate_phdr(f) {
135        if let Error::IteratorPhdrError { err } = err {
136            return *err.downcast::<i32>().unwrap();
137        }
138    }
139    0
140}