diff options
| author | KyleMiles <krm504@nyu.edu> | 2023-04-18 12:38:09 -0400 |
|---|---|---|
| committer | KyleMiles <krm504@nyu.edu> | 2023-07-10 12:58:35 -0400 |
| commit | 6325884782a47e0c89154cf7ce0952368e25ea2a (patch) | |
| tree | aacb435dc9e386035b75471715abcc912ce18697 /rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs | |
| parent | 068a2ff42ef4e15e67d2904166e53a51420b65a1 (diff) | |
DWARF Import DebugInfo Plugin
Resolves #3206
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs')
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs new file mode 100644 index 00000000..0cb9ede2 --- /dev/null +++ b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -0,0 +1,263 @@ +// Copyright 2021-2023 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}; + +use binaryninja::{ + debuginfo::{DebugFunctionInfo, DebugInfo}, + rc::*, + templatesimplifier::simplify_str_to_str, + types::{Conf, FunctionParameter, Type}, +}; + +use gimli::{DebuggingInformationEntry, Reader, Unit}; + +use std::{ + collections::{hash_map::Values, HashMap}, + ffi::CString, + hash::Hash, +}; + +pub(crate) type TypeUID = usize; + +///////////////////////// +// FunctionInfoBuilder + +// TODO : Function local variables +#[derive(PartialEq, Eq, Hash)] +pub struct FunctionInfoBuilder { + pub full_name: Option<CString>, + pub raw_name: Option<CString>, + pub return_type: Option<TypeUID>, + pub address: Option<u64>, + pub parameters: Vec<Option<(CString, TypeUID)>>, +} + +impl FunctionInfoBuilder { + pub fn update( + &mut self, + full_name: Option<CString>, + raw_name: Option<CString>, + return_type: Option<TypeUID>, + address: Option<u64>, + parameters: Vec<Option<(CString, 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() { + if let Some(old_parameter) = self.parameters.get(i) { + if old_parameter.is_none() { + self.parameters[i] = new_parameter; + } + } else { + self.parameters.push(new_parameter); + } + } + } +} + +////////////////////// +// DebugInfoBuilder + +// TODO : Don't make this pub...fix the value thing +pub(crate) struct DebugType { + name: CString, + t: Ref<Type>, + commit: bool, +} + +// 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 struct DebugInfoBuilder { + functions: Vec<FunctionInfoBuilder>, + types: HashMap<TypeUID, DebugType>, + data_variables: HashMap<u64, (Option<CString>, TypeUID)>, + names: HashMap<TypeUID, CString>, +} + +impl DebugInfoBuilder { + pub fn new() -> Self { + DebugInfoBuilder { + functions: vec![], + types: HashMap::new(), + data_variables: HashMap::new(), + names: HashMap::new(), + } + } + + #[allow(clippy::too_many_arguments)] + pub fn insert_function( + &mut self, + full_name: Option<CString>, + raw_name: Option<CString>, + return_type: Option<TypeUID>, + address: Option<u64>, + parameters: Vec<Option<(CString, TypeUID)>>, + ) { + if let Some(function) = self.functions.iter_mut().find(|func| { + (func.raw_name.is_some() && func.raw_name == raw_name) + || (func.full_name.is_some() && func.full_name == full_name) + }) { + function.update(full_name, raw_name, return_type, address, parameters); + } else { + self.functions.push(FunctionInfoBuilder { + full_name, + raw_name, + return_type, + address, + parameters, + }); + } + } + + pub fn functions(&self) -> &[FunctionInfoBuilder] { + &self.functions + } + + pub(crate) fn types(&self) -> Values<'_, TypeUID, DebugType> { + self.types.values() + } + + pub fn add_type(&mut self, type_uid: TypeUID, name: CString, t: Ref<Type>, commit: bool) { + assert!(self + .types + .insert(type_uid, DebugType { name, t, commit }) + .is_none()); + } + + // TODO : Non-copy? + pub fn get_type(&self, type_uid: TypeUID) -> Option<(CString, Ref<Type>)> { + self.types + .get(&type_uid) + .map(|type_ref_ref| (type_ref_ref.name.clone(), type_ref_ref.t.clone())) + } + + pub fn contains_type(&self, type_uid: TypeUID) -> bool { + self.types.get(&type_uid).is_some() + } + + pub fn add_data_variable(&mut self, address: u64, name: Option<CString>, type_uid: TypeUID) { + assert!(self + .data_variables + .insert(address, (name, type_uid)) + .is_none()); + } + + pub fn set_name(&mut self, die_uid: TypeUID, name: CString) { + assert!(self.names.insert(die_uid, name).is_none()); + } + + pub fn get_name<R: Reader<Offset = usize>>( + &self, + unit: &Unit<R>, + entry: &DebuggingInformationEntry<R>, + ) -> Option<CString> { + self.names + .get(&get_uid( + unit, + &unit.entry(resolve_specification(unit, entry)).unwrap(), + )) + .cloned() + } + + fn commit_types(&self, debug_info: &mut DebugInfo) { + for debug_type in self.types() { + if debug_type.commit { + debug_info.add_type(debug_type.name.clone(), debug_type.t.as_ref()); + } + } + } + + // 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().1, + name.clone() + )); + } + } + + fn commit_functions(&self, debug_info: &mut DebugInfo) { + for function in self.functions() { + let return_type = match function.return_type { + Some(return_type_id) => { + Conf::new(self.get_type(return_type_id).unwrap().1.clone(), 0) + } + _ => Conf::new(binaryninja::types::Type::void(), 0), + }; + + let parameters: Vec<FunctionParameter<CString>> = function + .parameters + .iter() + .filter_map(|parameter| { + if let Some((name, uid)) = parameter { + Some(FunctionParameter::new( + self.get_type(*uid).unwrap().1, + name.clone(), + None, + )) + } else { + None + } + }) + .collect(); + + // TODO : Handle + let platform = None; + let variable_parameters = false; + // let calling_convention: Option<Ref<CallingConvention<CoreArchitecture>>> = None; + + let function_type = + binaryninja::types::Type::function(&return_type, ¶meters, variable_parameters); + + let simplified_full_name = function + .full_name + .as_ref() + .map(|name| simplify_str_to_str(name.as_ref()).as_str().to_owned()) + .map(|simp| CString::new(simp).unwrap()); + + debug_info.add_function(DebugFunctionInfo::new( + simplified_full_name.clone(), + simplified_full_name, // 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(function_type), + function.address, + platform, + )); + } + } + + pub fn commit_info(&self, debug_info: &mut DebugInfo) { + self.commit_types(debug_info); + self.commit_data_variables(debug_info); + self.commit_functions(debug_info); + } +} |
