dlopen_rs/
dladdr.rs

1use crate::{register::MANAGER, Dylib, ElfLibrary};
2use core::{
3    ffi::{c_char, c_int, c_void, CStr},
4    fmt::Debug,
5    ptr::null,
6};
7
8#[repr(C)]
9pub struct CDlinfo {
10    pub dli_fname: *const c_char,
11    pub dli_fbase: *mut c_void,
12    pub dli_sname: *const c_char,
13    pub dli_saddr: *mut c_void,
14}
15
16pub struct DlInfo {
17    /// dylib
18    dylib: Dylib,
19    /// Name of symbol whose definition overlaps addr
20    sname: Option<&'static CStr>,
21    /// Exact address of symbol named in dli_sname
22    saddr: usize,
23}
24
25impl DlInfo {
26    #[inline]
27    pub fn dylib(&self) -> &Dylib {
28        &self.dylib
29    }
30
31    /// Name of symbol whose definition overlaps addr
32    #[inline]
33    pub fn symbol_name(&self) -> Option<&str> {
34        self.sname.map(|s| s.to_str().unwrap())
35    }
36
37    /// Exact address of symbol
38    #[inline]
39    pub fn symbol_addr(&self) -> Option<usize> {
40        if self.saddr == 0 {
41            None
42        } else {
43            Some(self.saddr)
44        }
45    }
46}
47
48impl Debug for DlInfo {
49    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50        f.debug_struct("DlInfo")
51            .field("dylib", &self.dylib)
52            .field("sname", &self.sname)
53            .field("saddr", &format_args!("{:#x}", self.saddr))
54            .finish()
55    }
56}
57
58impl ElfLibrary {
59    fn addr2dso(addr: usize) -> Option<Dylib> {
60        MANAGER.read().all.values().find_map(|v| {
61            let start = v.relocated_dylib_ref().base();
62            let end = start + v.relocated_dylib_ref().map_len();
63            if (start..end).contains(&addr) {
64                Some(v.get_dylib())
65            } else {
66                None
67            }
68        })
69    }
70
71    /// determines whether the address specified in addr is located in one of the shared objects loaded by the calling
72    /// application.  If it is, then `dladdr` returns information about the shared object and
73    /// symbol that overlaps addr.
74    pub fn dladdr(addr: usize) -> Option<DlInfo> {
75        log::info!(
76            "dladdr: Try to find the symbol information corresponding to [{:#x}]",
77            addr
78        );
79        Self::addr2dso(addr).map(|dylib| {
80            let mut dl_info = DlInfo {
81                dylib,
82                sname: None,
83                saddr: 0,
84            };
85            let symtab = dl_info.dylib.inner.symtab();
86            for i in 0..symtab.count_syms() {
87                let (sym, syminfo) = symtab.symbol_idx(i);
88                let start = dl_info.dylib.base() + sym.st_value();
89                let end = start + sym.st_size();
90                if sym.st_value() != 0
91                    && sym.is_ok_bind()
92                    && sym.is_ok_type()
93                    && (start..end).contains(&addr)
94                {
95                    dl_info.sname = Some(unsafe { core::mem::transmute(syminfo.cname().unwrap()) });
96                    dl_info.saddr = start;
97                }
98            }
99            dl_info
100        })
101    }
102}
103
104/// It is the same as `dladdr`.
105pub unsafe extern "C" fn dladdr(addr: *const c_void, info: *mut CDlinfo) -> c_int {
106    if let Some(dl_info) = ElfLibrary::dladdr(addr as usize) {
107        let info = &mut *info;
108        info.dli_fbase = dl_info.dylib().base() as _;
109        info.dli_fname = dl_info.dylib().cname().as_ptr();
110        info.dli_saddr = dl_info.symbol_addr().unwrap_or(0) as _;
111        info.dli_sname = dl_info.sname.map_or(null(), |s| s.as_ptr());
112        1
113    } else {
114        0
115    }
116}