diff options
| author | Josh Ferrell <josh@vector35.com> | 2025-10-16 12:19:57 -0400 |
|---|---|---|
| committer | Josh Ferrell <josh@vector35.com> | 2025-10-16 20:29:43 -0400 |
| commit | b6740d03abd22441e2e79e1d6a13c474e3fcba90 (patch) | |
| tree | b4b317e182f23a0b9f2f633b25c54bed814abb4b /plugins/dwarf/dwarf_import/src | |
| parent | e75d64a86a128b9a8cb3cb606e5f9560e90a4758 (diff) | |
Apply relocations when parsing DWARF
Diffstat (limited to 'plugins/dwarf/dwarf_import/src')
| -rw-r--r-- | plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs | 4 | ||||
| -rw-r--r-- | plugins/dwarf/dwarf_import/src/lib.rs | 207 |
2 files changed, 93 insertions, 118 deletions
diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index 0dc6b7fb..94f370a1 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -125,7 +125,7 @@ pub(crate) struct DebugInfoBuilderContext<R: ReaderType> { } impl<R: ReaderType> DebugInfoBuilderContext<R> { - pub(crate) fn new(view: &BinaryView, dwarf: &Dwarf<R>) -> Option<Self> { + pub(crate) fn new(default_address_size: usize, dwarf: &Dwarf<R>) -> Option<Self> { let mut units = vec![]; let mut iter = dwarf.units(); while let Ok(Some(header)) = iter.next() { @@ -154,7 +154,7 @@ impl<R: ReaderType> DebugInfoBuilderContext<R> { units, sup_units, names: HashMap::new(), - default_address_size: view.address_size(), + default_address_size, total_die_count: 0, total_unit_size_bytes: 0, }) diff --git a/plugins/dwarf/dwarf_import/src/lib.rs b/plugins/dwarf/dwarf_import/src/lib.rs index dcac4f0b..8ad5e442 100644 --- a/plugins/dwarf/dwarf_import/src/lib.rs +++ b/plugins/dwarf/dwarf_import/src/lib.rs @@ -32,9 +32,7 @@ use binaryninja::{ settings::Settings, template_simplifier::simplify_str_to_str, }; -use dwarfreader::{ - create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf, -}; +use dwarfreader::{create_section_reader_object, get_endian, is_dwo_dwarf, is_non_dwo_dwarf}; use functions::parse_lexical_block; use gimli::{ @@ -46,6 +44,7 @@ use binaryninja::logger::Logger; use helpers::{get_build_id, load_debug_info_for_build_id}; use iset::IntervalMap; use log::{debug, error, warn}; +use object::{Object, ObjectSection}; trait ReaderType: Reader<Offset = usize> {} impl<T: Reader<Offset = usize>> ReaderType for T {} @@ -388,7 +387,7 @@ fn parse_unit<R: ReaderType>( } fn parse_unwind_section<R: Reader, U: UnwindSection<R>>( - view: &BinaryView, + file: &object::File, unwind_section: U, ) -> gimli::Result<iset::IntervalMap<u64, i64>> where @@ -396,40 +395,22 @@ where { let mut bases = gimli::BaseAddresses::default(); - // DWARF info is stored relative to the original image base (0 for relocatable images), normalize entries to the original image base - let section_adjustment = view.original_image_base().wrapping_sub(view.image_base()); - - if let Some(section) = view - .section_by_name(".eh_frame_hdr") - .or(view.section_by_name("__eh_frame_hdr")) - { - bases = bases.set_eh_frame_hdr(section.start().wrapping_add(section_adjustment)); + if let Some(section) = file.section_by_name(".eh_frame_hdr") { + bases = bases.set_eh_frame_hdr(section.address()); } - if let Some(section) = view - .section_by_name(".eh_frame") - .or(view.section_by_name("__eh_frame")) - { - bases = bases.set_eh_frame(section.start().wrapping_add(section_adjustment)); - } else if let Some(section) = view - .section_by_name(".debug_frame") - .or(view.section_by_name("__debug_frame")) - { - bases = bases.set_eh_frame(section.start().wrapping_add(section_adjustment)); + if let Some(section) = file.section_by_name(".eh_frame") { + bases = bases.set_eh_frame(section.address()); + } else if let Some(section) = file.section_by_name(".debug_frame") { + bases = bases.set_eh_frame(section.address()); } - if let Some(section) = view - .section_by_name(".text") - .or(view.section_by_name("__text")) - { - bases = bases.set_text(section.start().wrapping_add(section_adjustment)); + if let Some(section) = file.section_by_name(".text") { + bases = bases.set_text(section.address()); } - if let Some(section) = view - .section_by_name(".got") - .or(view.section_by_name("__got")) - { - bases = bases.set_got(section.start().wrapping_add(section_adjustment)); + if let Some(section) = file.section_by_name(".got") { + bases = bases.set_got(section.address()); } let mut cies = HashMap::new(); @@ -535,58 +516,49 @@ fn get_supplementary_build_id(bv: &BinaryView) -> Option<String> { } } -fn parse_range_data_offsets( - bv: &BinaryView, - dwo_file: bool, -) -> Option<Result<IntervalMap<u64, i64>, ()>> { - if bv.section_by_name(".eh_frame").is_some() || bv.section_by_name("__eh_frame").is_some() { - let eh_frame_endian = get_endian(bv); - let eh_frame_section_reader = |section_id: SectionId| -> _ { - create_section_reader(section_id, bv, eh_frame_endian, dwo_file) - }; - let mut eh_frame = match gimli::EhFrame::load(eh_frame_section_reader) { - Ok(x) => x, - Err(e) => { - log::error!("Failed to load EH frame: {}", e); - return None; - } - }; - if let Some(view_arch) = bv.default_arch() { - if view_arch.name().as_str() == "aarch64" { - eh_frame.set_vendor(gimli::Vendor::AArch64); - } +fn parse_range_data_offsets(bv: &BinaryView) -> Result<IntervalMap<u64, i64>, String> { + let raw_view = bv.raw_view().unwrap(); + let raw_view_data = raw_view.read_vec(0, raw_view.len() as usize); + let file = + object::File::parse(&*raw_view_data).map_err(|e| format!("Failed to parse bv: {}", e))?; + let dwo_file = file.section_by_name(".debug_info.dwo").is_some(); + let endian = match file.endianness() { + object::Endianness::Little => gimli::RunTimeEndian::Little, + object::Endianness::Big => gimli::RunTimeEndian::Big, + }; + + let section_reader = |section_id: SectionId| -> _ { + create_section_reader_object(section_id, &file, endian, dwo_file) + }; + + if file.section_by_name(".eh_frame").is_some() { + let mut eh_frame = gimli::EhFrame::load(section_reader) + .map_err(|e| format!("Failed to load EH frame: {}", e))?; + + if file.architecture() == object::Architecture::Aarch64 { + eh_frame.set_vendor(gimli::Vendor::AArch64); } - eh_frame.set_address_size(bv.address_size() as u8); - Some( - parse_unwind_section(bv, eh_frame) - .map_err(|e| error!("Error parsing .eh_frame: {}", e)), - ) - } else if bv.section_by_name(".debug_frame").is_some() - || bv.section_by_name("__debug_frame").is_some() - { - let debug_frame_endian = get_endian(bv); - let debug_frame_section_reader = |section_id: SectionId| -> _ { - create_section_reader(section_id, bv, debug_frame_endian, dwo_file) - }; - let mut debug_frame = match gimli::DebugFrame::load(debug_frame_section_reader) { - Ok(x) => x, - Err(e) => { - log::error!("Failed to load debug frame: {}", e); - return None; - } - }; - if let Some(view_arch) = bv.default_arch() { - if view_arch.name().as_str() == "aarch64" { - debug_frame.set_vendor(gimli::Vendor::AArch64); - } + + if let Some(address_size) = file.architecture().address_size() { + eh_frame.set_address_size(address_size.bytes()); } - debug_frame.set_address_size(bv.address_size() as u8); - Some( - parse_unwind_section(bv, debug_frame) - .map_err(|e| error!("Error parsing .debug_frame: {}", e)), - ) + + parse_unwind_section(&file, eh_frame).map_err(|e| format!("Error parsing .eh_frame: {}", e)) + } else if file.section_by_name(".debug_frame").is_some() { + let mut debug_frame = gimli::DebugFrame::load(section_reader) + .map_err(|e| format!("Failed to load debug frame: {}", e))?; + + if file.architecture() == object::Architecture::Aarch64 { + debug_frame.set_vendor(gimli::Vendor::AArch64); + } + + if let Some(address_size) = file.architecture().address_size() { + debug_frame.set_address_size(address_size.bytes()); + } + parse_unwind_section(&file, debug_frame) + .map_err(|e| format!("Error parsing .debug_frame: {}", e)) } else { - None + Ok(Default::default()) } } @@ -595,35 +567,43 @@ fn parse_dwarf( debug_bv: &BinaryView, supplementary_bv: Option<&BinaryView>, progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>, -) -> Result<DebugInfoBuilder, ()> { +) -> Result<DebugInfoBuilder, String> { // TODO: warn if no supplementary file and .gnu_debugaltlink section present // 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 = &debug_bv.raw_view().ok_or(())?; - let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) { - debug_bv + let raw_view = &debug_bv + .raw_view() + .ok_or("Failed to get raw view for debug binary view".to_string())?; + + let address_size = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) { + debug_bv.address_size() } else { - raw_view + raw_view.address_size() }; - let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view); + // Parse this early to reduce peak memory usage + let range_data_offsets = parse_range_data_offsets(bv).unwrap_or_default(); - // gimli setup - let endian = get_endian(view); - let mut section_reader = - |section_id: SectionId| -> _ { create_section_reader(section_id, view, endian, dwo_file) }; + // Read the raw view to an object::File so relocations get handled for us + let raw_view_data = raw_view.read_vec(0, raw_view.len() as usize); + let debug_file = + object::File::parse(&*raw_view_data).map_err(|e| format!("Failed to parse bv: {}", e))?; + let dwo_file = debug_file.section_by_name(".debug_info.dwo").is_some(); + let endian = match debug_file.endianness() { + object::Endianness::Little => gimli::RunTimeEndian::Little, + object::Endianness::Big => gimli::RunTimeEndian::Big, + }; - let mut dwarf = match Dwarf::load(&mut section_reader) { - Ok(x) => x, - Err(e) => { - error!("Failed to load DWARF info: {}", e); - return Err(()); - } + let mut section_reader = |section_id: SectionId| -> _ { + create_section_reader_object(section_id, &debug_file, endian, dwo_file) }; + let mut dwarf = Dwarf::load(&mut section_reader) + .map_err(|e| format!("Failed to load DWARF info: {}", e))?; + if dwo_file { dwarf.file_type = DwarfFileType::Dwo; } else { @@ -631,31 +611,22 @@ fn parse_dwarf( } if let Some(sup_bv) = supplementary_bv { + let sup_raw_view = sup_bv + .raw_view() + .ok_or_else(|| format!("Failed to get raw view for supplementary bv"))?; + let sup_view_data = sup_raw_view.read_vec(0, sup_raw_view.len() as usize); + let sup_file = object::File::parse(&*sup_view_data) + .map_err(|e| format!("Failed to parse supplementary bv: {}", e))?; let sup_endian = get_endian(sup_bv); - let sup_dwo_file = is_dwo_dwarf(sup_bv) || is_raw_dwo_dwarf(sup_bv); + let sup_dwo_file = sup_file.section_by_name(".debug_info.dwo").is_some(); let sup_section_reader = |section_id: SectionId| -> _ { - create_section_reader(section_id, sup_bv, sup_endian, sup_dwo_file) + create_section_reader_object(section_id, &sup_file, sup_endian, sup_dwo_file) }; if let Err(e) = dwarf.load_sup(sup_section_reader) { error!("Failed to load supplementary file: {}", e); } } - let range_data_offsets = match parse_range_data_offsets(bv, dwo_file) { - Some(x) => x?, - None => { - if let Some(raw_view) = bv.raw_view() { - if let Some(offsets) = parse_range_data_offsets(&raw_view, dwo_file) { - offsets? - } else { - Default::default() - } - } else { - Default::default() - } - } - }; - // 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, @@ -663,7 +634,8 @@ fn parse_dwarf( 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 let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(address_size, &dwarf) + { calculate_total_unit_bytes(&dwarf, &mut debug_info_builder_context); let progress_weights = [0.5, 0.5]; @@ -770,7 +742,10 @@ impl CustomDebugInfoParser for DWARFParser { builder.post_process(bv, debug_info).commit_info(debug_info); true } - Err(_) => false, + Err(e) => { + log::error!("Failed to parse DWARF: {}", e); + false + } }; if let (Some(ext), true) = (external_file, close_external) { |
