dlopen_rs/
dl_iterate_phdr.rs

1use crate::{register::MANAGER, ElfLibrary, Error, Result};
2use alloc::boxed::Box;
3use core::{
4    ffi::{c_char, c_int, c_ulonglong, c_void, CStr},
5    ptr::null_mut,
6};
7use elf_loader::arch::ElfPhdr;
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: &'lib CStr,
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        self.lib_name.to_str().unwrap()
37    }
38
39    /// Get the C-style name of the dynamic library.
40    #[inline]
41    pub fn cname(&self) -> &CStr {
42        self.lib_name
43    }
44
45    /// Get the base address of the dynamic library.
46    #[inline]
47    pub fn base(&self) -> usize {
48        self.lib_base
49    }
50
51    /// Get the program headers of the dynamic library.
52    #[inline]
53    pub fn phdrs(&self) -> &[ElfPhdr] {
54        self.phdrs
55    }
56}
57
58impl ElfLibrary {
59    /// Iterate over the program headers of all dynamic libraries.
60    pub fn dl_iterate_phdr<F>(mut callback: F) -> Result<()>
61    where
62        F: FnMut(&DlPhdrInfo) -> Result<()>,
63    {
64        let reader = MANAGER.read();
65        for lib in reader.all.values() {
66            let phdrs = lib.relocated_dylib_ref().phdrs();
67            if phdrs.is_empty() {
68                continue;
69            }
70            let info = DlPhdrInfo {
71                lib_base: lib.relocated_dylib_ref().base(),
72                lib_name: lib.relocated_dylib_ref().cname(),
73                phdrs,
74                dlpi_adds: reader.all.len() as _,
75                dlpi_subs: 0,
76                tls_modid: 0,
77                tls_data: None,
78            };
79            callback(&info)?;
80        }
81        Ok(())
82    }
83}
84
85pub(crate) type CallBack =
86    unsafe extern "C" fn(info: *mut CDlPhdrInfo, size: usize, data: *mut c_void) -> c_int;
87
88// It is the same as `dl_iterate_phdr`.
89pub extern "C" fn dl_iterate_phdr(callback: Option<CallBack>, data: *mut c_void) -> c_int {
90    let f = |info: &DlPhdrInfo| {
91        if let Some(callback) = callback {
92            let mut c_info = CDlPhdrInfo {
93                dlpi_addr: info.lib_base,
94                dlpi_name: info.lib_name.as_ptr(),
95                dlpi_phdr: info.phdrs.as_ptr(),
96                dlpi_phnum: info.phdrs.len() as _,
97                dlpi_adds: info.dlpi_adds,
98                dlpi_subs: info.dlpi_subs,
99                dlpi_tls_modid: info.tls_modid,
100                dlpi_tls_data: info
101                    .tls_data
102                    .map(|data| data.as_ptr() as _)
103                    .unwrap_or(null_mut()),
104            };
105            unsafe {
106                let ret = callback(&mut c_info, size_of::<CDlPhdrInfo>(), data);
107                if ret != 0 {
108                    return Err(Error::IteratorPhdrError { err: Box::new(ret) });
109                }
110            };
111        }
112        Ok(())
113    };
114    if let Err(Error::IteratorPhdrError { err }) = ElfLibrary::dl_iterate_phdr(f) {
115        *err.downcast::<i32>().unwrap()
116    } else {
117        0
118    }
119}