use crate::common::VAddr;
use crate::format::elf::Elf64File;
use gimli::{BaseAddresses, CieOrFde, EhFrame, LittleEndian, UnwindSection};
use crate::analysis::function_map::{Function, FunctionSource};
#[derive(Debug, thiserror::Error)]
pub enum EhFrameError {
#[error("gimli rejected the .eh_frame section: {0}")]
Gimli(gimli::Error),
}
pub fn discover_from_eh_frame(elf: &Elf64File) -> Result<Vec<Function>, EhFrameError> {
let Some((_, sh, data)) = elf.section_by_name(".eh_frame") else {
return Ok(Vec::new());
};
if data.is_empty() {
return Ok(Vec::new());
}
let eh_frame = EhFrame::new(data, LittleEndian);
let mut bases = BaseAddresses::default().set_eh_frame(sh.sh_addr);
if let Some((_, text_sh, _)) = elf.section_by_name(".text") {
bases = bases.set_text(text_sh.sh_addr);
}
let mut entries = eh_frame.entries(&bases);
let mut out = Vec::new();
while let Some(entry) = entries.next().map_err(EhFrameError::Gimli)? {
let CieOrFde::Fde(partial) = entry else {
continue;
};
let fde = partial
.parse(EhFrame::cie_from_offset)
.map_err(EhFrameError::Gimli)?;
let start = fde.initial_address();
let len = fde.len();
out.push(Function {
addr: VAddr(start),
size: len,
name: format!("sub_{start:x}"),
sources: vec![FunctionSource::EhFrame],
});
}
Ok(out)
}