Add fde-custom feature
This commit is contained in:
parent
2f3ffefe8e
commit
02b5c86f89
@ -24,6 +24,7 @@ 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 = []
|
||||||
|
fde-custom = []
|
||||||
dwarf-expr = []
|
dwarf-expr = []
|
||||||
hide-trace = []
|
hide-trace = []
|
||||||
personality = []
|
personality = []
|
||||||
|
@ -23,6 +23,7 @@ The unwinder can be enabled with `unwinder` feature. Here are the feature gates
|
|||||||
| fde-registry | Yes | Provide `__register__frame` and others for dynamic registration. Requires either `libc` or `spin` for a mutex implementation. |
|
| fde-registry | Yes | Provide `__register__frame` and others for dynamic registration. Requires either `libc` or `spin` for a mutex implementation. |
|
||||||
| fde-gnu-eh-frame-hdr | No | Use `__executable_start`, `__etext` and `__GNU_EH_FRAME_HDR` to retrieve frame unwind table. The former two symbols are usually provided by the linker, while the last one is provided if GNU LD is used and --eh-frame-hdr option is enabled. |
|
| fde-gnu-eh-frame-hdr | No | Use `__executable_start`, `__etext` and `__GNU_EH_FRAME_HDR` to retrieve frame unwind table. The former two symbols are usually provided by the linker, while the last one is provided if GNU LD is used and --eh-frame-hdr option is enabled. |
|
||||||
| fde-static | No | Use `__executable_start`, `__etext` and `__eh_frame` to retrieve frame unwind table. The former two symbols are usually provided by the linker, while the last one would need to be provided by the user via linker script. |
|
| fde-static | No | Use `__executable_start`, `__etext` and `__eh_frame` to retrieve frame unwind table. The former two symbols are usually provided by the linker, while the last one would need to be provided by the user via linker script. |
|
||||||
|
| fde-custom | No | Allow the program to provide a custom means of retrieving frame unwind table at runtime via the `set_custom_eh_frame_finder` function. |
|
||||||
| dwarf-expr | Yes | Enable the dwarf expression evaluator. Usually not necessary for Rust |
|
| dwarf-expr | Yes | Enable the dwarf expression evaluator. Usually not necessary for Rust |
|
||||||
| hide-trace | Yes | Hide unwinder frames in back trace |
|
| hide-trace | Yes | Hide unwinder frames in back trace |
|
||||||
|
|
||||||
|
@ -20,6 +20,9 @@ extern crate alloc;
|
|||||||
#[cfg(feature = "unwinder")]
|
#[cfg(feature = "unwinder")]
|
||||||
mod unwinder;
|
mod unwinder;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "unwinder", feature = "fde-custom"))]
|
||||||
|
pub use unwinder::custom_eh_frame_finder;
|
||||||
|
|
||||||
pub mod abi;
|
pub mod abi;
|
||||||
|
|
||||||
mod arch;
|
mod arch;
|
||||||
|
165
src/unwinder/find_fde/custom.rs
Normal file
165
src/unwinder/find_fde/custom.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
use super::{FDEFinder, FDESearchResult};
|
||||||
|
use crate::util::{deref_pointer, get_unlimited_slice};
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
use gimli::{BaseAddresses, EhFrame, EhFrameHdr, NativeEndian, UnwindSection};
|
||||||
|
|
||||||
|
pub(crate) struct CustomFinder(());
|
||||||
|
|
||||||
|
pub(crate) fn get_finder() -> &'static CustomFinder {
|
||||||
|
&CustomFinder(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FDEFinder for CustomFinder {
|
||||||
|
fn find_fde(&self, pc: usize) -> Option<FDESearchResult> {
|
||||||
|
get_custom_eh_frame_finder().and_then(|eh_frame_finder| find_fde(eh_frame_finder, pc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for types whose values can be used as the global EH frame finder set by [`set_custom_eh_frame_finder`].
|
||||||
|
pub unsafe trait EhFrameFinder {
|
||||||
|
fn find(&self, pc: usize) -> Option<FrameInfo>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FrameInfo {
|
||||||
|
pub text_base: usize,
|
||||||
|
pub kind: FrameInfoKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FrameInfoKind {
|
||||||
|
EhFrameHdr(usize),
|
||||||
|
EhFrame(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut CUSTOM_EH_FRAME_FINDER: Option<&(dyn EhFrameFinder + Sync)> = None;
|
||||||
|
|
||||||
|
static CUSTOM_EH_FRAME_FINDER_STATE: AtomicU32 = AtomicU32::new(UNINITIALIZED);
|
||||||
|
|
||||||
|
const UNINITIALIZED: u32 = 0;
|
||||||
|
const INITIALIZING: u32 = 1;
|
||||||
|
const INITIALIZED: u32 = 2;
|
||||||
|
|
||||||
|
/// The type returned by [`set_custom_eh_frame_finder`] if [`set_custom_eh_frame_finder`] has
|
||||||
|
/// already been called.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SetCustomEhFrameFinderError(());
|
||||||
|
|
||||||
|
/// Sets the global EH frame finder.
|
||||||
|
///
|
||||||
|
/// This function should only be called once during the lifetime of the program.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// An error is returned if this function has already been called during the lifetime of the
|
||||||
|
/// program.
|
||||||
|
pub fn set_custom_eh_frame_finder(
|
||||||
|
fde_finder: &'static (dyn EhFrameFinder + Sync),
|
||||||
|
) -> Result<(), SetCustomEhFrameFinderError> {
|
||||||
|
match CUSTOM_EH_FRAME_FINDER_STATE.compare_exchange(
|
||||||
|
UNINITIALIZED,
|
||||||
|
INITIALIZING,
|
||||||
|
Ordering::SeqCst,
|
||||||
|
Ordering::SeqCst,
|
||||||
|
) {
|
||||||
|
Ok(UNINITIALIZED) => {
|
||||||
|
unsafe {
|
||||||
|
CUSTOM_EH_FRAME_FINDER = Some(fde_finder);
|
||||||
|
}
|
||||||
|
CUSTOM_EH_FRAME_FINDER_STATE.store(INITIALIZED, Ordering::SeqCst);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(INITIALIZING) => {
|
||||||
|
while CUSTOM_EH_FRAME_FINDER_STATE.load(Ordering::SeqCst) == INITIALIZING {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
Err(SetCustomEhFrameFinderError(()))
|
||||||
|
}
|
||||||
|
Err(INITIALIZED) => Err(SetCustomEhFrameFinderError(())),
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_custom_eh_frame_finder() -> Option<&'static dyn EhFrameFinder> {
|
||||||
|
if CUSTOM_EH_FRAME_FINDER_STATE.load(Ordering::SeqCst) == INITIALIZED {
|
||||||
|
Some(unsafe { CUSTOM_EH_FRAME_FINDER.unwrap() })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_fde<T: EhFrameFinder + ?Sized>(eh_frame_finder: &T, pc: usize) -> Option<FDESearchResult> {
|
||||||
|
let info = eh_frame_finder.find(pc)?;
|
||||||
|
let text_base = info.text_base;
|
||||||
|
match info.kind {
|
||||||
|
FrameInfoKind::EhFrameHdr(eh_frame_hdr) => {
|
||||||
|
find_fde_with_eh_frame_hdr(pc, text_base, eh_frame_hdr)
|
||||||
|
}
|
||||||
|
FrameInfoKind::EhFrame(eh_frame) => find_fde_with_eh_frame(pc, text_base, eh_frame),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_fde_with_eh_frame_hdr(
|
||||||
|
pc: usize,
|
||||||
|
text_base: usize,
|
||||||
|
eh_frame_hdr: usize,
|
||||||
|
) -> Option<FDESearchResult> {
|
||||||
|
unsafe {
|
||||||
|
let bases = BaseAddresses::default()
|
||||||
|
.set_text(text_base 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_fde_with_eh_frame(pc: usize, text_base: usize, eh_frame: usize) -> Option<FDESearchResult> {
|
||||||
|
unsafe {
|
||||||
|
let bases = BaseAddresses::default()
|
||||||
|
.set_text(text_base as _)
|
||||||
|
.set_eh_frame(eh_frame as _);
|
||||||
|
let eh_frame = EhFrame::new(get_unlimited_slice(eh_frame as _), NativeEndian);
|
||||||
|
|
||||||
|
if let Ok(fde) = eh_frame.fde_for_address(&bases, pc as _, EhFrame::cie_from_offset) {
|
||||||
|
return Some(FDESearchResult {
|
||||||
|
fde,
|
||||||
|
bases,
|
||||||
|
eh_frame,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
#[cfg(feature = "fde-custom")]
|
||||||
|
mod custom;
|
||||||
#[cfg(feature = "fde-static")]
|
#[cfg(feature = "fde-static")]
|
||||||
mod fixed;
|
mod fixed;
|
||||||
#[cfg(feature = "fde-gnu-eh-frame-hdr")]
|
#[cfg(feature = "fde-gnu-eh-frame-hdr")]
|
||||||
@ -10,6 +12,14 @@ mod registry;
|
|||||||
use crate::util::*;
|
use crate::util::*;
|
||||||
use gimli::{BaseAddresses, EhFrame, FrameDescriptionEntry};
|
use gimli::{BaseAddresses, EhFrame, FrameDescriptionEntry};
|
||||||
|
|
||||||
|
#[cfg(feature = "fde-custom")]
|
||||||
|
pub mod custom_eh_frame_finder {
|
||||||
|
pub use super::custom::{
|
||||||
|
set_custom_eh_frame_finder, EhFrameFinder, FrameInfo, FrameInfoKind,
|
||||||
|
SetCustomEhFrameFinderError,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FDESearchResult {
|
pub struct FDESearchResult {
|
||||||
pub fde: FrameDescriptionEntry<StaticSlice>,
|
pub fde: FrameDescriptionEntry<StaticSlice>,
|
||||||
@ -25,6 +35,10 @@ pub struct GlobalFinder(());
|
|||||||
|
|
||||||
impl FDEFinder for GlobalFinder {
|
impl FDEFinder for GlobalFinder {
|
||||||
fn find_fde(&self, pc: usize) -> Option<FDESearchResult> {
|
fn find_fde(&self, pc: usize) -> Option<FDESearchResult> {
|
||||||
|
#[cfg(feature = "fde-custom")]
|
||||||
|
if let Some(v) = custom::get_finder().find_fde(pc) {
|
||||||
|
return Some(v);
|
||||||
|
}
|
||||||
#[cfg(feature = "fde-registry")]
|
#[cfg(feature = "fde-registry")]
|
||||||
if let Some(v) = registry::get_finder().find_fde(pc) {
|
if let Some(v) = registry::get_finder().find_fde(pc) {
|
||||||
return Some(v);
|
return Some(v);
|
||||||
|
@ -13,6 +13,9 @@ use arch::*;
|
|||||||
use find_fde::FDEFinder;
|
use find_fde::FDEFinder;
|
||||||
use frame::Frame;
|
use frame::Frame;
|
||||||
|
|
||||||
|
#[cfg(feature = "fde-custom")]
|
||||||
|
pub use find_fde::custom_eh_frame_finder;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct UnwindException {
|
pub struct UnwindException {
|
||||||
pub exception_class: u64,
|
pub exception_class: u64,
|
||||||
|
Loading…
Reference in New Issue
Block a user