From c40a114e23f182e6463b70a215c120431bd4cc43 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 2 Oct 2021 10:13:04 -0700 Subject: [PATCH] Add a way to find `.eh_frame_hdr` using `__GNU_EH_FRAME_HDR`. GNU ld on at least some platforms adds a symbol `__GNU_EH_FRAME_HDR` for the `.eh_frame_hdr` section. Add a cargo feature `fde-gnu-eh-frame-hdr` to enable use of this. --- Cargo.toml | 1 + src/unwinder/find_fde/gnu_eh_frame_hdr.rs | 66 +++++++++++++++++++++++ src/unwinder/find_fde/mod.rs | 6 +++ 3 files changed, 73 insertions(+) create mode 100644 src/unwinder/find_fde/gnu_eh_frame_hdr.rs diff --git a/Cargo.toml b/Cargo.toml index c690cad..c18ce62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ unwinder = [] fde-phdr = ["libc"] fde-registry = ["alloc"] fde-static = [] +fde-gnu-eh-frame-hdr = [] dwarf-expr = [] hide-trace = [] personality = [] diff --git a/src/unwinder/find_fde/gnu_eh_frame_hdr.rs b/src/unwinder/find_fde/gnu_eh_frame_hdr.rs new file mode 100644 index 0000000..0750769 --- /dev/null +++ b/src/unwinder/find_fde/gnu_eh_frame_hdr.rs @@ -0,0 +1,66 @@ +use super::FDESearchResult; +use crate::util::*; + +use gimli::{BaseAddresses, EhFrame, EhFrameHdr, NativeEndian, UnwindSection}; + +pub struct StaticFinder(()); + +pub fn get_finder() -> &'static StaticFinder { + &StaticFinder(()) +} + +extern "C" { + static __executable_start: u8; + static __etext: u8; + static __GNU_EH_FRAME_HDR: u8; +} + +impl super::FDEFinder for StaticFinder { + fn find_fde(&self, pc: usize) -> Option { + unsafe { + let text_start = &__executable_start as *const u8 as usize; + let text_end = &__etext as *const u8 as usize; + if !(text_start..text_end).contains(&pc) { + return None; + } + + let eh_frame_hdr = &__GNU_EH_FRAME_HDR as *const u8 as usize; + let bases = BaseAddresses::default() + .set_text(text_start as _) + .set_eh_frame_hdr(eh_frame_hdr as _); + let eh_frame_hdr = EhFrameHdr::new( + get_unlimited_slice(eh_frame_hdr as usize as _), + NativeEndian, + ) + .parse(&bases, core::mem::size_of::() as _) + .ok()?; + let eh_frame = deref_pointer(eh_frame_hdr.eh_frame_ptr()); + let bases = bases.set_eh_frame(eh_frame as _); + let eh_frame = EhFrame::new(get_unlimited_slice(eh_frame as _), NativeEndian); + + // 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, pc as _, EhFrame::cie_from_offset) + { + return Some(FDESearchResult { + fde, + bases, + eh_frame, + }); + } + } + + // Otherwise do the linear search. + if let Ok(fde) = eh_frame.fde_for_address(&bases, pc as _, EhFrame::cie_from_offset) { + return Some(FDESearchResult { + fde, + bases, + eh_frame, + }); + } + + None + } + } +} diff --git a/src/unwinder/find_fde/mod.rs b/src/unwinder/find_fde/mod.rs index 714c9df..54df17c 100644 --- a/src/unwinder/find_fde/mod.rs +++ b/src/unwinder/find_fde/mod.rs @@ -1,5 +1,7 @@ #[cfg(feature = "fde-static")] mod fixed; +#[cfg(feature = "fde-gnu-eh-frame-hdr")] +mod gnu_eh_frame_hdr; #[cfg(feature = "fde-phdr")] mod phdr; #[cfg(feature = "fde-registry")] @@ -35,6 +37,10 @@ impl FDEFinder for GlobalFinder { if let Some(v) = fixed::get_finder().find_fde(pc) { return Some(v); } + #[cfg(feature = "fde-gnu-eh-frame-hdr")] + if let Some(v) = gnu_eh_frame_hdr::get_finder().find_fde(pc) { + return Some(v); + } None } }