diff options
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/lib.rs')
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/lib.rs | 723 |
1 files changed, 0 insertions, 723 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/lib.rs b/rust/examples/dwarf/dwarf_import/src/lib.rs deleted file mode 100644 index 6050593f..00000000 --- a/rust/examples/dwarf/dwarf_import/src/lib.rs +++ /dev/null @@ -1,723 +0,0 @@ -// Copyright 2021-2024 Vector 35 Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod die_handlers; -mod dwarfdebuginfo; -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_variable; - -use binaryninja::binaryview::BinaryViewBase; -use binaryninja::{ - binaryview::{BinaryView, BinaryViewExt}, - debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser}, - settings::Settings, - templatesimplifier::simplify_str_to_str, -}; -use dwarfreader::{ - create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf, -}; - -use functions::parse_lexical_block; -use gimli::{constants, CfaRule, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section, SectionId, Unit, UnwindContext, UnwindSection}; - -use helpers::{get_build_id, load_debug_info_for_build_id}; -use log::{debug, error, warn}; -use binaryninja::logger::Logger; - -trait ReaderType: Reader<Offset = usize> {} -impl<T: Reader<Offset = usize>> ReaderType for T {} - - -pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<(), ()> + 'b>( - original_fn: F, - subpart: usize, - subpart_weights: &[f64], -) -> Box<dyn Fn(usize, usize) -> Result<(), ()> + 'b> { - // Normalize weights - let weight_sum: f64 = subpart_weights.iter().sum(); - if weight_sum < 0.0001 { - return Box::new(|_, _| Ok(())); - } - - // Keep a running count of weights for the start - let mut subpart_starts = vec![]; - let mut start = 0f64; - for w in subpart_weights { - subpart_starts.push(start); - start += *w; - } - - let subpart_start = subpart_starts[subpart] / weight_sum; - let weight = subpart_weights[subpart] / weight_sum; - - Box::new(move |cur: usize, max: usize| { - // Just use a large number for easy divisibility - let steps = 1000000f64; - let subpart_size = steps * weight; - let subpart_progress = ((cur as f64) / (max as f64)) * subpart_size; - - original_fn( - (subpart_start * steps + subpart_progress) as usize, - steps as usize, - ) - }) -} - - -fn calculate_total_unit_bytes<R: ReaderType>( - dwarf: &Dwarf<R>, - debug_info_builder_context: &mut DebugInfoBuilderContext<R>, -) -{ - let mut iter = dwarf.units(); - let mut total_size: usize = 0; - while let Ok(Some(header)) = iter.next() - { - total_size += header.length_including_self(); - } - debug_info_builder_context.total_unit_size_bytes = total_size; -} - -fn recover_names<R: ReaderType>( - dwarf: &Dwarf<R>, - debug_info_builder_context: &mut DebugInfoBuilderContext<R>, - progress: &dyn Fn(usize, usize) -> Result<(), ()>, -) -> bool { - - let mut res = true; - if let Some(sup_dwarf) = dwarf.sup() { - res = recover_names_internal(sup_dwarf, debug_info_builder_context, progress); - } - - if res { - res = recover_names_internal(dwarf, debug_info_builder_context, progress); - } - res -} - -fn recover_names_internal<R: ReaderType>( - dwarf: &Dwarf<R>, - debug_info_builder_context: &mut DebugInfoBuilderContext<R>, - progress: &dyn Fn(usize, usize) -> Result<(), ()>, -) -> bool { - let mut iter = dwarf.units(); - let mut current_byte_offset: usize = 0; - while let Ok(Some(header)) = iter.next() { - let unit_offset = header.offset().as_debug_info_offset().unwrap().0; - let unit = dwarf.unit(header).unwrap(); - let mut namespace_qualifiers: Vec<(isize, String)> = vec![]; - let mut entries = unit.entries(); - let mut depth = 0; - - // The first entry in the unit is the header for the unit - if let Ok(Some((delta_depth, _))) = entries.next_dfs() { - depth += delta_depth; - debug_info_builder_context.total_die_count += 1; - } - - while let Ok(Some((delta_depth, entry))) = entries.next_dfs() { - debug_info_builder_context.total_die_count += 1; - - if (*progress)(current_byte_offset, debug_info_builder_context.total_unit_size_bytes).is_err() { - return false; // Parsing canceled - }; - current_byte_offset = unit_offset + entry.offset().0; - - depth += delta_depth; - if depth < 0 { - error!("DWARF information is seriously malformed. Aborting parsing."); - return false; - } - - // TODO : Better module/component support - namespace_qualifiers.retain(|&(entry_depth, _)| entry_depth < depth); - - match entry.tag() { - constants::DW_TAG_namespace => { - fn resolve_namespace_name<R: ReaderType>( - dwarf: &Dwarf<R>, - unit: &Unit<R>, - entry: &DebuggingInformationEntry<R>, - debug_info_builder_context: &DebugInfoBuilderContext<R>, - namespace_qualifiers: &mut Vec<(isize, String)>, - depth: isize, - ) { - if let Some(namespace_qualifier) = - get_name(dwarf, unit, entry, debug_info_builder_context) - { - namespace_qualifiers.push((depth, namespace_qualifier)); - } else if let Some(die_reference) = get_attr_die( - dwarf, - unit, - entry, - debug_info_builder_context, - constants::DW_AT_extension, - ) { - match die_reference { - DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { - resolve_namespace_name( - dwarf, - entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - debug_info_builder_context, - namespace_qualifiers, - depth, - ) - } - DieReference::Err => { - warn!( - "Failed to fetch DIE when resolving namespace. Debug information may be incomplete." - ); - } - } - } else { - namespace_qualifiers.push((depth, "anonymous_namespace".to_string())); - } - } - - resolve_namespace_name( - dwarf, - &unit, - entry, - debug_info_builder_context, - &mut namespace_qualifiers, - depth, - ); - } - constants::DW_TAG_class_type - | constants::DW_TAG_structure_type - | constants::DW_TAG_union_type => { - if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) { - namespace_qualifiers.push((depth, name)) - } else { - namespace_qualifiers.push(( - depth, - match entry.tag() { - constants::DW_TAG_class_type => "anonymous_class".to_string(), - constants::DW_TAG_structure_type => { - "anonymous_structure".to_string() - } - constants::DW_TAG_union_type => "anonymous_union".to_string(), - _ => unreachable!(), - }, - )) - } - debug_info_builder_context.set_name( - get_uid(dwarf, &unit, entry), - simplify_str_to_str( - namespace_qualifiers - .iter() - .map(|(_, namespace)| namespace.to_owned()) - .collect::<Vec<String>>() - .join("::"), - ) - .to_string(), - ); - } - constants::DW_TAG_typedef - | constants::DW_TAG_subprogram - | constants::DW_TAG_enumeration_type => { - if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) { - debug_info_builder_context.set_name( - get_uid(dwarf, &unit, entry), - simplify_str_to_str( - namespace_qualifiers - .iter() - .chain(vec![&(-1, name)].into_iter()) - .map(|(_, namespace)| namespace.to_owned()) - .collect::<Vec<String>>() - .join("::"), - ) - .to_string(), - ); - } - } - _ => { - if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) { - debug_info_builder_context.set_name(get_uid(dwarf, &unit, entry), name); - } - } - } - } - } - - true -} - -fn parse_unit<R: ReaderType>( - dwarf: &Dwarf<R>, - unit: &Unit<R>, - debug_info_builder_context: &DebugInfoBuilderContext<R>, - debug_info_builder: &mut DebugInfoBuilder, - progress: &dyn Fn(usize, usize) -> Result<(), ()>, - current_die_number: &mut usize, -) { - let mut entries = unit.entries(); - - let mut current_depth: isize = 0; - let mut functions_by_depth: Vec<(Option<usize>, isize)> = vec![]; - let mut lexical_blocks_by_depth: Vec<(iset::IntervalSet<u64>, 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((depth_delta, entry))) = entries.next_dfs() { - *current_die_number += 1; - if (*progress)( - *current_die_number, - debug_info_builder_context.total_die_count, - ) - .is_err() - { - 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; - } - - if let Some((_lexical_block, depth)) = lexical_blocks_by_depth.last() { - if current_depth <= *depth { - lexical_blocks_by_depth.pop(); - } - else { - break - } - } - else { - break; - } - } - - match entry.tag() { - constants::DW_TAG_subprogram => { - let fn_idx = parse_function_entry(dwarf, unit, entry, debug_info_builder_context, debug_info_builder); - functions_by_depth.push((fn_idx, current_depth)); - }, - constants::DW_TAG_lexical_block => { - if let Some(block_ranges) = parse_lexical_block(dwarf, unit, entry) { - lexical_blocks_by_depth.push((block_ranges, current_depth)); - } - }, - constants::DW_TAG_variable => { - let current_fn_idx = functions_by_depth.last().and_then(|x| x.0); - let current_lexical_block = lexical_blocks_by_depth.last().and_then(|x| Some(&x.0)); - parse_variable(dwarf, unit, entry, debug_info_builder_context, debug_info_builder, current_fn_idx, current_lexical_block) - }, - constants::DW_TAG_class_type | - constants::DW_TAG_enumeration_type | - constants::DW_TAG_structure_type | - constants::DW_TAG_union_type | - constants::DW_TAG_typedef => { - // Ensure types are loaded even if they're unused - types::get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder); - }, - _ => (), - } - } -} - -fn parse_unwind_section<R: Reader, U: UnwindSection<R>>( - view: &BinaryView, - unwind_section: U, -) -> gimli::Result<iset::IntervalMap<u64, i64>> -where <U as UnwindSection<R>>::Offset: std::hash::Hash { - 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()); - } else if let Ok(section) = view.section_by_name(".debug_frame").or(view.section_by_name("__debug_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 cfa_offsets = iset::IntervalMap::new(); - - let mut entries = unwind_section.entries(&bases); - let mut unwind_context = UnwindContext::new(); - loop { - match entries.next()? { - None => return Ok(cfa_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(|| unwind_section.cie_from_offset(bases, o)) - .clone() - }) { - Ok(fde) => fde, - Err(e) => { - error!("Failed to parse FDE: {}", e); - continue; - } - }; - - if fde.len() == 0 { - // This FDE is a terminator - return Ok(cfa_offsets); - } - - if fde.initial_address().overflowing_add(fde.len()).1 { - warn!("FDE at offset {:?} exceeds bounds of memory space! {:#x} + length {:#x}", fde.offset(), fde.initial_address(), fde.len()); - } else { - // Walk the FDE table rows and store their CFA - let mut fde_table = fde.rows(&unwind_section, &bases, &mut unwind_context)?; - - while let Some(row) = fde_table.next_row()? { - match row.cfa() { - CfaRule::RegisterAndOffset {register: _, offset} => { - // TODO: we should store offsets by register - if row.start_address() < row.end_address() { - cfa_offsets.insert( - row.start_address()..row.end_address(), - *offset, - ); - } - else { - debug!("Invalid FDE table row addresses: {:#x}..{:#x}", row.start_address(), row.end_address()); - } - }, - CfaRule::Expression(_) => { - debug!("Unhandled CFA expression when determining offset"); - } - }; - - } - } - } - } - } -} - -fn get_supplementary_build_id(bv: &BinaryView) -> Option<String> { - let raw_view = bv.raw_view().ok()?; - if let Ok(section) = raw_view.section_by_name(".gnu_debugaltlink") { - let start = section.start(); - let len = section.len(); - - if len < 20 { - // Not large enough to hold a build id - return None; - } - - raw_view - .read_vec(start, len) - .splitn(2, |x| *x == 0) - .last() - .map(|a| { - a.iter().map(|b| format!("{:02x}", b)).collect() - }) - } - else { - None - } -} - -fn parse_dwarf( - _bv: &BinaryView, - debug_bv: &BinaryView, - supplementary_bv: Option<&BinaryView>, - progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>, -) -> Result<DebugInfoBuilder, ()> { - // 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()?; - let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) { - debug_bv - } else { - raw_view - }; - - let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view); - - // gimli setup - let endian = get_endian(view); - let mut section_reader = - |section_id: SectionId| -> _ { create_section_reader(section_id, view, endian, dwo_file) }; - - let mut dwarf = match Dwarf::load(&mut section_reader) { - Ok(x) => x, - Err(e) => { - error!("Failed to load DWARF info: {}", e); - return Err(()); - } - }; - - if dwo_file { - dwarf.file_type = DwarfFileType::Dwo; - } - else { - dwarf.file_type = DwarfFileType::Main; - } - - if let Some(sup_bv) = supplementary_bv { - let sup_endian = get_endian(sup_bv); - let sup_dwo_file = is_dwo_dwarf(sup_bv) || is_raw_dwo_dwarf(sup_bv); - let sup_section_reader = - |section_id: SectionId| -> _ { create_section_reader(section_id, sup_bv, 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; - if view.section_by_name(".eh_frame").is_ok() || view.section_by_name("__eh_frame").is_ok() { - let eh_frame_endian = get_endian(view); - let mut eh_frame_section_reader = - |section_id: SectionId| -> _ { create_section_reader(section_id, view, eh_frame_endian, dwo_file) }; - let mut eh_frame = gimli::EhFrame::load(&mut eh_frame_section_reader).unwrap(); - eh_frame.set_address_size(view.address_size() as u8); - range_data_offsets = parse_unwind_section(view, eh_frame) - .map_err(|e| error!("Error parsing .eh_frame: {}", e))?; - } - else if view.section_by_name(".debug_frame").is_ok() || view.section_by_name("__debug_frame").is_ok() { - let debug_frame_endian = get_endian(view); - let mut debug_frame_section_reader = - |section_id: SectionId| -> _ { create_section_reader(section_id, view, debug_frame_endian, dwo_file) }; - let mut debug_frame = gimli::DebugFrame::load(&mut debug_frame_section_reader).unwrap(); - debug_frame.set_address_size(view.address_size() as u8); - range_data_offsets = parse_unwind_section(view, debug_frame) - .map_err(|e| error!("Error parsing .debug_frame: {}", e))?; - } - else { - range_data_offsets = 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, - // 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) { - calculate_total_unit_bytes(&dwarf, &mut debug_info_builder_context); - - let progress_weights = [0.5, 0.5]; - let name_progress = split_progress(&progress, 0, &progress_weights); - let parse_progress = split_progress(&progress, 1, &progress_weights); - - if !recover_names(&dwarf, &mut debug_info_builder_context, &name_progress) - || debug_info_builder_context.total_die_count == 0 - { - return Ok(debug_info_builder); - } - - // Parse all the compilation units - let mut current_die_number = 0; - - for unit in debug_info_builder_context.sup_units() { - parse_unit( - dwarf.sup().unwrap(), - &unit, - &debug_info_builder_context, - &mut debug_info_builder, - &parse_progress, - &mut current_die_number, - ); - } - - for unit in debug_info_builder_context.units() { - parse_unit( - &dwarf, - &unit, - &debug_info_builder_context, - &mut debug_info_builder, - &parse_progress, - &mut current_die_number, - ); - } - } - - Ok(debug_info_builder) -} - -struct DWARFParser; - -impl CustomDebugInfoParser for DWARFParser { - fn is_valid(&self, view: &BinaryView) -> bool { - if dwarfreader::is_valid(view) || dwarfreader::can_use_debuginfod(view) { - return true; - } - if dwarfreader::has_build_id_section(view) { - if let Ok(build_id) = get_build_id(view) { - if helpers::find_local_debug_file_for_build_id(&build_id, view).is_some() { - return true; - } - } - } - if helpers::find_sibling_debug_file(view).is_some() { - return true; - } - false - } - - fn parse_info( - &self, - debug_info: &mut DebugInfo, - bv: &BinaryView, - debug_file: &BinaryView, - progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>, - ) -> bool { - let (external_file, close_external) = if !dwarfreader::is_valid(bv) { - if let (Some(debug_view), x) = helpers::load_sibling_debug_file(bv) { - (Some(debug_view), x) - } - else if let Ok(build_id) = get_build_id(bv) { - helpers::load_debug_info_for_build_id(&build_id, bv) - } - else { - (None, false) - } - } - else { - (None, false) - }; - - let sup_bv = get_supplementary_build_id( - external_file - .as_deref() - .unwrap_or(debug_file) - ) - .and_then(|build_id| { - load_debug_info_for_build_id(&build_id, bv) - .0 - .map(|x| x.raw_view().unwrap()) - }); - - let result = match parse_dwarf( - bv, - external_file.as_deref().unwrap_or(debug_file), - sup_bv.as_deref(), - progress - ) - { - Ok(mut builder) => { - builder.post_process(bv, debug_info).commit_info(debug_info); - true - } - Err(_) => false, - }; - - if let (Some(ext), true) = (external_file, close_external) { - ext.file().close(); - } - - result - } -} - -#[no_mangle] -pub extern "C" fn CorePluginInit() -> bool { - Logger::new("DWARF").init(); - - let settings = Settings::new(""); - - settings.register_setting_json( - "network.enableDebuginfod", - r#"{ - "title" : "Enable Debuginfod Support", - "type" : "boolean", - "default" : false, - "description" : "Enable using Debuginfod servers to fetch DWARF debug info for files with a .note.gnu.build-id section.", - "ignore" : [] - }"#, - ); - - settings.register_setting_json( - "network.debuginfodServers", - r#"{ - "title" : "Debuginfod Server URLs", - "type" : "array", - "sorted" : true, - "default" : [], - "description" : "Servers to use for fetching DWARF debug info for files with a .note.gnu.build-id section.", - "ignore" : [] - }"#, - ); - - settings.register_setting_json( - "analysis.debugInfo.enableDebugDirectories", - r#"{ - "title" : "Enable Debug File Directories", - "type" : "boolean", - "default" : true, - "description" : "Enable searching local debug directories for DWARF debug info.", - "ignore" : [] - }"#, - ); - - settings.register_setting_json( - "analysis.debugInfo.debugDirectories", - r#"{ - "title" : "Debug File Directories", - "type" : "array", - "sorted" : true, - "default" : [], - "description" : "Paths to folder containing DWARF debug info stored by build id.", - "ignore" : [] - }"#, - ); - - settings.register_setting_json( - "analysis.debugInfo.loadSiblingDebugFiles", - r#"{ - "title" : "Enable Loading of Sibling Debug Files", - "type" : "boolean", - "default" : true, - "description" : "Enable automatic loading of X.debug and X.dSYM files next to a file named X.", - "ignore" : [] - }"#, - ); - - DebugInfoParser::register("DWARF", DWARFParser {}); - true -} |
