summaryrefslogtreecommitdiff
path: root/rust/examples/dwarf/dwarf_import/src/lib.rs
diff options
context:
space:
mode:
authorJosh Ferrell <josh@vector35.com>2024-06-13 14:13:11 -0400
committerJosh Ferrell <josh@vector35.com>2024-06-13 14:13:11 -0400
commitd75066545afdc9b5d0a52bd2518e43f683c671a5 (patch)
treebed26d348a726fc02c1d44cbdc36a8056b44baaf /rust/examples/dwarf/dwarf_import/src/lib.rs
parentf3a440248cda4d0c850cd32e3aa127ab29e0bb3e (diff)
Apply stack variables from DWARF
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/lib.rs')
-rw-r--r--rust/examples/dwarf/dwarf_import/src/lib.rs104
1 files changed, 95 insertions, 9 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/lib.rs b/rust/examples/dwarf/dwarf_import/src/lib.rs
index 71367bfc..62b8c18a 100644
--- a/rust/examples/dwarf/dwarf_import/src/lib.rs
+++ b/rust/examples/dwarf/dwarf_import/src/lib.rs
@@ -18,11 +18,14 @@ mod functions;
mod helpers;
mod types;
+use std::collections::HashMap;
+
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext};
use crate::functions::parse_function_entry;
use crate::helpers::{get_attr_die, get_name, get_uid, DieReference};
-use crate::types::parse_data_variable;
+use crate::types::parse_variable;
+use binaryninja::binaryview::BinaryViewBase;
use binaryninja::{
binaryview::{BinaryView, BinaryViewExt},
debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
@@ -34,7 +37,7 @@ use dwarfreader::{
create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf,
};
-use gimli::{constants, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, SectionId, Unit};
+use gimli::{constants, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section, SectionId, Unit, UnwindSection};
use log::{error, warn, LevelFilter};
@@ -188,9 +191,12 @@ fn parse_unit<R: Reader<Offset = usize>>(
) {
let mut entries = unit.entries();
+ let mut current_depth: isize = 0;
+ let mut functions_by_depth: Vec<(Option<usize>, isize)> = vec![];
+
// Really all we care about as we iterate the entries in a given unit is how they modify state (our perception of the file)
// There's a lot of junk we don't care about in DWARF info, so we choose a couple DIEs and mutate state (add functions (which adds the types it uses) and keep track of what namespace we're in)
- while let Ok(Some((_, entry))) = entries.next_dfs() {
+ while let Ok(Some((depth_delta, entry))) = entries.next_dfs() {
*current_die_number += 1;
if (*progress)(
*current_die_number,
@@ -201,29 +207,100 @@ fn parse_unit<R: Reader<Offset = usize>>(
return; // Parsing canceled
}
+ current_depth = current_depth.saturating_add(depth_delta);
+
+ loop {
+ if let Some((_fn_idx, depth)) = functions_by_depth.last() {
+ if current_depth <= *depth {
+ functions_by_depth.pop();
+ }
+ else {
+ break
+ }
+ }
+ else {
+ break;
+ }
+ }
+
match entry.tag() {
constants::DW_TAG_subprogram => {
- parse_function_entry(unit, entry, debug_info_builder_context, debug_info_builder)
+ let fn_idx = parse_function_entry(unit, entry, debug_info_builder_context, debug_info_builder);
+ functions_by_depth.push((fn_idx, current_depth));
}
constants::DW_TAG_variable => {
- parse_data_variable(unit, entry, debug_info_builder_context, debug_info_builder)
+ let current_fn_idx = functions_by_depth.last().and_then(|x| x.0);
+ parse_variable(unit, entry, debug_info_builder_context, debug_info_builder, current_fn_idx)
}
_ => (),
}
}
}
+fn parse_eh_frame<R: Reader>(
+ view: &BinaryView,
+ mut eh_frame: gimli::EhFrame<R>,
+) -> gimli::Result<iset::IntervalMap<u64, i64>> {
+ eh_frame.set_address_size(view.address_size() as u8);
+
+ let mut bases = gimli::BaseAddresses::default();
+ if let Ok(section) = view.section_by_name(".eh_frame_hdr").or(view.section_by_name("__eh_frame_hdr")) {
+ bases = bases.set_eh_frame_hdr(section.start());
+ }
+ if let Ok(section) = view.section_by_name(".eh_frame").or(view.section_by_name("__eh_frame")) {
+ bases = bases.set_eh_frame(section.start());
+ }
+ if let Ok(section) = view.section_by_name(".text").or(view.section_by_name("__text")) {
+ bases = bases.set_text(section.start());
+ }
+ if let Ok(section) = view.section_by_name(".got").or(view.section_by_name("__got")) {
+ bases = bases.set_got(section.start());
+ }
+
+ let mut cies = HashMap::new();
+ let mut cie_data_offsets = iset::IntervalMap::new();
+
+ let mut entries = eh_frame.entries(&bases);
+ loop {
+ match entries.next()? {
+ None => return Ok(cie_data_offsets),
+ Some(gimli::CieOrFde::Cie(_cie)) => {
+ // TODO: do we want to do anything with standalone CIEs?
+ }
+ Some(gimli::CieOrFde::Fde(partial)) => {
+ let fde = match partial.parse(|_, bases, o| {
+ cies.entry(o)
+ .or_insert_with(|| eh_frame.cie_from_offset(bases, o))
+ .clone()
+ }) {
+ Ok(fde) => fde,
+ Err(e) => {
+ error!("Failed to parse FDE: {}", e);
+ continue;
+ }
+ };
+ // Store CIE offset for FDE range
+ cie_data_offsets.insert(
+ fde.initial_address()..fde.initial_address()+fde.len(),
+ fde.cie().data_alignment_factor()
+ );
+ }
+ }
+ }
+}
+
fn parse_dwarf(
bv: &BinaryView,
+ debug_bv: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
) -> Result<DebugInfoBuilder, ()> {
// Determine if this is a DWO
// TODO : Make this more robust...some DWOs follow non-DWO conventions
// Figure out if it's the given view or the raw view that has the dwarf info in it
- let raw_view = &bv.raw_view().unwrap();
- let view = if is_dwo_dwarf(bv) || is_non_dwo_dwarf(bv) {
- bv
+ let raw_view = &debug_bv.raw_view().unwrap();
+ let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) {
+ debug_bv
} else {
raw_view
};
@@ -239,11 +316,20 @@ fn parse_dwarf(
dwarf.file_type = DwarfFileType::Dwo;
}
+ let eh_frame_endian = get_endian(bv);
+ let mut eh_frame_section_reader =
+ |section_id: SectionId| -> _ { create_section_reader(section_id, bv, eh_frame_endian, dwo_file) };
+ let eh_frame = gimli::EhFrame::load(&mut eh_frame_section_reader).unwrap();
+
+ let range_data_offsets = parse_eh_frame(bv, eh_frame)
+ .map_err(|e| println!("Error parsing .eh_frame: {}", e))?;
+
// Create debug info builder and recover name mapping first
// Since DWARF is stored as a tree with arbitrary implicit edges among leaves,
// it is not possible to correctly track namespaces while you're parsing "in order" without backtracking,
// so we just do it up front
let mut debug_info_builder = DebugInfoBuilder::new();
+ debug_info_builder.set_range_data_offsets(range_data_offsets);
if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, dwarf) {
if !recover_names(&mut debug_info_builder_context, &progress)
|| debug_info_builder_context.total_die_count == 0
@@ -308,7 +394,7 @@ impl CustomDebugInfoParser for DWARFParser {
None
};
- match parse_dwarf(external_file.as_deref().unwrap_or(debug_file), progress) {
+ match parse_dwarf(bv, external_file.as_deref().unwrap_or(debug_file), progress) {
Ok(mut builder) => {
builder
.post_process(bv, debug_info)