#![allow(clippy::cast_possible_truncation)]
pub mod bpf_relocs;
pub mod call_sites;
mod eh_frame;
mod entry;
mod function_map;
mod plt;
mod signatures;
mod symbols;
pub use call_sites::{CallSiteError, discover_from_bpf_call_sites};
pub use eh_frame::{EhFrameError, discover_from_eh_frame};
pub use function_map::{Function, FunctionMap, FunctionSource};
pub use plt::{PltError, discover_plt_thunks};
pub use signatures::discover_from_signatures;
pub use symbols::{SymbolError, discover_from_symbol_tables};
use crate::common::VAddr;
use crate::format::elf::Elf64File;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Symbol(#[from] SymbolError),
#[error(transparent)]
EhFrame(#[from] EhFrameError),
#[error(transparent)]
Plt(#[from] PltError),
#[error(transparent)]
BpfReloc(#[from] bpf_relocs::BpfRelocError),
#[error(transparent)]
CallSite(#[from] CallSiteError),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub fn discover_functions(elf: &Elf64File) -> Result<FunctionMap> {
let mut map = FunctionMap::new();
for f in entry::discover_entry_point(elf) {
map.insert(f);
}
for f in discover_from_eh_frame(elf)? {
map.insert(f);
}
let bpf_syscalls = bpf_relocs::build_call_site_names(elf)?;
for f in discover_from_bpf_call_sites(elf, &bpf_syscalls)? {
map.insert(f);
}
for f in discover_from_signatures(elf) {
map.insert(f);
}
for f in discover_plt_thunks(elf)? {
map.insert(f);
}
for f in discover_from_symbol_tables(elf)? {
map.insert(f);
}
fill_in_sizes_from_neighbors(&mut map, elf);
Ok(map)
}
fn fill_in_sizes_from_neighbors(map: &mut FunctionMap, elf: &Elf64File) {
use crate::format::elf::SHF_EXECINSTR;
let func_addrs: Vec<u64> = map.iter().map(|f| f.addr.0).collect();
let exec_sections: Vec<(u64, u64)> = elf
.sections()
.filter(|(_, sh, _)| sh.sh_flags & SHF_EXECINSTR != 0 && sh.sh_size > 0)
.map(|(_, sh, _)| (sh.sh_addr, sh.sh_addr.saturating_add(sh.sh_size)))
.collect();
let zero_sized: Vec<u64> = map
.iter()
.filter(|f| f.size == 0)
.map(|f| f.addr.0)
.collect();
for addr in zero_sized {
let Some(&(_, sec_end)) = exec_sections.iter().find(|(s, e)| addr >= *s && addr < *e)
else {
continue;
};
let next_in_section = func_addrs
.iter()
.copied()
.filter(|&a| a > addr && a < sec_end)
.min()
.unwrap_or(sec_end);
let new_size = next_in_section.saturating_sub(addr);
map.insert(Function {
addr: VAddr(addr),
size: new_size,
name: String::new(), sources: Vec::new(),
});
}
}