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.
45use super::mystd::env;
6use super::mystd::ffi::{OsStr, OsString};
7use super::mystd::os::unix::prelude::*;
8use super::{parse_running_mmaps, Library, LibrarySegment};
9use alloc::borrow::ToOwned;
10use alloc::vec::Vec;
11use core::ffi::CStr;
12use core::slice;
1314struct CallbackData {
15 libs: Vec<Library>,
16 maps: Option<Vec<parse_running_mmaps::MapsEntry>>,
17}
18pub(super) fn native_libraries() -> Vec<Library> {
19let mut cb_data = CallbackData {
20 libs: Vec::new(),
21#[cfg(not(target_os = "hurd"))]
22maps: parse_running_mmaps::parse_maps().ok(),
23#[cfg(target_os = "hurd")]
24maps: None,
25 };
26unsafe {
27 libc::dl_iterate_phdr(Some(callback), core::ptr::addr_of_mut!(cb_data).cast());
28 }
29 cb_data.libs
30}
3132fn infer_current_exe(
33 maps: &Option<Vec<parse_running_mmaps::MapsEntry>>,
34 base_addr: usize,
35) -> OsString {
36#[cfg(not(target_os = "hurd"))]
37if let Some(entries) = maps {
38let opt_path = entries
39 .iter()
40 .find(|e| e.ip_matches(base_addr) && e.pathname().len() > 0)
41 .map(|e| e.pathname())
42 .cloned();
43if let Some(path) = opt_path {
44return path;
45 }
46 }
4748 env::current_exe().map(|e| e.into()).unwrap_or_default()
49}
5051/// # Safety
52/// `info` must be a valid pointer.
53/// `data` must be a valid pointer to `CallbackData`.
54#[forbid(unsafe_op_in_unsafe_fn)]
55unsafe extern "C" fn callback(
56 info: *mut libc::dl_phdr_info,
57 _size: libc::size_t,
58 data: *mut libc::c_void,
59) -> libc::c_int {
60// SAFETY: We are guaranteed these fields:
61let dlpi_addr = unsafe { (*info).dlpi_addr };
62let dlpi_name = unsafe { (*info).dlpi_name };
63let dlpi_phdr = unsafe { (*info).dlpi_phdr };
64let dlpi_phnum = unsafe { (*info).dlpi_phnum };
65// SAFETY: We assured this.
66let CallbackData { libs, maps } = unsafe { &mut *data.cast::<CallbackData>() };
67// most implementations give us the main program first
68let is_main = libs.is_empty();
69// we may be statically linked, which means we are main and mostly one big blob of code
70let is_static = dlpi_addr == 0;
71// sometimes we get a null or 0-len CStr, based on libc's whims, but these mean the same thing
72let no_given_name = dlpi_name.is_null()
73// SAFETY: we just checked for null
74|| unsafe { *dlpi_name == 0 };
75let name = if is_static {
76// don't try to look up our name from /proc/self/maps, it'll get silly
77env::current_exe().unwrap_or_default().into_os_string()
78 } else if is_main && no_given_name {
79 infer_current_exe(&maps, dlpi_addr as usize)
80 } else {
81// this fallback works even if we are main, because some platforms give the name anyways
82if dlpi_name.is_null() {
83 OsString::new()
84 } else {
85// SAFETY: we just checked for nullness
86OsStr::from_bytes(unsafe { CStr::from_ptr(dlpi_name) }.to_bytes()).to_owned()
87 }
88 };
89#[cfg(target_os = "android")]
90let zip_offset: Option<u64> = {
91// only check for ZIP-embedded file if we have data from /proc/self/maps
92maps.as_ref().and_then(|maps| {
93// check if file is embedded within a ZIP archive by searching for `!/`
94super::extract_zip_path_android(&name).and_then(|_| {
95// find MapsEntry matching library's base address and get its file offset
96maps.iter()
97 .find(|m| m.ip_matches(dlpi_addr as usize))
98 .map(|m| m.offset())
99 })
100 })
101 };
102let headers = if dlpi_phdr.is_null() || dlpi_phnum == 0 {
103&[]
104 } else {
105// SAFETY: We just checked for nullness or 0-len slices
106unsafe { slice::from_raw_parts(dlpi_phdr, dlpi_phnum as usize) }
107 };
108 libs.push(Library {
109 name,
110#[cfg(target_os = "android")]
111zip_offset,
112 segments: headers
113 .iter()
114 .map(|header| LibrarySegment {
115 len: header.p_memsz as usize,
116 stated_virtual_memory_address: header.p_vaddr as usize,
117 })
118 .collect(),
119 bias: dlpi_addr as usize,
120 });
1210
122}