use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use fallible_iterator::FallibleIterator;
use gimli::{EndianSlice, LittleEndian};
use crate::arch::Arch;
use crate::cache::{AllocationPolicy, Cache};
use crate::dwarf::{DwarfCfiIndex, DwarfUnwinder, DwarfUnwinding, UnwindSectionType};
use crate::error::{Error, UnwinderError};
use crate::instruction_analysis::InstructionAnalysis;
#[cfg(feature = "macho")]
use crate::macho::{
CompactUnwindInfoUnwinder, CompactUnwindInfoUnwinding, CuiUnwindResult, TextBytes,
};
#[cfg(feature = "pe")]
use crate::pe::{DataAtRvaRange, PeUnwinding};
use crate::rule_cache::CacheResult;
use crate::unwind_result::UnwindResult;
use crate::unwind_rule::UnwindRule;
use crate::FrameAddress;
use core::marker::PhantomData;
use core::ops::{Deref, Range};
use core::sync::atomic::{AtomicU16, Ordering};
pub trait Unwinder: Clone {
type UnwindRegs;
type Cache;
type Module;
fn add_module(&mut self, module: Self::Module);
fn remove_module(&mut self, module_avma_range_start: u64);
fn max_known_code_address(&self) -> u64;
fn unwind_frame<F>(
&self,
address: FrameAddress,
regs: &mut Self::UnwindRegs,
cache: &mut Self::Cache,
read_stack: &mut F,
) -> Result<Option<u64>, Error>
where
F: FnMut(u64) -> Result<u64, ()>;
fn iter_frames<'u, 'c, 'r, F>(
&'u self,
pc: u64,
regs: Self::UnwindRegs,
cache: &'c mut Self::Cache,
read_stack: &'r mut F,
) -> UnwindIterator<'u, 'c, 'r, Self, F>
where
F: FnMut(u64) -> Result<u64, ()>,
{
UnwindIterator::new(self, pc, regs, cache, read_stack)
}
}
pub struct UnwindIterator<'u, 'c, 'r, U: Unwinder, F: FnMut(u64) -> Result<u64, ()>> {
unwinder: &'u U,
state: UnwindIteratorState,
regs: U::UnwindRegs,
cache: &'c mut U::Cache,
read_stack: &'r mut F,
}
enum UnwindIteratorState {
Initial(u64),
Unwinding(FrameAddress),
Done,
}
impl<'u, 'c, 'r, U: Unwinder, F: FnMut(u64) -> Result<u64, ()>> UnwindIterator<'u, 'c, 'r, U, F> {
pub fn new(
unwinder: &'u U,
pc: u64,
regs: U::UnwindRegs,
cache: &'c mut U::Cache,
read_stack: &'r mut F,
) -> Self {
Self {
unwinder,
state: UnwindIteratorState::Initial(pc),
regs,
cache,
read_stack,
}
}
}
impl<U: Unwinder, F: FnMut(u64) -> Result<u64, ()>> UnwindIterator<'_, '_, '_, U, F> {
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<FrameAddress>, Error> {
let next = match self.state {
UnwindIteratorState::Initial(pc) => {
self.state = UnwindIteratorState::Unwinding(FrameAddress::InstructionPointer(pc));
return Ok(Some(FrameAddress::InstructionPointer(pc)));
}
UnwindIteratorState::Unwinding(address) => {
self.unwinder
.unwind_frame(address, &mut self.regs, self.cache, self.read_stack)?
}
UnwindIteratorState::Done => return Ok(None),
};
match next {
Some(return_address) => {
let return_address = FrameAddress::from_return_address(return_address)
.ok_or(Error::ReturnAddressIsNull)?;
self.state = UnwindIteratorState::Unwinding(return_address);
Ok(Some(return_address))
}
None => {
self.state = UnwindIteratorState::Done;
Ok(None)
}
}
}
}
impl<U: Unwinder, F: FnMut(u64) -> Result<u64, ()>> FallibleIterator
for UnwindIterator<'_, '_, '_, U, F>
{
type Item = FrameAddress;
type Error = Error;
fn next(&mut self) -> Result<Option<FrameAddress>, Error> {
self.next()
}
}
static GLOBAL_MODULES_GENERATION: AtomicU16 = AtomicU16::new(0);
fn next_global_modules_generation() -> u16 {
GLOBAL_MODULES_GENERATION.fetch_add(1, Ordering::Relaxed)
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "macho", feature = "pe"))] {
pub trait Unwinding:
Arch + DwarfUnwinding + InstructionAnalysis + CompactUnwindInfoUnwinding + PeUnwinding {}
impl<T: Arch + DwarfUnwinding + InstructionAnalysis + CompactUnwindInfoUnwinding + PeUnwinding>
Unwinding for T {}
} else if #[cfg(feature = "macho")] {
pub trait Unwinding:
Arch + DwarfUnwinding + InstructionAnalysis + CompactUnwindInfoUnwinding {}
impl<T: Arch + DwarfUnwinding + InstructionAnalysis + CompactUnwindInfoUnwinding> Unwinding for T {}
} else if #[cfg(feature = "pe")] {
pub trait Unwinding:
Arch + DwarfUnwinding + InstructionAnalysis + PeUnwinding {}
impl<T: Arch + DwarfUnwinding + InstructionAnalysis + PeUnwinding> Unwinding for T {}
} else {
pub trait Unwinding: Arch + DwarfUnwinding + InstructionAnalysis {}
impl<T: Arch + DwarfUnwinding + InstructionAnalysis> Unwinding for T {}
}
}
pub struct UnwinderInternal<D, A, P> {
modules: Vec<Module<D>>,
modules_generation: u16,
_arch: PhantomData<A>,
_allocation_policy: PhantomData<P>,
}
impl<D, A, P> Default for UnwinderInternal<D, A, P> {
fn default() -> Self {
Self::new()
}
}
impl<D, A, P> Clone for UnwinderInternal<D, A, P> {
fn clone(&self) -> Self {
Self {
modules: self.modules.clone(),
modules_generation: self.modules_generation,
_arch: PhantomData,
_allocation_policy: PhantomData,
}
}
}
impl<D, A, P> UnwinderInternal<D, A, P> {
pub fn new() -> Self {
Self {
modules: Vec::new(),
modules_generation: next_global_modules_generation(),
_arch: PhantomData,
_allocation_policy: PhantomData,
}
}
}
impl<D: Deref<Target = [u8]>, A: Unwinding, P: AllocationPolicy> UnwinderInternal<D, A, P> {
pub fn add_module(&mut self, module: Module<D>) {
let insertion_index = match self
.modules
.binary_search_by_key(&module.avma_range.start, |module| module.avma_range.start)
{
Ok(i) => i, Err(i) => i,
};
self.modules.insert(insertion_index, module);
self.modules_generation = next_global_modules_generation();
}
pub fn remove_module(&mut self, module_address_range_start: u64) {
if let Ok(index) = self
.modules
.binary_search_by_key(&module_address_range_start, |module| {
module.avma_range.start
})
{
self.modules.remove(index);
self.modules_generation = next_global_modules_generation();
};
}
pub fn max_known_code_address(&self) -> u64 {
self.modules.last().map_or(0, |m| m.avma_range.end)
}
fn find_module_for_address(&self, address: u64) -> Option<(usize, u32)> {
let (module_index, module) = match self
.modules
.binary_search_by_key(&address, |m| m.avma_range.start)
{
Ok(i) => (i, &self.modules[i]),
Err(insertion_index) => {
if insertion_index == 0 {
return None;
}
let i = insertion_index - 1;
let module = &self.modules[i];
if module.avma_range.end <= address {
return None;
}
(i, module)
}
};
if address < module.base_avma {
return None;
}
let relative_address = u32::try_from(address - module.base_avma).ok()?;
Some((module_index, relative_address))
}
fn with_cache<F, G>(
&self,
address: FrameAddress,
regs: &mut A::UnwindRegs,
cache: &mut Cache<A::UnwindRule, P>,
read_stack: &mut F,
callback: G,
) -> Result<Option<u64>, Error>
where
F: FnMut(u64) -> Result<u64, ()>,
G: FnOnce(
&Module<D>,
FrameAddress,
u32,
&mut A::UnwindRegs,
&mut Cache<A::UnwindRule, P>,
&mut F,
) -> Result<UnwindResult<A::UnwindRule>, UnwinderError>,
{
let lookup_address = address.address_for_lookup();
let is_first_frame = !address.is_return_address();
let cache_handle = match cache
.rule_cache
.lookup(lookup_address, self.modules_generation)
{
CacheResult::Hit(unwind_rule) => {
return unwind_rule.exec(is_first_frame, regs, read_stack);
}
CacheResult::Miss(handle) => handle,
};
let unwind_rule = match self.find_module_for_address(lookup_address) {
None => A::UnwindRule::fallback_rule(),
Some((module_index, relative_lookup_address)) => {
let module = &self.modules[module_index];
match callback(
module,
address,
relative_lookup_address,
regs,
cache,
read_stack,
) {
Ok(UnwindResult::ExecRule(rule)) => rule,
Ok(UnwindResult::Uncacheable(return_address)) => {
return Ok(Some(return_address))
}
Err(_err) => {
A::UnwindRule::fallback_rule()
}
}
}
};
cache.rule_cache.insert(cache_handle, unwind_rule);
unwind_rule.exec(is_first_frame, regs, read_stack)
}
pub fn unwind_frame<F>(
&self,
address: FrameAddress,
regs: &mut A::UnwindRegs,
cache: &mut Cache<A::UnwindRule, P>,
read_stack: &mut F,
) -> Result<Option<u64>, Error>
where
F: FnMut(u64) -> Result<u64, ()>,
{
self.with_cache(address, regs, cache, read_stack, Self::unwind_frame_impl)
}
fn unwind_frame_impl<F>(
module: &Module<D>,
address: FrameAddress,
rel_lookup_address: u32,
regs: &mut A::UnwindRegs,
cache: &mut Cache<A::UnwindRule, P>,
read_stack: &mut F,
) -> Result<UnwindResult<A::UnwindRule>, UnwinderError>
where
F: FnMut(u64) -> Result<u64, ()>,
{
let is_first_frame = !address.is_return_address();
let unwind_result = match &*module.unwind_data {
#[cfg(feature = "macho")]
ModuleUnwindDataInternal::CompactUnwindInfoAndEhFrame {
unwind_info,
eh_frame,
stubs_svma: stubs,
stub_helper_svma: stub_helper,
base_addresses,
text_data,
} => {
let text_bytes = text_data.as_ref().and_then(|data| {
let offset_from_base =
u32::try_from(data.svma_range.start.checked_sub(module.base_svma)?).ok()?;
Some(TextBytes::new(offset_from_base, &data.bytes[..]))
});
let stubs_range = if let Some(stubs_range) = stubs {
(
(stubs_range.start - module.base_svma) as u32,
(stubs_range.end - module.base_svma) as u32,
)
} else {
(0, 0)
};
let stub_helper_range = if let Some(stub_helper_range) = stub_helper {
(
(stub_helper_range.start - module.base_svma) as u32,
(stub_helper_range.end - module.base_svma) as u32,
)
} else {
(0, 0)
};
let mut unwinder = CompactUnwindInfoUnwinder::<A>::new(
&unwind_info[..],
text_bytes,
stubs_range,
stub_helper_range,
);
let unwind_result = unwinder.unwind_frame(rel_lookup_address, is_first_frame)?;
match unwind_result {
CuiUnwindResult::ExecRule(rule) => UnwindResult::ExecRule(rule),
CuiUnwindResult::NeedDwarf(fde_offset) => {
let eh_frame_data =
eh_frame.as_deref().ok_or(UnwinderError::NoDwarfData)?;
let mut dwarf_unwinder = DwarfUnwinder::<_, A, _>::new(
EndianSlice::new(eh_frame_data, LittleEndian),
UnwindSectionType::EhFrame,
None,
&mut cache.gimli_unwind_context,
base_addresses.clone(),
module.base_svma,
);
dwarf_unwinder.unwind_frame_with_fde::<_, P::GimliEvaluationStorage<_>>(
regs,
is_first_frame,
rel_lookup_address,
fde_offset,
read_stack,
)?
}
}
}
ModuleUnwindDataInternal::EhFrameHdrAndEhFrame {
eh_frame_hdr,
eh_frame,
base_addresses,
} => {
let eh_frame_hdr_data = &eh_frame_hdr[..];
let mut dwarf_unwinder = DwarfUnwinder::<_, A, _>::new(
EndianSlice::new(eh_frame, LittleEndian),
UnwindSectionType::EhFrame,
Some(eh_frame_hdr_data),
&mut cache.gimli_unwind_context,
base_addresses.clone(),
module.base_svma,
);
let fde_offset = dwarf_unwinder
.get_fde_offset_for_relative_address(rel_lookup_address)
.ok_or(UnwinderError::EhFrameHdrCouldNotFindAddress)?;
dwarf_unwinder.unwind_frame_with_fde::<_, P::GimliEvaluationStorage<_>>(
regs,
is_first_frame,
rel_lookup_address,
fde_offset,
read_stack,
)?
}
ModuleUnwindDataInternal::DwarfCfiIndexAndEhFrame {
index,
eh_frame,
base_addresses,
} => {
let mut dwarf_unwinder = DwarfUnwinder::<_, A, _>::new(
EndianSlice::new(eh_frame, LittleEndian),
UnwindSectionType::EhFrame,
None,
&mut cache.gimli_unwind_context,
base_addresses.clone(),
module.base_svma,
);
let fde_offset = index
.fde_offset_for_relative_address(rel_lookup_address)
.ok_or(UnwinderError::DwarfCfiIndexCouldNotFindAddress)?;
dwarf_unwinder.unwind_frame_with_fde::<_, P::GimliEvaluationStorage<_>>(
regs,
is_first_frame,
rel_lookup_address,
fde_offset,
read_stack,
)?
}
ModuleUnwindDataInternal::DwarfCfiIndexAndDebugFrame {
index,
debug_frame,
base_addresses,
} => {
let mut dwarf_unwinder = DwarfUnwinder::<_, A, _>::new(
EndianSlice::new(debug_frame, LittleEndian),
UnwindSectionType::DebugFrame,
None,
&mut cache.gimli_unwind_context,
base_addresses.clone(),
module.base_svma,
);
let fde_offset = index
.fde_offset_for_relative_address(rel_lookup_address)
.ok_or(UnwinderError::DwarfCfiIndexCouldNotFindAddress)?;
dwarf_unwinder.unwind_frame_with_fde::<_, P::GimliEvaluationStorage<_>>(
regs,
is_first_frame,
rel_lookup_address,
fde_offset,
read_stack,
)?
}
#[cfg(feature = "pe")]
ModuleUnwindDataInternal::PeUnwindInfo {
pdata,
rdata,
xdata,
text,
} => <A as PeUnwinding>::unwind_frame(
crate::pe::PeSections {
pdata,
rdata: rdata.as_ref(),
xdata: xdata.as_ref(),
text: text.as_ref(),
},
rel_lookup_address,
regs,
is_first_frame,
read_stack,
)?,
ModuleUnwindDataInternal::None => return Err(UnwinderError::NoModuleUnwindData),
};
Ok(unwind_result)
}
}
enum ModuleUnwindDataInternal<D> {
#[cfg(feature = "macho")]
CompactUnwindInfoAndEhFrame {
unwind_info: D,
eh_frame: Option<D>,
stubs_svma: Option<Range<u64>>,
stub_helper_svma: Option<Range<u64>>,
base_addresses: crate::dwarf::BaseAddresses,
text_data: Option<TextByteData<D>>,
},
EhFrameHdrAndEhFrame {
eh_frame_hdr: D,
eh_frame: D,
base_addresses: crate::dwarf::BaseAddresses,
},
DwarfCfiIndexAndEhFrame {
index: DwarfCfiIndex,
eh_frame: D,
base_addresses: crate::dwarf::BaseAddresses,
},
DwarfCfiIndexAndDebugFrame {
index: DwarfCfiIndex,
debug_frame: D,
base_addresses: crate::dwarf::BaseAddresses,
},
#[cfg(feature = "pe")]
PeUnwindInfo {
pdata: D,
rdata: Option<DataAtRvaRange<D>>,
xdata: Option<DataAtRvaRange<D>>,
text: Option<DataAtRvaRange<D>>,
},
None,
}
impl<D: Deref<Target = [u8]>> ModuleUnwindDataInternal<D> {
fn new(section_info: &mut impl ModuleSectionInfo<D>) -> Self {
use crate::dwarf::base_addresses_for_sections;
#[cfg(feature = "macho")]
if let Some(unwind_info) = section_info.section_data(b"__unwind_info") {
let eh_frame = section_info.section_data(b"__eh_frame");
let stubs = section_info.section_svma_range(b"__stubs");
let stub_helper = section_info.section_svma_range(b"__stub_helper");
let text_data = if let (Some(bytes), Some(svma_range)) = (
section_info.segment_data(b"__TEXT"),
section_info.segment_svma_range(b"__TEXT"),
) {
Some(TextByteData { bytes, svma_range })
} else if let (Some(bytes), Some(svma_range)) = (
section_info.section_data(b"__text"),
section_info.section_svma_range(b"__text"),
) {
Some(TextByteData { bytes, svma_range })
} else {
None
};
return ModuleUnwindDataInternal::CompactUnwindInfoAndEhFrame {
unwind_info,
eh_frame,
stubs_svma: stubs,
stub_helper_svma: stub_helper,
base_addresses: base_addresses_for_sections(section_info),
text_data,
};
}
#[cfg(feature = "pe")]
if let Some(pdata) = section_info.section_data(b".pdata") {
let mut range_and_data = |name| {
let rva_range = section_info.section_svma_range(name).and_then(|range| {
Some(Range {
start: (range.start - section_info.base_svma()).try_into().ok()?,
end: (range.end - section_info.base_svma()).try_into().ok()?,
})
})?;
let data = section_info.section_data(name)?;
Some(DataAtRvaRange { data, rva_range })
};
return ModuleUnwindDataInternal::PeUnwindInfo {
pdata,
rdata: range_and_data(b".rdata"),
xdata: range_and_data(b".xdata"),
text: range_and_data(b".text"),
};
}
if let Some(eh_frame) = section_info
.section_data(b".eh_frame")
.or_else(|| section_info.section_data(b"__eh_frame"))
{
if let Some(eh_frame_hdr) = section_info
.section_data(b".eh_frame_hdr")
.or_else(|| section_info.section_data(b"__eh_frame_hdr"))
{
ModuleUnwindDataInternal::EhFrameHdrAndEhFrame {
eh_frame_hdr,
eh_frame,
base_addresses: base_addresses_for_sections(section_info),
}
} else {
match DwarfCfiIndex::try_new_eh_frame(&eh_frame, section_info) {
Ok(index) => ModuleUnwindDataInternal::DwarfCfiIndexAndEhFrame {
index,
eh_frame,
base_addresses: base_addresses_for_sections(section_info),
},
Err(_) => ModuleUnwindDataInternal::None,
}
}
} else if let Some(debug_frame) = section_info.section_data(b".debug_frame") {
match DwarfCfiIndex::try_new_debug_frame(&debug_frame, section_info) {
Ok(index) => ModuleUnwindDataInternal::DwarfCfiIndexAndDebugFrame {
index,
debug_frame,
base_addresses: base_addresses_for_sections(section_info),
},
Err(_) => ModuleUnwindDataInternal::None,
}
} else {
ModuleUnwindDataInternal::None
}
}
}
#[cfg(feature = "macho")]
struct TextByteData<D> {
pub bytes: D,
pub svma_range: Range<u64>,
}
pub struct Module<D> {
#[allow(unused)]
name: String,
avma_range: Range<u64>,
base_avma: u64,
base_svma: u64,
unwind_data: Arc<ModuleUnwindDataInternal<D>>,
}
impl<D> Clone for Module<D> {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
avma_range: self.avma_range.clone(),
base_avma: self.base_avma,
base_svma: self.base_svma,
unwind_data: self.unwind_data.clone(),
}
}
}
pub trait ModuleSectionInfo<D> {
fn base_svma(&self) -> u64;
fn section_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>>;
fn section_data(&mut self, name: &[u8]) -> Option<D>;
fn segment_svma_range(&mut self, _name: &[u8]) -> Option<Range<u64>> {
None
}
fn segment_data(&mut self, _name: &[u8]) -> Option<D> {
None
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ExplicitModuleSectionInfo<D> {
pub base_svma: u64,
pub text_svma: Option<Range<u64>>,
pub text: Option<D>,
pub stubs_svma: Option<Range<u64>>,
pub stub_helper_svma: Option<Range<u64>>,
pub got_svma: Option<Range<u64>>,
pub unwind_info: Option<D>,
pub eh_frame_svma: Option<Range<u64>>,
pub eh_frame: Option<D>,
pub eh_frame_hdr_svma: Option<Range<u64>>,
pub eh_frame_hdr: Option<D>,
pub debug_frame: Option<D>,
pub text_segment_svma: Option<Range<u64>>,
pub text_segment: Option<D>,
}
impl<D> ModuleSectionInfo<D> for ExplicitModuleSectionInfo<D>
where
D: Deref<Target = [u8]>,
{
fn base_svma(&self) -> u64 {
self.base_svma
}
fn section_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>> {
match name {
b"__text" | b".text" => self.text_svma.clone(),
b"__stubs" => self.stubs_svma.clone(),
b"__stub_helper" => self.stub_helper_svma.clone(),
b"__eh_frame" | b".eh_frame" => self.eh_frame_svma.clone(),
b"__eh_frame_hdr" | b".eh_frame_hdr" => self.eh_frame_hdr_svma.clone(),
b"__got" | b".got" => self.got_svma.clone(),
_ => None,
}
}
fn section_data(&mut self, name: &[u8]) -> Option<D> {
match name {
b"__text" | b".text" => self.text.take(),
b"__unwind_info" => self.unwind_info.take(),
b"__eh_frame" | b".eh_frame" => self.eh_frame.take(),
b"__eh_frame_hdr" | b".eh_frame_hdr" => self.eh_frame_hdr.take(),
b"__debug_frame" | b".debug_frame" => self.debug_frame.take(),
_ => None,
}
}
fn segment_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>> {
match name {
b"__TEXT" => self.text_segment_svma.clone(),
_ => None,
}
}
fn segment_data(&mut self, name: &[u8]) -> Option<D> {
match name {
b"__TEXT" => self.text_segment.take(),
_ => None,
}
}
}
impl<D: Deref<Target = [u8]>> Module<D> {
pub fn new(
name: String,
avma_range: core::ops::Range<u64>,
base_avma: u64,
mut section_info: impl ModuleSectionInfo<D>,
) -> Self {
let unwind_data = ModuleUnwindDataInternal::new(&mut section_info);
Self {
name,
avma_range,
base_avma,
base_svma: section_info.base_svma(),
unwind_data: Arc::new(unwind_data),
}
}
pub fn avma_range(&self) -> core::ops::Range<u64> {
self.avma_range.clone()
}
pub fn base_avma(&self) -> u64 {
self.base_avma
}
pub fn name(&self) -> &str {
&self.name
}
}