summaryrefslogtreecommitdiff
path: root/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs')
-rw-r--r--rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs643
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, &parameters, 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);
- }
-}