Parcourir la source

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.
Dan Gohman il y a 3 ans
Parent
commit
c40a114e23
3 fichiers modifiés avec 73 ajouts et 0 suppressions
  1. 1 0
      Cargo.toml
  2. 66 0
      src/unwinder/find_fde/gnu_eh_frame_hdr.rs
  3. 6 0
      src/unwinder/find_fde/mod.rs

+ 1 - 0
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 = []

+ 66 - 0
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<FDESearchResult> {
+        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::<usize>() 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
+        }
+    }
+}

+ 6 - 0
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
     }
 }