diff options
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs')
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs | 643 |
1 files changed, 0 insertions, 643 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs deleted file mode 100644 index 168ddaaa..00000000 --- a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ /dev/null @@ -1,643 +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. - -use crate::{helpers::{get_uid, resolve_specification, DieReference}, ReaderType}; - -use binaryninja::{ - binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}, - debuginfo::{DebugFunctionInfo, DebugInfo}, - platform::Platform, - rc::*, - symbol::SymbolType, - templatesimplifier::simplify_str_to_fqn, - types::{Conf, FunctionParameter, NamedTypedVariable, Type, Variable, VariableSourceType}, -}; - -use gimli::{DebuggingInformationEntry, Dwarf, Unit}; - -use indexmap::{map::Values, IndexMap}; -use log::{debug, error, warn}; -use std::{ - cmp::Ordering, - collections::HashMap, - hash::Hash, -}; - -pub(crate) type TypeUID = usize; - -///////////////////////// -// FunctionInfoBuilder - -// TODO : Function local variables -#[derive(PartialEq, Eq, Hash)] -pub(crate) struct FunctionInfoBuilder { - pub(crate) full_name: Option<String>, - pub(crate) raw_name: Option<String>, - pub(crate) return_type: Option<TypeUID>, - pub(crate) address: Option<u64>, - pub(crate) parameters: Vec<Option<(String, TypeUID)>>, - pub(crate) platform: Option<Ref<Platform>>, - pub(crate) variable_arguments: bool, - pub(crate) stack_variables: Vec<NamedTypedVariable>, - pub(crate) use_cfa: bool, //TODO actually store more info about the frame base -} - -impl FunctionInfoBuilder { - pub(crate) fn update( - &mut self, - full_name: Option<String>, - raw_name: Option<String>, - return_type: Option<TypeUID>, - address: Option<u64>, - parameters: &Vec<Option<(String, TypeUID)>>, - ) { - if full_name.is_some() { - self.full_name = full_name; - } - - if raw_name.is_some() { - self.raw_name = raw_name; - } - - if return_type.is_some() { - self.return_type = return_type; - } - - if address.is_some() { - self.address = address; - } - - for (i, new_parameter) in parameters.into_iter().enumerate() { - match self.parameters.get(i) { - Some(None) => self.parameters[i] = new_parameter.clone(), - Some(Some(_)) => (), - // Some(Some((name, _))) if name.as_bytes().is_empty() => { - // self.parameters[i] = new_parameter - // } - // Some(Some((_, uid))) if *uid == 0 => self.parameters[i] = new_parameter, // TODO : This is a placebo....void types aren't actually UID 0 - _ => self.parameters.push(new_parameter.clone()), - } - } - } -} - -////////////////////// -// DebugInfoBuilder - -// TODO : Don't make this pub...fix the value thing -pub(crate) struct DebugType { - name: String, - t: Ref<Type>, - commit: bool, -} - -impl DebugType { - pub fn get_name(&self) -> &String { - &self.name - } - - pub fn get_type(&self) -> Ref<Type> { - self.t.clone() - } -} - -pub(crate) struct DebugInfoBuilderContext<R: ReaderType> { - units: Vec<Unit<R>>, - sup_units: Vec<Unit<R>>, - names: HashMap<TypeUID, String>, - default_address_size: usize, - pub(crate) total_die_count: usize, - pub(crate) total_unit_size_bytes: usize, -} - -impl<R: ReaderType> DebugInfoBuilderContext<R> { - pub(crate) fn new(view: &BinaryView, dwarf: &Dwarf<R>) -> Option<Self> { - - let mut units = vec![]; - let mut iter = dwarf.units(); - while let Ok(Some(header)) = iter.next() { - if let Ok(unit) = dwarf.unit(header) { - units.push(unit); - } else { - error!("Unable to read DWARF information. File may be malformed or corrupted. Not applying debug info."); - return None; - } - } - - let mut sup_units = vec![]; - if let Some(sup_dwarf) = dwarf.sup() { - let mut sup_iter = sup_dwarf.units(); - while let Ok(Some(header)) = sup_iter.next() { - if let Ok(unit) = sup_dwarf.unit(header) { - sup_units.push(unit); - } else { - error!("Unable to read supplementary DWARF information. File may be malformed or corrupted. Not applying debug info."); - return None; - } - } - } - - Some(Self { - units, - sup_units, - names: HashMap::new(), - default_address_size: view.address_size(), - total_die_count: 0, - total_unit_size_bytes: 0, - }) - } - - pub(crate) fn units(&self) -> &[Unit<R>] { - &self.units - } - - pub(crate) fn sup_units(&self) -> &[Unit<R>] { - &self.sup_units - } - - pub(crate) fn default_address_size(&self) -> usize { - self.default_address_size - } - - pub(crate) fn set_name(&mut self, die_uid: TypeUID, name: String) { - // die_uids need to be unique here - assert!(self.names.insert(die_uid, name).is_none()); - } - - pub(crate) fn get_name( - &self, - dwarf: &Dwarf<R>, - unit: &Unit<R>, - entry: &DebuggingInformationEntry<R>, - ) -> Option<String> { - match resolve_specification(dwarf, unit, entry, self) { - DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => self - .names - .get(&get_uid( - dwarf, - entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - )) - .cloned(), - DieReference::Err => None, - } - } -} - -// DWARF info is stored and displayed in a tree, but is really a graph -// The purpose of this builder is to help resolve those graph edges by mapping partial function -// info and types to one DIE's UID (T) before adding the completed info to BN's debug info -pub(crate) struct DebugInfoBuilder { - functions: Vec<FunctionInfoBuilder>, - raw_function_name_indices: HashMap<String, usize>, - full_function_name_indices: HashMap<String, usize>, - types: IndexMap<TypeUID, DebugType>, - data_variables: HashMap<u64, (Option<String>, TypeUID)>, - range_data_offsets: iset::IntervalMap<u64, i64> -} - -impl DebugInfoBuilder { - pub(crate) fn new() -> Self { - Self { - functions: vec![], - raw_function_name_indices: HashMap::new(), - full_function_name_indices: HashMap::new(), - types: IndexMap::new(), - data_variables: HashMap::new(), - range_data_offsets: iset::IntervalMap::new(), - } - } - - pub(crate) fn set_range_data_offsets(&mut self, offsets: iset::IntervalMap<u64, i64>) { - self.range_data_offsets = offsets - } - - #[allow(clippy::too_many_arguments)] - pub(crate) fn insert_function( - &mut self, - full_name: Option<String>, - raw_name: Option<String>, - return_type: Option<TypeUID>, - address: Option<u64>, - parameters: &Vec<Option<(String, TypeUID)>>, - variable_arguments: bool, - use_cfa: bool, - ) -> Option<usize> { - // Returns the index of the function - // Raw names should be the primary key, but if they don't exist, use the full name - // TODO : Consider further falling back on address/architecture - - /* - If it has a raw_name and we know it, update it and return - Else if it has a full_name and we know it, update it and return - Else Add a new entry if we don't know the full_name or raw_name - */ - - if let Some(ident) = &raw_name { - // check if we already know about this raw name's index - // if we do, and the full name will change, remove the known full index if it exists - // update the function - // if the full name exists, update the stored index for the full name - if let Some(idx) = self.raw_function_name_indices.get(ident) { - let function = self.functions.get_mut(*idx).unwrap(); - - if function.full_name.is_some() && function.full_name != full_name { - self.full_function_name_indices.remove(function.full_name.as_ref().unwrap()); - } - - function.update(full_name, raw_name, return_type, address, parameters); - - if function.full_name.is_some() { - self.full_function_name_indices.insert(function.full_name.clone().unwrap(), *idx); - } - - return Some(*idx); - } - } - else if let Some(ident) = &full_name { - // check if we already know about this full name's index - // if we do, and the raw name will change, remove the known raw index if it exists - // update the function - // if the raw name exists, update the stored index for the raw name - if let Some(idx) = self.full_function_name_indices.get(ident) { - let function = self.functions.get_mut(*idx).unwrap(); - - if function.raw_name.is_some() && function.raw_name != raw_name { - self.raw_function_name_indices.remove(function.raw_name.as_ref().unwrap()); - } - - function.update(full_name, raw_name, return_type, address, parameters); - - if function.raw_name.is_some() { - self.raw_function_name_indices.insert(function.raw_name.clone().unwrap(), *idx); - } - - return Some(*idx); - } - } - else { - debug!("Function entry in DWARF without full or raw name."); - return None; - } - - let function = FunctionInfoBuilder { - full_name, - raw_name, - return_type, - address, - parameters: parameters.clone(), - platform: None, - variable_arguments, - stack_variables: vec![], - use_cfa, - }; - - if let Some(n) = &function.full_name { - self.full_function_name_indices.insert(n.clone(), self.functions.len()); - } - - if let Some(n) = &function.raw_name { - self.raw_function_name_indices.insert(n.clone(), self.functions.len()); - } - - self.functions.push(function); - Some(self.functions.len()-1) - } - - pub(crate) fn functions(&self) -> &[FunctionInfoBuilder] { - &self.functions - } - - #[allow(dead_code)] - pub(crate) fn types(&self) -> Values<'_, TypeUID, DebugType> { - self.types.values() - } - - pub(crate) fn add_type(&mut self, type_uid: TypeUID, name: &String, t: Ref<Type>, commit: bool) { - if let Some(DebugType { - name: existing_name, - t: existing_type, - commit: _, - }) = self.types.insert( - type_uid, - DebugType { - name: name.clone(), - t: t.clone(), - commit, - }, - ) { - if existing_type != t && commit { - warn!("DWARF info contains duplicate type definition. Overwriting type `{}` (named `{:?}`) with `{}` (named `{:?}`)", - existing_type, - existing_name, - t, - name - ); - } - } - } - - pub(crate) fn remove_type(&mut self, type_uid: TypeUID) { - self.types.swap_remove(&type_uid); - } - - pub(crate) fn get_type(&self, type_uid: TypeUID) -> Option<&DebugType> { - self.types.get(&type_uid) - } - - pub(crate) fn contains_type(&self, type_uid: TypeUID) -> bool { - self.types.contains_key(&type_uid) - } - - - pub(crate) fn add_stack_variable( - &mut self, - fn_idx: Option<usize>, - offset: i64, - name: Option<String>, - type_uid: Option<TypeUID>, - lexical_block: Option<&iset::IntervalSet<u64>>, - ) { - let name = match name { - Some(x) => { - if x.len() == 1 && x.chars().next() == Some('\x00') { - // Anonymous variable, generate name - format!("debug_var_{}", offset) - } - else { - x - } - }, - None => { - // Anonymous variable, generate name - format!("debug_var_{}", offset) - } - }; - - let Some(function_index) = fn_idx else { - // If we somehow lost track of what subprogram we're in or we're not actually in a subprogram - error!("Trying to add a local variable outside of a subprogram. Please report this issue."); - return; - }; - - // Either get the known type or use a 0 confidence void type so we at least get the name applied - let t = match type_uid { - Some(uid) => Conf::new(self.get_type(uid).unwrap().get_type(), 128), - None => Conf::new(Type::void(), 0) - }; - let function = &mut self.functions[function_index]; - - // TODO: If we can't find a known offset can we try to guess somehow? - - let Some(func_addr) = function.address else { - // If we somehow are processing a function's variables before the function is created - error!("Trying to add a local variable without a known function start. Please report this issue."); - return; - }; - - let adjusted_offset; - - let Some(adjustment_at_variable_lifetime_start) = lexical_block.and_then(|block_ranges| { - block_ranges - .unsorted_iter() - .find_map(|x| self.range_data_offsets.values_overlap(x.start).next()) - }).or_else(|| { - // Try using the offset at the adjustment 4 bytes after the function start, in case the function starts with a stack adjustment - // TODO: This is a decent heuristic but not perfect, since further adjustments could still be made - self.range_data_offsets.values_overlap(func_addr+4).next() - }).or_else(|| { - // If all else fails, use the function start address - self.range_data_offsets.values_overlap(func_addr).next() - }) else { - // Unknown why, but this is happening with MachO + external dSYM - debug!("Refusing to add a local variable ({}@{}) to function at {} without a known CIE offset.", name, offset, func_addr); - return; - }; - - // TODO: handle non-sp frame bases - // TODO: if not in a lexical block these can be wrong, see https://github.com/Vector35/binaryninja-api/issues/5882#issuecomment-2406065057 - if function.use_cfa { - // Apply CFA offset to variable storage offset if DW_AT_frame_base is frame base is CFA - adjusted_offset = offset + adjustment_at_variable_lifetime_start; - } - else { - // If it's using SP, we know the SP offset is <SP offset> + (<entry SP CFA offset> - <SP CFA offset>) - let Some(adjustment_at_entry) = self.range_data_offsets.values_overlap(func_addr).next() else { - // Unknown why, but this is happening with MachO + external dSYM - debug!("Refusing to add a local variable ({}@{}) to function at {} without a known CIE offset for function start.", name, offset, func_addr); - return; - }; - - adjusted_offset = offset + (adjustment_at_entry - adjustment_at_variable_lifetime_start); - } - - if adjusted_offset > 0 { - // If we somehow end up with a positive sp offset - error!("Trying to add a local variable \"{}\" in function at {:#x} at positive storage offset {}. Please report this issue.", name, func_addr, adjusted_offset); - return; - } - - let var = Variable::new(VariableSourceType::StackVariableSourceType, 0, adjusted_offset); - function.stack_variables.push(NamedTypedVariable::new(var, name, t, false)); - - } - - pub(crate) fn add_data_variable( - &mut self, - address: u64, - name: Option<String>, - type_uid: TypeUID, - ) { - if let Some((_existing_name, existing_type_uid)) = - self.data_variables.insert(address, (name, type_uid)) - { - let existing_type = self.get_type(existing_type_uid).unwrap().get_type(); - let new_type = self.get_type(type_uid).unwrap().get_type(); - - if existing_type_uid != type_uid || existing_type != new_type { - warn!("DWARF info contains duplicate data variable definition. Overwriting data variable at 0x{:08x} (`{}`) with `{}`", - address, - existing_type, - new_type - ); - } - } - } - - fn commit_types(&self, debug_info: &mut DebugInfo) { - let mut type_uids_by_name: HashMap<String, TypeUID> = HashMap::new(); - - for (debug_type_uid, debug_type) in self.types.iter() { - if !debug_type.commit { - continue; - } - - let mut debug_type_name = debug_type.name.clone(); - - // Prevent storing two types with the same name and differing definitions - if let Some(stored_uid) = type_uids_by_name.get(&debug_type_name) { - let Some(stored_debug_type) = self.types.get(stored_uid) else { - error!("Stored type name without storing a type! Please report this error. UID: {}, name: {}", stored_uid, debug_type_name); - continue; - }; - - let mut skip_adding_type = false; - if stored_debug_type.t != debug_type.t { - // We already stored a type with this name and it's a different type, deconflict the name and try again - let mut i = 1; - loop { - if let Some(stored_uid) = type_uids_by_name.get(&debug_type_name) { - if debug_type_uid == stored_uid { - // We already have a type with this name but it's the same type so we're ok - skip_adding_type = true; - break; - } - if let Some(stored_debug_type) = self.types.get(stored_uid) { - if stored_debug_type.t == debug_type.t { - // We already have a type with this name but it's the same type so we're ok - skip_adding_type = true; - break; - } - } - - debug_type_name = format!("{}_{}", debug_type.name, i); - i += 1; - } - else { - // We found a unique name - break; - } - } - } - - if skip_adding_type { - continue; - } - }; - - type_uids_by_name.insert(debug_type_name.clone(), *debug_type_uid); - debug_info.add_type(debug_type_name, debug_type.t.as_ref(), &[]); - // TODO : Components - } - } - - // TODO : Consume data? - fn commit_data_variables(&self, debug_info: &mut DebugInfo) { - for (&address, (name, type_uid)) in &self.data_variables { - assert!(debug_info.add_data_variable( - address, - &self.get_type(*type_uid).unwrap().t, - name.clone(), - &[] // TODO : Components - )); - } - } - - fn get_function_type(&self, function: &FunctionInfoBuilder) -> Ref<Type> { - let return_type = match function.return_type { - Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().get_type(), 128), - _ => Conf::new(binaryninja::types::Type::void(), 0), - }; - - let parameters: Vec<FunctionParameter> = function - .parameters - .iter() - .filter_map(|parameter| match parameter { - Some((name, 0)) => Some(FunctionParameter::new(Type::void(), name.clone(), None)), - Some((name, uid)) => Some(FunctionParameter::new( - self.get_type(*uid).unwrap().get_type(), - name.clone(), - None, - )), - _ => None, - }) - .collect(); - - binaryninja::types::Type::function(&return_type, ¶meters, function.variable_arguments) - } - - fn commit_functions(&self, debug_info: &mut DebugInfo) { - for function in self.functions() { - // let calling_convention: Option<Ref<CallingConvention<CoreArchitecture>>> = None; - - debug_info.add_function(DebugFunctionInfo::new( - function.full_name.clone(), - function.full_name.clone(), // TODO : This should eventually be changed, but the "full_name" should probably be the unsimplified version, and the "short_name" should be the simplified version...currently the symbols view shows the full version, so changing it here too makes it look bad in the UI - function.raw_name.clone(), - Some(self.get_function_type(function)), - function.address, - function.platform.clone(), - vec![], // TODO : Components - function.stack_variables.clone(), // TODO: local non-stack variables - )); - } - } - - pub(crate) fn post_process(&mut self, bv: &BinaryView, _debug_info: &mut DebugInfo) -> &Self { - // When originally resolving names, we need to check: - // If there's already a name from binja that's "more correct" than what we found (has more namespaces) - // If there's no name for the DIE, but there's a linkage name that's resolved in binja to a usable name - // This is no longer true, because DWARF doesn't provide platform information for functions, so we at least need to post-process thumb functions - - for func in &mut self.functions { - // If the function's raw name already exists in the binary... - if let Some(raw_name) = &func.raw_name { - if let Ok(symbol) = bv.symbol_by_raw_name(raw_name) { - // Link mangled names without addresses to existing symbols in the binary - if func.address.is_none() && func.raw_name.is_some() { - // DWARF doesn't contain GOT info, so remove any entries there...they will be wrong (relying on Binja's mechanisms for the GOT is good ) - if symbol.sym_type() != SymbolType::ImportAddress { - func.address = Some(symbol.address()); - } - } - - if let Some(full_name) = &func.full_name { - let func_full_name = full_name; - let symbol_full_name = symbol.full_name(); - - // If our name has fewer namespaces than the existing name, assume we lost the namespace info - if simplify_str_to_fqn(func_full_name, true).len() - < simplify_str_to_fqn(symbol_full_name.clone(), true).len() - { - func.full_name = Some(symbol_full_name.to_string()); - } - } - } - } - - if let Some(address) = func.address.as_mut() { - let (diff, overflowed) = bv.start().overflowing_sub(bv.original_image_base()); - if !overflowed { - *address = (*address).overflowing_add(diff).0; // rebase the address - let existing_functions = bv.functions_at(*address); - match existing_functions.len().cmp(&1) { - Ordering::Greater => { - warn!("Multiple existing functions at address {address:08x}. One or more functions at this address may have the wrong platform information. Please report this binary."); - } - Ordering::Equal => func.platform = Some(existing_functions.get(0).platform()), - Ordering::Less => {} - } - } - } - } - - self - } - - pub(crate) fn commit_info(&self, debug_info: &mut DebugInfo) { - self.commit_types(debug_info); - self.commit_data_variables(debug_info); - self.commit_functions(debug_info); - } -} |
