Add method to get PHDR from aux vector

This commit is contained in:
Gary Guo 2021-10-04 23:01:26 +01:00
parent b6f7d85be5
commit 5364967c0e
2 changed files with 75 additions and 41 deletions

View File

@ -19,6 +19,8 @@ gimli = { git = "https://github.com/gimli-rs/gimli.git" }
alloc = [] alloc = []
unwinder = [] unwinder = []
fde-phdr = ["libc"] fde-phdr = ["libc"]
fde-phdr-dl = ["fde-phdr"]
fde-phdr-aux = ["fde-phdr"]
fde-registry = ["alloc"] fde-registry = ["alloc"]
fde-static = [] fde-static = []
fde-gnu-eh-frame-hdr = [] fde-gnu-eh-frame-hdr = []
@ -31,7 +33,7 @@ panic = ["alloc"]
panic-handler = ["print", "panic"] panic-handler = ["print", "panic"]
panic-handler-dummy = [] panic-handler-dummy = []
system-alloc = [] 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] [profile.dev]
# Must be turned on due to Rust bug https://github.com/rust-lang/rust/issues/50007 # Must be turned on due to Rust bug https://github.com/rust-lang/rust/issues/50007

View File

@ -5,12 +5,12 @@ use core::ffi::c_void;
use core::mem; use core::mem;
use core::slice; use core::slice;
use gimli::{BaseAddresses, EhFrame, EhFrameHdr, NativeEndian, UnwindSection}; 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 { #[cfg(target_pointer_width = "32")]
pc: usize, use libc::Elf32_Phdr as Elf_Phdr;
result: Option<FDESearchResult>, #[cfg(target_pointer_width = "64")]
} use libc::Elf64_Phdr as Elf_Phdr;
pub struct PhdrFinder(()); pub struct PhdrFinder(());
@ -20,32 +20,76 @@ pub fn get_finder() -> &'static PhdrFinder {
impl super::FDEFinder for PhdrFinder { impl super::FDEFinder for PhdrFinder {
fn find_fde(&self, pc: usize) -> Option<FDESearchResult> { fn find_fde(&self, pc: usize) -> Option<FDESearchResult> {
let mut data = CallbackData { pc, result: None }; #[cfg(feature = "fde-phdr-aux")]
unsafe { dl_iterate_phdr(Some(phdr_callback), &mut data as *mut CallbackData as _) }; if let Some(v) = search_aux_phdr(pc) {
data.result 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( #[cfg(feature = "fde-phdr-aux")]
info: *mut dl_phdr_info, fn search_aux_phdr(pc: usize) -> Option<FDESearchResult> {
_size: usize, use libc::{getauxval, AT_PHDR, AT_PHNUM, PT_PHDR};
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);
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<FDESearchResult> {
use libc::{dl_iterate_phdr, dl_phdr_info};
struct CallbackData {
pc: usize,
result: Option<FDESearchResult>,
}
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<FDESearchResult> {
unsafe {
let mut text = None; let mut text = None;
let mut eh_frame_hdr = None; let mut eh_frame_hdr = None;
let mut dynamic = None; let mut dynamic = None;
for phdr in phdrs { for phdr in phdrs {
let start = (*info).dlpi_addr + phdr.p_vaddr; let start = base + phdr.p_vaddr as usize;
match phdr.p_type { match phdr.p_type {
PT_LOAD => { PT_LOAD => {
let end = start + phdr.p_memsz; let end = start + phdr.p_memsz as usize;
let range = start..end; let range = start..end;
if range.contains(&(data.pc as _)) { if range.contains(&pc) {
text = Some(range); text = Some(range);
} }
} }
@ -59,15 +103,8 @@ unsafe extern "C" fn phdr_callback(
} }
} }
let text = match text { let text = text?;
Some(v) => v, let eh_frame_hdr = eh_frame_hdr?;
None => return 0,
};
let eh_frame_hdr = match eh_frame_hdr {
Some(v) => v,
None => return 0,
};
let mut bases = BaseAddresses::default() let mut bases = BaseAddresses::default()
.set_eh_frame_hdr(eh_frame_hdr as _) .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 _), get_unlimited_slice(eh_frame_hdr as usize as _),
NativeEndian, NativeEndian,
) )
.parse(&bases, mem::size_of::<usize>() as _); .parse(&bases, mem::size_of::<usize>() as _)
let eh_frame_hdr = match eh_frame_hdr { .ok()?;
Ok(v) => v,
Err(_) => return 0,
};
let eh_frame = deref_pointer(eh_frame_hdr.eh_frame_ptr()); let eh_frame = deref_pointer(eh_frame_hdr.eh_frame_ptr());
bases = bases.set_eh_frame(eh_frame as _); 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. // Use binary search table for address if available.
if let Some(table) = eh_frame_hdr.table() { if let Some(table) = eh_frame_hdr.table() {
if let Ok(fde) = 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, fde,
bases, bases,
eh_frame, eh_frame,
}); });
return 1;
} }
} }
// Otherwise do the linear search. // Otherwise do the linear search.
if let Ok(fde) = eh_frame.fde_for_address(&bases, data.pc as _, EhFrame::cie_from_offset) { if let Ok(fde) = eh_frame.fde_for_address(&bases, pc as _, EhFrame::cie_from_offset) {
data.result = Some(FDESearchResult { return Some(FDESearchResult {
fde, fde,
bases, bases,
eh_frame, eh_frame,
}); });
return 1;
} }
0 None
} }
} }