diff --git a/Cargo.toml b/Cargo.toml index 372a92a..7e1dd15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ gimli = { git = "https://github.com/gimli-rs/gimli.git" } alloc = [] unwinder = [] fde-phdr = ["libc"] +fde-phdr-dl = ["fde-phdr"] +fde-phdr-aux = ["fde-phdr"] fde-registry = ["alloc"] fde-static = [] fde-gnu-eh-frame-hdr = [] @@ -31,7 +33,7 @@ panic = ["alloc"] panic-handler = ["print", "panic"] panic-handler-dummy = [] system-alloc = [] -default = ["unwinder", "dwarf-expr", "hide-trace", "fde-phdr", "fde-registry"] +default = ["unwinder", "dwarf-expr", "hide-trace", "fde-phdr-dl", "fde-registry"] [profile.dev] # Must be turned on due to Rust bug https://github.com/rust-lang/rust/issues/50007 diff --git a/src/unwinder/find_fde/phdr.rs b/src/unwinder/find_fde/phdr.rs index e61ee78..25f18de 100644 --- a/src/unwinder/find_fde/phdr.rs +++ b/src/unwinder/find_fde/phdr.rs @@ -5,12 +5,12 @@ use core::ffi::c_void; use core::mem; use core::slice; use gimli::{BaseAddresses, EhFrame, EhFrameHdr, NativeEndian, UnwindSection}; -use libc::{dl_iterate_phdr, dl_phdr_info, PT_DYNAMIC, PT_GNU_EH_FRAME, PT_LOAD}; +use libc::{PT_DYNAMIC, PT_GNU_EH_FRAME, PT_LOAD}; -struct CallbackData { - pc: usize, - result: Option, -} +#[cfg(target_pointer_width = "32")] +use libc::Elf32_Phdr as Elf_Phdr; +#[cfg(target_pointer_width = "64")] +use libc::Elf64_Phdr as Elf_Phdr; pub struct PhdrFinder(()); @@ -20,32 +20,76 @@ pub fn get_finder() -> &'static PhdrFinder { impl super::FDEFinder for PhdrFinder { fn find_fde(&self, pc: usize) -> Option { - let mut data = CallbackData { pc, result: None }; - unsafe { dl_iterate_phdr(Some(phdr_callback), &mut data as *mut CallbackData as _) }; - data.result + #[cfg(feature = "fde-phdr-aux")] + if let Some(v) = search_aux_phdr(pc) { + return Some(v); + } + #[cfg(feature = "fde-phdr-dl")] + if let Some(v) = search_dl_phdr(pc) { + return Some(v); + } + None } } -unsafe extern "C" fn phdr_callback( - info: *mut dl_phdr_info, - _size: usize, - data: *mut c_void, -) -> c_int { - unsafe { - let data = &mut *(data as *mut CallbackData); - let phdrs = slice::from_raw_parts((*info).dlpi_phdr, (*info).dlpi_phnum as usize); +#[cfg(feature = "fde-phdr-aux")] +fn search_aux_phdr(pc: usize) -> Option { + use libc::{getauxval, AT_PHDR, AT_PHNUM, PT_PHDR}; + unsafe { + let phdr = getauxval(AT_PHDR) as *const Elf_Phdr; + let phnum = getauxval(AT_PHNUM) as usize; + let phdrs = slice::from_raw_parts(phdr, phnum); + // With known address of PHDR, we can calculate the base address in reverse. + let base = + phdrs.as_ptr() as usize - phdrs.iter().find(|x| x.p_type == PT_PHDR)?.p_vaddr as usize; + search_phdr(phdrs, base, pc) + } +} + +#[cfg(feature = "fde-phdr-dl")] +fn search_dl_phdr(pc: usize) -> Option { + use libc::{dl_iterate_phdr, dl_phdr_info}; + + struct CallbackData { + pc: usize, + result: Option, + } + + unsafe extern "C" fn phdr_callback( + info: *mut dl_phdr_info, + _size: usize, + data: *mut c_void, + ) -> c_int { + unsafe { + let data = &mut *(data as *mut CallbackData); + let phdrs = slice::from_raw_parts((*info).dlpi_phdr, (*info).dlpi_phnum as usize); + if let Some(v) = search_phdr(phdrs, (*info).dlpi_addr as _, data.pc) { + data.result = Some(v); + return 1; + } + 0 + } + } + + let mut data = CallbackData { pc, result: None }; + unsafe { dl_iterate_phdr(Some(phdr_callback), &mut data as *mut CallbackData as _) }; + data.result +} + +fn search_phdr(phdrs: &[Elf_Phdr], base: usize, pc: usize) -> Option { + unsafe { let mut text = None; let mut eh_frame_hdr = None; let mut dynamic = None; for phdr in phdrs { - let start = (*info).dlpi_addr + phdr.p_vaddr; + let start = base + phdr.p_vaddr as usize; match phdr.p_type { PT_LOAD => { - let end = start + phdr.p_memsz; + let end = start + phdr.p_memsz as usize; let range = start..end; - if range.contains(&(data.pc as _)) { + if range.contains(&pc) { text = Some(range); } } @@ -59,15 +103,8 @@ unsafe extern "C" fn phdr_callback( } } - let text = match text { - Some(v) => v, - None => return 0, - }; - - let eh_frame_hdr = match eh_frame_hdr { - Some(v) => v, - None => return 0, - }; + let text = text?; + let eh_frame_hdr = eh_frame_hdr?; let mut bases = BaseAddresses::default() .set_eh_frame_hdr(eh_frame_hdr as _) @@ -95,11 +132,8 @@ unsafe extern "C" fn phdr_callback( get_unlimited_slice(eh_frame_hdr as usize as _), NativeEndian, ) - .parse(&bases, mem::size_of::() as _); - let eh_frame_hdr = match eh_frame_hdr { - Ok(v) => v, - Err(_) => return 0, - }; + .parse(&bases, mem::size_of::() as _) + .ok()?; let eh_frame = deref_pointer(eh_frame_hdr.eh_frame_ptr()); bases = bases.set_eh_frame(eh_frame as _); @@ -108,27 +142,25 @@ unsafe extern "C" fn phdr_callback( // Use binary search table for address if available. if let Some(table) = eh_frame_hdr.table() { if let Ok(fde) = - table.fde_for_address(&eh_frame, &bases, data.pc as _, EhFrame::cie_from_offset) + table.fde_for_address(&eh_frame, &bases, pc as _, EhFrame::cie_from_offset) { - data.result = Some(FDESearchResult { + return Some(FDESearchResult { fde, bases, eh_frame, }); - return 1; } } // Otherwise do the linear search. - if let Ok(fde) = eh_frame.fde_for_address(&bases, data.pc as _, EhFrame::cie_from_offset) { - data.result = Some(FDESearchResult { + if let Ok(fde) = eh_frame.fde_for_address(&bases, pc as _, EhFrame::cie_from_offset) { + return Some(FDESearchResult { fde, bases, eh_frame, }); - return 1; } - 0 + None } }