From 6a496b01b3e8a85c8231c949a306933e7ad58bfc Mon Sep 17 00:00:00 2001 From: Josh Ferrell Date: Thu, 18 Jul 2024 14:05:23 -0400 Subject: Support loading supplementary DWARF files --- rust/examples/dwarf/dwarf_import/src/helpers.rs | 183 +++++++++++++++--------- 1 file changed, 112 insertions(+), 71 deletions(-) (limited to 'rust/examples/dwarf/dwarf_import/src/helpers.rs') diff --git a/rust/examples/dwarf/dwarf_import/src/helpers.rs b/rust/examples/dwarf/dwarf_import/src/helpers.rs index 92b08bce..7127b2b8 100644 --- a/rust/examples/dwarf/dwarf_import/src/helpers.rs +++ b/rust/examples/dwarf/dwarf_import/src/helpers.rs @@ -20,127 +20,171 @@ use std::{ str::FromStr }; -use crate::DebugInfoBuilderContext; +use crate::{DebugInfoBuilderContext, ReaderType}; use binaryninja::binaryview::BinaryViewBase; use binaryninja::filemetadata::FileMetadata; use binaryninja::Endianness; use binaryninja::{binaryview::{BinaryView, BinaryViewExt}, downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider}, rc::Ref, settings::Settings}; +use gimli::Dwarf; use gimli::{ constants, Attribute, AttributeValue, - AttributeValue::{DebugInfoRef, UnitRef}, - DebuggingInformationEntry, Operation, Reader, Unit, UnitOffset, UnitSectionOffset, + AttributeValue::{DebugInfoRef, DebugInfoRefSup, UnitRef}, + DebuggingInformationEntry, Operation, Unit, UnitOffset, UnitSectionOffset, }; use log::warn; -pub(crate) fn get_uid>( +pub(crate) fn get_uid( + dwarf: &Dwarf, unit: &Unit, entry: &DebuggingInformationEntry, ) -> usize { - match entry.offset().to_unit_section_offset(unit) { + // We set a large gap between supplementary and main entries + let adj = dwarf.sup().map_or(0, |_| 0x1000000000000000); + let entry_offset = match entry.offset().to_unit_section_offset(unit) { UnitSectionOffset::DebugInfoOffset(o) => o.0, UnitSectionOffset::DebugTypesOffset(o) => o.0, - } + }; + entry_offset + adj } //////////////////////////////////// // DIE attr convenience functions -pub(crate) enum DieReference<'a, R: Reader> { - UnitAndOffset((&'a Unit, UnitOffset)), +pub(crate) enum DieReference<'a, R: ReaderType> { + UnitAndOffset((&'a Dwarf, &'a Unit, UnitOffset)), Err, } -pub(crate) fn get_attr_die<'a, R: Reader>( +pub(crate) fn get_attr_die<'a, R: ReaderType>( + dwarf: &'a Dwarf, unit: &'a Unit, entry: &DebuggingInformationEntry, debug_info_builder_context: &'a DebugInfoBuilderContext, attr: constants::DwAt, ) -> Option> { match entry.attr_value(attr) { - Ok(Some(UnitRef(offset))) => Some(DieReference::UnitAndOffset((unit, offset))), + Ok(Some(UnitRef(offset))) => Some(DieReference::UnitAndOffset((dwarf, unit, offset))), Ok(Some(DebugInfoRef(offset))) => { - for source_unit in debug_info_builder_context.units() { + if dwarf.sup().is_some() { + for source_unit in debug_info_builder_context.units() { + if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { + return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset))); + } + } + } + else { + // This could either have no supplementary file because it is one or because it just doesn't have one + // operate on supplementary file if dwarf is a supplementary file, else self + + // It's possible this is a reference in the supplementary file to itself + for source_unit in debug_info_builder_context.sup_units() { + if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { + return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset))); + } + } + + // ... or it just doesn't have a supplementary file + for source_unit in debug_info_builder_context.units() { + if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { + return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset))); + } + } + } + + None + }, + Ok(Some(DebugInfoRefSup(offset))) => { + for source_unit in debug_info_builder_context.sup_units() { if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { - return Some(DieReference::UnitAndOffset((source_unit, new_offset))); + return Some(DieReference::UnitAndOffset((dwarf.sup().unwrap(), source_unit, new_offset))); } } - warn!("Failed to fetch DIE. Debug information may be incomplete."); + warn!("Failed to fetch DIE. Supplementary debug information may be incomplete."); None - } - // Ok(Some(DebugInfoRefSup(offset))) TODO - dwarf 5 stuff + }, _ => None, } } -pub(crate) fn resolve_specification<'a, R: Reader>( +pub(crate) fn resolve_specification<'a, R: ReaderType>( + dwarf: &'a Dwarf, unit: &'a Unit, entry: &DebuggingInformationEntry, debug_info_builder_context: &'a DebugInfoBuilderContext, ) -> DieReference<'a, R> { if let Some(die_reference) = get_attr_die( + dwarf, unit, entry, debug_info_builder_context, constants::DW_AT_specification, ) { match die_reference { - DieReference::UnitAndOffset((entry_unit, entry_offset)) => { + DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { if let Ok(entry) = entry_unit.entry(entry_offset) { - resolve_specification(entry_unit, &entry, debug_info_builder_context) + resolve_specification(dwarf, entry_unit, &entry, debug_info_builder_context) } else { - warn!("Failed to fetch DIE. Debug information may be incomplete."); + warn!("Failed to fetch DIE for attr DW_AT_specification. Debug information may be incomplete."); DieReference::Err } } DieReference::Err => DieReference::Err, } } else if let Some(die_reference) = get_attr_die( + dwarf, unit, entry, debug_info_builder_context, constants::DW_AT_abstract_origin, ) { match die_reference { - DieReference::UnitAndOffset((entry_unit, entry_offset)) => { - if entry_offset == entry.offset() { + DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { + if entry_offset == entry.offset() && unit.header.offset() == entry_unit.header.offset() { warn!("DWARF information is invalid (infinite abstract origin reference cycle). Debug information may be incomplete."); DieReference::Err } else if let Ok(new_entry) = entry_unit.entry(entry_offset) { - resolve_specification(entry_unit, &new_entry, debug_info_builder_context) + resolve_specification(dwarf, entry_unit, &new_entry, debug_info_builder_context) } else { - warn!("Failed to fetch DIE. Debug information may be incomplete."); + warn!("Failed to fetch DIE for attr DW_AT_abstract_origin. Debug information may be incomplete."); DieReference::Err } } DieReference::Err => DieReference::Err, } } else { - DieReference::UnitAndOffset((unit, entry.offset())) + DieReference::UnitAndOffset((dwarf, unit, entry.offset())) } } // Get name from DIE, or referenced dependencies -pub(crate) fn get_name>( +pub(crate) fn get_name( + dwarf: &Dwarf, unit: &Unit, entry: &DebuggingInformationEntry, debug_info_builder_context: &DebugInfoBuilderContext, ) -> Option { - match resolve_specification(unit, entry, debug_info_builder_context) { - DieReference::UnitAndOffset((entry_unit, entry_offset)) => { + match resolve_specification(dwarf, unit, entry, debug_info_builder_context) { + DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { if let Ok(Some(attr_val)) = entry_unit .entry(entry_offset) .unwrap() .attr_value(constants::DW_AT_name) { - if let Ok(attr_string) = debug_info_builder_context - .dwarf() - .attr_string(entry_unit, attr_val) + if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val.clone()) { if let Ok(attr_string) = attr_string.to_string() { return Some(attr_string.to_string()); } } + else if let Some(dwarf) = &dwarf.sup { + if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val) + { + if let Ok(attr_string) = attr_string.to_string() { + return Some(attr_string.to_string()); + } + } + } } // if let Some(raw_name) = get_raw_name(unit, entry, debug_info_builder_context) { @@ -157,26 +201,32 @@ pub(crate) fn get_name>( } // Get raw name from DIE, or referenced dependencies -pub(crate) fn get_raw_name>( +pub(crate) fn get_raw_name( + dwarf: &Dwarf, unit: &Unit, entry: &DebuggingInformationEntry, - debug_info_builder_context: &DebugInfoBuilderContext, ) -> Option { if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_linkage_name) { - if let Ok(attr_string) = debug_info_builder_context - .dwarf() - .attr_string(unit, attr_val) + if let Ok(attr_string) = dwarf.attr_string(unit, attr_val.clone()) { if let Ok(attr_string) = attr_string.to_string() { return Some(attr_string.to_string()); } } + else if let Some(dwarf) = dwarf.sup() { + if let Ok(attr_string) = dwarf.attr_string(unit, attr_val) + { + if let Ok(attr_string) = attr_string.to_string() { + return Some(attr_string.to_string()); + } + } + } } None } // Get the size of an object as a usize -pub(crate) fn get_size_as_usize>( +pub(crate) fn get_size_as_usize( entry: &DebuggingInformationEntry, ) -> Option { if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) { @@ -189,7 +239,7 @@ pub(crate) fn get_size_as_usize>( } // Get the size of an object as a u64 -pub(crate) fn get_size_as_u64>( +pub(crate) fn get_size_as_u64( entry: &DebuggingInformationEntry, ) -> Option { if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) { @@ -202,7 +252,7 @@ pub(crate) fn get_size_as_u64>( } // Get the size of a subrange as a u64 -pub(crate) fn get_subrange_size>( +pub(crate) fn get_subrange_size( entry: &DebuggingInformationEntry, ) -> u64 { if let Ok(Some(attr)) = entry.attr(constants::DW_AT_upper_bound) { @@ -217,35 +267,27 @@ pub(crate) fn get_subrange_size>( } // Get the start address of a function -pub(crate) fn get_start_address>( +pub(crate) fn get_start_address( + dwarf: &Dwarf, unit: &Unit, entry: &DebuggingInformationEntry, - debug_info_builder_context: &DebugInfoBuilderContext, ) -> Option { if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_low_pc) { - match debug_info_builder_context - .dwarf() - .attr_address(unit, attr_val) + match dwarf.attr_address(unit, attr_val) { Ok(Some(val)) => Some(val), _ => None, } } else if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_entry_pc) { - match debug_info_builder_context - .dwarf() - .attr_address(unit, attr_val) + match dwarf.attr_address(unit, attr_val) { Ok(Some(val)) => Some(val), _ => None, } } else if let Ok(Some(attr_value)) = entry.attr_value(constants::DW_AT_ranges) { - if let Ok(Some(ranges_offset)) = debug_info_builder_context - .dwarf() - .attr_ranges_offset(unit, attr_value) + if let Ok(Some(ranges_offset)) = dwarf.attr_ranges_offset(unit, attr_value) { - if let Ok(mut ranges) = debug_info_builder_context - .dwarf() - .ranges(unit, ranges_offset) + if let Ok(mut ranges) = dwarf.ranges(unit, ranges_offset) { if let Ok(Some(range)) = ranges.next() { return Some(range.begin); @@ -259,7 +301,7 @@ pub(crate) fn get_start_address>( } // Get an attribute value as a u64 if it can be coerced -pub(crate) fn get_attr_as_u64>(attr: &Attribute) -> Option { +pub(crate) fn get_attr_as_u64(attr: &Attribute) -> Option { if let Some(value) = attr.udata_value() { Some(value) } else if let Some(value) = attr.sdata_value() { @@ -278,7 +320,7 @@ pub(crate) fn get_attr_as_u64>(attr: &Attribute) -> } // Get an attribute value as a usize if it can be coerced -pub(crate) fn get_attr_as_usize>(attr: Attribute) -> Option { +pub(crate) fn get_attr_as_usize(attr: Attribute) -> Option { if let Some(value) = attr.u8_value() { Some(value.into()) } else if let Some(value) = attr.u16_value() { @@ -292,7 +334,7 @@ pub(crate) fn get_attr_as_usize>(attr: Attribute) - // Get an attribute value as a usize if it can be coerced // Parses DW_OP_address, DW_OP_const -pub(crate) fn get_expr_value>( +pub(crate) fn get_expr_value( unit: &Unit, attr: Attribute, ) -> Option { @@ -365,11 +407,9 @@ pub(crate) fn get_build_id(view: &BinaryView) -> Result { } -pub(crate) fn download_debug_info(view: &BinaryView) -> Result, String> { +pub(crate) fn download_debug_info(build_id: &String, view: &BinaryView) -> Result, String> { let settings = Settings::new(""); - let build_id = get_build_id(view)?; - let debug_server_urls = settings.get_string_list("network.debuginfodServers", Some(view), None); for debug_server_url in debug_server_urls.iter() { @@ -438,7 +478,7 @@ pub(crate) fn download_debug_info(view: &BinaryView) -> Result, } -pub(crate) fn find_local_debug_file(view: &BinaryView) -> Option { +pub(crate) fn find_local_debug_file(build_id: &String, view: &BinaryView) -> Option { let settings = Settings::new(""); let debug_info_paths = settings.get_string_list("analysis.debugInfo.debugDirectories", Some(view), None); @@ -446,11 +486,6 @@ pub(crate) fn find_local_debug_file(view: &BinaryView) -> Option { return None } - let build_id = match get_build_id(view) { - Ok(x) => x, - Err(_) => return None, - }; - for debug_info_path in debug_info_paths.into_iter() { if let Ok(path) = PathBuf::from_str(&debug_info_path.to_string()) { @@ -482,17 +517,23 @@ pub(crate) fn find_local_debug_file(view: &BinaryView) -> Option { } -pub(crate) fn load_debug_info_for_build_id(view: &BinaryView) -> Result>, String> { - if let Some(debug_file_path) = find_local_debug_file(view) { - Ok( +pub(crate) fn load_debug_info_for_build_id(build_id: &String, view: &BinaryView) -> (Option>, bool) { + if let Some(debug_file_path) = find_local_debug_file(build_id, view) { + return + ( binaryninja::load_with_options( debug_file_path, false, Some("{\"analysis.debugInfo.internal\": false}") - ) - ) + ), + false + ); } - else { - Ok(None) + else if Settings::new("").get_bool("network.enableDebuginfod", Some(view), None) { + return ( + download_debug_info(build_id, view).ok(), + true + ); } + (None, false) } -- cgit v1.3.1