| 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 | |
| 5 | use super::mystd::borrow::ToOwned; |
| 6 | use super::mystd::env; |
| 7 | use super::mystd::ffi::{CStr, OsStr}; |
| 8 | use super::mystd::os::unix::prelude::*; |
| 9 | use super::{Library, LibrarySegment, OsString, Vec}; |
| 10 | use core::slice; |
| 11 | |
| 12 | pub(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 | |
| 20 | fn 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`. |
| 40 | unsafe 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 | |