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 = Vec::new();
14 unsafe {
15 libc::dl_iterate_phdr(callback:Some(callback), data: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: PathBuf| e.into()).unwrap_or_default()
36}
37
38// `info` should be a valid pointers.
39// `vec` should be a valid pointer to a `std::Vec`.
40unsafe extern "C" fn callback(
41 info: *mut libc::dl_phdr_info,
42 _size: libc::size_t,
43 vec: *mut libc::c_void,
44) -> libc::c_int {
45 let info = &*info;
46 let libs = &mut *vec.cast::<Vec<Library>>();
47 let is_main_prog = info.dlpi_name.is_null() || *info.dlpi_name == 0;
48 let name = if is_main_prog {
49 // The man page for dl_iterate_phdr says that the first object visited by
50 // callback is the main program; so the first time we encounter a
51 // nameless entry, we can assume its the main program and try to infer its path.
52 // After that, we cannot continue that assumption, and we use an empty string.
53 if libs.is_empty() {
54 infer_current_exe(info.dlpi_addr as usize)
55 } else {
56 OsString::new()
57 }
58 } else {
59 let bytes = CStr::from_ptr(info.dlpi_name).to_bytes();
60 OsStr::from_bytes(bytes).to_owned()
61 };
62 let headers = slice::from_raw_parts(info.dlpi_phdr, info.dlpi_phnum as usize);
63 libs.push(Library {
64 name,
65 segments: headers
66 .iter()
67 .map(|header| LibrarySegment {
68 len: (*header).p_memsz as usize,
69 stated_virtual_memory_address: (*header).p_vaddr as usize,
70 })
71 .collect(),
72 bias: info.dlpi_addr as usize,
73 });
74 0
75}
76