Skip to main content

dlopen_rs/api/
dladdr.rs

1use crate::{ElfLibrary, core_impl::register::addr2dso};
2use core::{
3    ffi::{CStr, c_char, c_int, c_void},
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: ElfLibrary,
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) -> &ElfLibrary {
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.and_then(|s| s.to_str().ok())
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    /// determines whether the address specified in addr is located in one of the shared objects loaded by the calling
60    /// application.  If it is, then `dladdr` returns information about the shared object and
61    /// symbol that overlaps addr.
62    pub fn dladdr(addr: usize) -> Option<DlInfo> {
63        log::info!(
64            "dladdr: Try to find the symbol information corresponding to [{:#x}]",
65            addr
66        );
67        addr2dso(addr).map(|dylib| {
68            let mut dl_info = DlInfo {
69                dylib,
70                sname: None,
71                saddr: 0,
72            };
73            let symtab = dl_info.dylib.inner.symtab();
74            let mut best_match: Option<(usize, &CStr)> = None;
75            for i in 0..symtab.count_syms() {
76                let (sym, syminfo) = symtab.symbol_idx(i);
77                if sym.st_value() == 0 || !sym.is_ok_bind() || !sym.is_ok_type() {
78                    continue;
79                }
80                let start = dl_info.dylib.base() + sym.st_value();
81                let end = start + sym.st_size();
82                if start <= addr && (sym.st_size() == 0 || addr < end) {
83                    if let Some((best_start, _)) = best_match {
84                        if start > best_start {
85                            if let Some(cname) = syminfo.cname() {
86                                best_match = Some((start, cname));
87                            }
88                        }
89                    } else {
90                        if let Some(cname) = syminfo.cname() {
91                            best_match = Some((start, cname));
92                        }
93                    }
94                }
95            }
96            if let Some((start, cname)) = best_match {
97                dl_info.sname = Some(unsafe { core::mem::transmute(cname) });
98                dl_info.saddr = start;
99            }
100            dl_info
101        })
102    }
103}
104
105/// # Safety
106/// It is the same as `dladdr`.
107#[unsafe(no_mangle)]
108pub unsafe extern "C" fn dladdr(addr: *const c_void, info: *mut CDlinfo) -> c_int {
109    if let Some(dl_info) = ElfLibrary::dladdr(addr as usize) {
110        let info = unsafe { &mut *info };
111        info.dli_fbase = dl_info.dylib().base() as _;
112        info.dli_fname = dl_info.dylib().cname();
113        info.dli_saddr = dl_info.symbol_addr().unwrap_or(0) as _;
114        info.dli_sname = dl_info.sname.map_or(null(), |s| s.as_ptr());
115        1
116    } else {
117        0
118    }
119}