diff options
| author | Josh Ferrell <josh@vector35.com> | 2024-06-13 14:13:11 -0400 |
|---|---|---|
| committer | Josh Ferrell <josh@vector35.com> | 2024-06-13 14:13:11 -0400 |
| commit | d75066545afdc9b5d0a52bd2518e43f683c671a5 (patch) | |
| tree | bed26d348a726fc02c1d44cbdc36a8056b44baaf /rust/examples/dwarf/dwarf_import/src/lib.rs | |
| parent | f3a440248cda4d0c850cd32e3aa127ab29e0bb3e (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.rs | 104 |
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) |
