backtrace/symbolize/gimli/
libs_dl_iterate_phdr.rs

1// Other Unix (e.g. Linux) platforms use ELF as an object file format
2// and typically implement an API called `dl_iterate_phdr` to load
3// native libraries.
4
5use super::mystd::borrow::ToOwned;
6use super::mystd::env;
7use super::mystd::ffi::{CStr, OsStr};
8use super::mystd::os::unix::prelude::*;
9use super::{Library, LibrarySegment, OsString, Vec};
10use core::slice;
11
12pub(super) fn native_libraries() -> Vec<Library> {
13    let mut ret = Vec::new();
14    unsafe {
15        libc::dl_iterate_phdr(Some(callback), core::ptr::addr_of_mut!(ret).cast());
16    }
17    return ret;
18}
19
20fn infer_current_exe(base_addr: usize) -> OsString {
21    cfg_if::cfg_if! {
22        if #[cfg(not(target_os = "hurd"))] {
23                if let Ok(entries) = super::parse_running_mmaps::parse_maps() {
24                let opt_path = entries
25                    .iter()
26                    .find(|e| e.ip_matches(base_addr) && e.pathname().len() > 0)
27                    .map(|e| e.pathname())
28                    .cloned();
29                if let Some(path) = opt_path {
30                    return path;
31                }
32            }
33        }
34    }
35    env::current_exe().map(|e| e.into()).unwrap_or_default()
36}
37
38/// # Safety
39/// `info` must be a valid pointer.
40/// `vec` must be a valid pointer to `Vec<Library>`
41#[forbid(unsafe_op_in_unsafe_fn)]
42unsafe extern "C" fn callback(
43    info: *mut libc::dl_phdr_info,
44    _size: libc::size_t,
45    vec: *mut libc::c_void,
46) -> libc::c_int {
47    // SAFETY: We are guaranteed these fields:
48    let dlpi_addr = unsafe { (*info).dlpi_addr };
49    let dlpi_name = unsafe { (*info).dlpi_name };
50    let dlpi_phdr = unsafe { (*info).dlpi_phdr };
51    let dlpi_phnum = unsafe { (*info).dlpi_phnum };
52    // SAFETY: We assured this.
53    let libs = unsafe { &mut *vec.cast::<Vec<Library>>() };
54    // most implementations give us the main program first
55    let is_main = libs.is_empty();
56    // we may be statically linked, which means we are main and mostly one big blob of code
57    let is_static = dlpi_addr == 0;
58    // sometimes we get a null or 0-len CStr, based on libc's whims, but these mean the same thing
59    let no_given_name = dlpi_name.is_null()
60        // SAFETY: we just checked for null
61        || unsafe { *dlpi_name == 0 };
62    let name = if is_static {
63        // don't try to look up our name from /proc/self/maps, it'll get silly
64        env::current_exe().unwrap_or_default().into_os_string()
65    } else if is_main && no_given_name {
66        infer_current_exe(dlpi_addr as usize)
67    } else {
68        // this fallback works even if we are main, because some platforms give the name anyways
69        if dlpi_name.is_null() {
70            OsString::new()
71        } else {
72            // SAFETY: we just checked for nullness
73            OsStr::from_bytes(unsafe { CStr::from_ptr(dlpi_name) }.to_bytes()).to_owned()
74        }
75    };
76    let headers = if dlpi_phdr.is_null() || dlpi_phnum == 0 {
77        &[]
78    } else {
79        // SAFETY: We just checked for nullness or 0-len slices
80        unsafe { slice::from_raw_parts(dlpi_phdr, dlpi_phnum as usize) }
81    };
82    libs.push(Library {
83        name,
84        segments: headers
85            .iter()
86            .map(|header| LibrarySegment {
87                len: (*header).p_memsz as usize,
88                stated_virtual_memory_address: (*header).p_vaddr as usize,
89            })
90            .collect(),
91        bias: dlpi_addr as usize,
92    });
93    0
94}