diff options
| author | KyleMiles <krm504@nyu.edu> | 2023-08-25 14:08:25 -0400 |
|---|---|---|
| committer | KyleMiles <krm504@nyu.edu> | 2023-09-29 12:02:59 -0400 |
| commit | 20e79d4e43a8734cdc5c4f9ca5b204a2c44aca8e (patch) | |
| tree | 067c27dd7c35158ef2d9f8fa4073e81574088406 /rust/examples | |
| parent | a452691f145583f092893dff6ecab14375d0613f (diff) | |
DWARF Import : Misc code cleanup, improvements, and enabling by default changes
This includes:
Gracefully handle missing DIE references
Partially revert 7849cda
Misc DWARFv5 Fixes
Speed Improvements
Fix crash on unexpected EOF
Partially revert 0d5fe9ec8963a26361b8bf1af17e029afc87f952 and Correctly initialize DWARF Import plugin
Diffstat (limited to 'rust/examples')
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/die_handlers.rs | 75 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs | 282 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/functions.rs | 32 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/helpers.rs | 236 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/lib.rs | 144 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/types.rs | 165 | ||||
| -rw-r--r-- | rust/examples/dwarfdump/Cargo.lock | 399 |
7 files changed, 497 insertions, 836 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/die_handlers.rs b/rust/examples/dwarf/dwarf_import/src/die_handlers.rs index 84ad8a43..cd3ad55f 100644 --- a/rust/examples/dwarf/dwarf_import/src/die_handlers.rs +++ b/rust/examples/dwarf/dwarf_import/src/die_handlers.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::dwarfdebuginfo::{DebugInfoBuilder, TypeUID}; +use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID}; use crate::helpers::*; use crate::types::get_type; @@ -21,30 +21,26 @@ use binaryninja::{ types::{EnumerationBuilder, FunctionParameter, ReferenceType, Type, TypeBuilder}, }; -use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Dwarf, Reader, Unit}; +use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Reader, Unit}; use std::ffi::CString; -pub fn handle_base_type<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, +pub(crate) fn handle_base_type<R: Reader<Offset = usize>>( unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, - debug_info_builder: &mut DebugInfoBuilder, + debug_info_builder_context: &DebugInfoBuilderContext<R>, ) -> Option<Ref<Type>> { // All base types have: - // DW_AT_name // DW_AT_encoding (our concept of type_class) // DW_AT_byte_size and/or DW_AT_bit_size + // *DW_AT_name // *DW_AT_endianity (assumed default for arch) // *DW_AT_data_bit_offset (assumed 0) // *Some indication of signedness? // * = Optional - // TODO : By spec base types need to have a name, what if it's spec non-conforming? - let name = debug_info_builder - .get_name(dwarf, unit, entry) - .expect("DW_TAG_base does not have name attribute"); - let size = get_size_as_usize(entry).expect("DW_TAG_base does not have size attribute"); + let name = debug_info_builder_context.get_name(unit, entry)?; + let size = get_size_as_usize(entry)?; match entry.attr_value(constants::DW_AT_encoding) { Ok(Some(Encoding(encoding))) => { match encoding { @@ -62,7 +58,7 @@ pub fn handle_base_type<R: Reader<Offset = usize>>( constants::DW_ATE_edited => None, constants::DW_ATE_signed_fixed => None, constants::DW_ATE_unsigned_fixed => None, - constants::DW_ATE_decimal_float => Some(Type::named_float(size, name)), // TODO : How is this different from binary floating point, ie. DW_ATE_float? + constants::DW_ATE_decimal_float => Some(Type::named_float(size, name)), constants::DW_ATE_UTF => Some(Type::named_int(size, false, name)), // TODO : Verify constants::DW_ATE_UCS => None, constants::DW_ATE_ASCII => None, // Some sort of array? @@ -75,11 +71,10 @@ pub fn handle_base_type<R: Reader<Offset = usize>>( } } -pub fn handle_enum<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, +pub(crate) fn handle_enum<R: Reader<Offset = usize>>( unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, - debug_info_builder: &mut DebugInfoBuilder, + debug_info_builder_context: &DebugInfoBuilderContext<R>, ) -> Option<Ref<Type>> { // All base types have: // DW_AT_byte_size @@ -114,9 +109,7 @@ pub fn handle_enum<R: Reader<Offset = usize>>( let mut children = tree.root().unwrap().children(); while let Ok(Some(child)) = children.next() { if child.entry().tag() == constants::DW_TAG_enumerator { - let name = debug_info_builder - .get_name(dwarf, unit, child.entry()) - .expect("DW_TAG_enumeration_type does not have name attribute"); + let name = debug_info_builder_context.get_name(unit, child.entry())?; let value = get_attr_as_u64( &child .entry() @@ -137,7 +130,7 @@ pub fn handle_enum<R: Reader<Offset = usize>>( )) } -pub fn handle_typedef( +pub(crate) fn handle_typedef( debug_info_builder: &mut DebugInfoBuilder, entry_type: Option<TypeUID>, typedef_name: CString, @@ -150,8 +143,6 @@ pub fn handle_typedef( // This will fail in the case where we have a typedef to a type that doesn't exist (failed to parse, incomplete, etc) if let Some(entry_type_offset) = entry_type { if let Some((name, t)) = debug_info_builder.get_type(entry_type_offset) { - // return (Some(t), true); - // TODO : This doesn't filter all the bad typedefs...we should consider not typedefing pointers or arrays, or it's a bug in my parsing if typedef_name == name { return (Some(t), false); } else if typedef_name != name { @@ -164,8 +155,9 @@ pub fn handle_typedef( (None, false) } -pub fn handle_pointer<R: Reader<Offset = usize>>( +pub(crate) fn handle_pointer<R: Reader<Offset = usize>>( entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, debug_info_builder: &mut DebugInfoBuilder, entry_type: Option<TypeUID>, reference_type: ReferenceType, @@ -203,7 +195,7 @@ pub fn handle_pointer<R: Reader<Offset = usize>>( let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1; Some(Type::pointer_of_width( parent_type.as_ref(), - debug_info_builder.default_address_size(), + debug_info_builder_context.default_address_size(), false, false, Some(reference_type), @@ -211,7 +203,7 @@ pub fn handle_pointer<R: Reader<Offset = usize>>( } else { Some(Type::pointer_of_width( Type::void().as_ref(), - debug_info_builder.default_address_size(), + debug_info_builder_context.default_address_size(), false, false, Some(reference_type), @@ -219,7 +211,7 @@ pub fn handle_pointer<R: Reader<Offset = usize>>( } } -pub fn handle_array<R: Reader<Offset = usize>>( +pub(crate) fn handle_array<R: Reader<Offset = usize>>( unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, debug_info_builder: &mut DebugInfoBuilder, @@ -265,10 +257,10 @@ pub fn handle_array<R: Reader<Offset = usize>>( } } -pub fn handle_function<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, +pub(crate) fn handle_function<R: Reader<Offset = usize>>( unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, debug_info_builder: &mut DebugInfoBuilder, entry_type: Option<TypeUID>, ) -> Option<Ref<Type>> { @@ -305,7 +297,7 @@ pub fn handle_function<R: Reader<Offset = usize>>( }; // Alias function type in the case that it contains itself - if let Some(name) = debug_info_builder.get_name(dwarf, unit, entry) { + if let Some(name) = debug_info_builder_context.get_name(unit, entry) { debug_info_builder.add_type( get_uid(unit, entry), name.clone(), @@ -313,7 +305,7 @@ pub fn handle_function<R: Reader<Offset = usize>>( name, &Type::function::<String, &binaryninja::types::Type>( return_type.as_ref(), - &vec![], + &[], false, ), ), @@ -325,15 +317,19 @@ pub fn handle_function<R: Reader<Offset = usize>>( let mut variable_arguments = false; // Get all the children and populate - // TODO : Handle other attributes? let mut tree = unit.entries_tree(Some(entry.offset())).unwrap(); let mut children = tree.root().unwrap().children(); while let Ok(Some(child)) = children.next() { if child.entry().tag() == constants::DW_TAG_formal_parameter { if let (Some(child_uid), Some(name)) = { ( - get_type(dwarf, unit, child.entry(), debug_info_builder), - debug_info_builder.get_name(dwarf, unit, child.entry()), + get_type( + unit, + child.entry(), + debug_info_builder_context, + debug_info_builder, + ), + debug_info_builder_context.get_name(unit, child.entry()), ) } { let child_type = debug_info_builder.get_type(child_uid).unwrap().1; @@ -344,7 +340,7 @@ pub fn handle_function<R: Reader<Offset = usize>>( } } - if debug_info_builder.get_name(dwarf, unit, entry).is_some() { + if debug_info_builder_context.get_name(unit, entry).is_some() { debug_info_builder.remove_type(get_uid(unit, entry)); } @@ -355,7 +351,7 @@ pub fn handle_function<R: Reader<Offset = usize>>( )) } -pub fn handle_const( +pub(crate) fn handle_const( debug_info_builder: &mut DebugInfoBuilder, entry_type: Option<TypeUID>, ) -> Option<Ref<Type>> { @@ -375,7 +371,7 @@ pub fn handle_const( } } -pub fn handle_volatile( +pub(crate) fn handle_volatile( debug_info_builder: &mut DebugInfoBuilder, entry_type: Option<TypeUID>, ) -> Option<Ref<Type>> { @@ -394,12 +390,3 @@ pub fn handle_volatile( Some(TypeBuilder::void().set_volatile(true).finalize()) } } - -// Handler template: -// pub fn handle_<R: Reader<Offset = usize>>( -// dwarf: &Dwarf<R>, -// unit: &Unit<R>, -// entry: &DebuggingInformationEntry<R>, -// debug_info_builder: &mut DebugInfoBuilder, -// ) -> Option<Ref<Type>> { -// } diff --git a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs index b088d527..429e64e2 100644 --- a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -15,10 +15,11 @@ use crate::helpers::{get_uid, resolve_specification, DieReference}; use binaryninja::{ - binaryview::{BinaryView, BinaryViewBase}, + binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}, debuginfo::{DebugFunctionInfo, DebugInfo}, rc::*, - templatesimplifier::simplify_str_to_str, + symbol::SymbolType, + templatesimplifier::simplify_str_to_fqn, types::{Conf, FunctionParameter, Type}, }; @@ -38,16 +39,16 @@ pub(crate) type TypeUID = usize; // 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)>>, +pub(crate) struct FunctionInfoBuilder { + pub(crate) full_name: Option<CString>, + pub(crate) raw_name: Option<CString>, + pub(crate) return_type: Option<TypeUID>, + pub(crate) address: Option<u64>, + pub(crate) parameters: Vec<Option<(CString, TypeUID)>>, } impl FunctionInfoBuilder { - pub fn update( + pub(crate) fn update( &mut self, full_name: Option<CString>, raw_name: Option<CString>, @@ -72,12 +73,14 @@ impl FunctionInfoBuilder { } 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); + match self.parameters.get(i) { + Some(None) => self.parameters[i] = new_parameter, + 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), } } } @@ -93,34 +96,90 @@ pub(crate) struct DebugType { commit: bool, } +pub(crate) struct DebugInfoBuilderContext<R: Reader<Offset = usize>> { + dwarf: Dwarf<R>, + units: Vec<Unit<R>>, + names: HashMap<TypeUID, CString>, + default_address_size: usize, + pub(crate) total_die_count: usize, +} + +impl<R: Reader<Offset = usize>> 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; + } + } + + Some(Self { + dwarf, + units, + names: HashMap::new(), + default_address_size: view.address_size(), + total_die_count: 0, + }) + } + + pub(crate) fn dwarf(&self) -> &Dwarf<R> { + &self.dwarf + } + + pub(crate) fn units(&self) -> &[Unit<R>] { + &self.units + } + + pub(crate) fn default_address_size(&self) -> usize { + self.default_address_size + } + + pub(crate) fn set_name(&mut self, die_uid: TypeUID, name: CString) { + assert!(self.names.insert(die_uid, name).is_none()); + } + + pub(crate) fn get_name( + &self, + unit: &Unit<R>, + entry: &DebuggingInformationEntry<R>, + ) -> Option<CString> { + match resolve_specification(unit, entry, self) { + DieReference::UnitAndOffset((entry_unit, entry_offset)) => self + .names + .get(&get_uid( + 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 struct DebugInfoBuilder { +pub(crate) struct DebugInfoBuilder { functions: Vec<FunctionInfoBuilder>, types: HashMap<TypeUID, DebugType>, data_variables: HashMap<u64, (Option<CString>, TypeUID)>, - names: HashMap<TypeUID, CString>, - default_address_size: usize, } impl DebugInfoBuilder { - pub fn new(view: &BinaryView) -> Self { - DebugInfoBuilder { + pub(crate) fn new() -> Self { + Self { functions: vec![], types: HashMap::new(), data_variables: HashMap::new(), - names: HashMap::new(), - default_address_size: view.address_size(), } } - pub fn default_address_size(&self) -> usize { - self.default_address_size - } - #[allow(clippy::too_many_arguments)] - pub fn insert_function( + pub(crate) fn insert_function( &mut self, full_name: Option<CString>, raw_name: Option<CString>, @@ -128,9 +187,18 @@ impl DebugInfoBuilder { 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) + // 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 let Some(function) = self + .functions + .iter_mut() + .find(|func| func.raw_name.is_some() && func.raw_name == raw_name) + { + function.update(full_name, raw_name, return_type, address, parameters); + } else if let Some(function) = self.functions.iter_mut().find(|func| { + (func.raw_name.is_none() || raw_name.is_none()) + && func.full_name.is_some() + && func.full_name == full_name }) { function.update(full_name, raw_name, return_type, address, parameters); } else { @@ -144,7 +212,7 @@ impl DebugInfoBuilder { } } - pub fn functions(&self) -> &[FunctionInfoBuilder] { + pub(crate) fn functions(&self) -> &[FunctionInfoBuilder] { &self.functions } @@ -152,7 +220,13 @@ impl DebugInfoBuilder { self.types.values() } - pub fn add_type(&mut self, type_uid: TypeUID, name: CString, t: Ref<Type>, commit: bool) { + pub(crate) fn add_type( + &mut self, + type_uid: TypeUID, + name: CString, + t: Ref<Type>, + commit: bool, + ) { if let Some(DebugType { name: existing_name, t: existing_type, @@ -165,7 +239,7 @@ impl DebugInfoBuilder { commit, }, ) { - if existing_type != t { + if existing_type != t && commit { error!("DWARF info contains duplicate type definition. Overwriting type `{}` (named `{:?}`) with `{}` (named `{:?}`)", existing_type, existing_name, @@ -176,22 +250,27 @@ impl DebugInfoBuilder { } } - pub fn remove_type(&mut self, type_uid: TypeUID) { + pub(crate) fn remove_type(&mut self, type_uid: TypeUID) { self.types.remove(&type_uid); } // TODO : Non-copy? - pub fn get_type(&self, type_uid: TypeUID) -> Option<(CString, Ref<Type>)> { + pub(crate) 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 { + pub(crate) 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) { + pub(crate) fn add_data_variable( + &mut self, + address: u64, + name: Option<CString>, + type_uid: TypeUID, + ) { if let Some((_existing_name, existing_type_uid)) = self.data_variables.insert(address, (name, type_uid)) { @@ -208,31 +287,6 @@ impl DebugInfoBuilder { } } - 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, - dwarf: &Dwarf<R>, - unit: &Unit<R>, - entry: &DebuggingInformationEntry<R>, - ) -> Option<CString> { - match resolve_specification(dwarf, unit, entry) { - DieReference::Offset(entry_offset) => self - .names - .get(&get_uid(unit, &unit.entry(entry_offset).unwrap())) - .cloned(), - DieReference::UnitAndOffset((entry_unit, entry_offset)) => self - .names - .get(&get_uid( - &entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - )) - .cloned(), - } - } - fn commit_types(&self, debug_info: &mut DebugInfo) { for debug_type in self.types() { if debug_type.commit { @@ -252,57 +306,87 @@ impl DebugInfoBuilder { } } - 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), - }; + 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().1.clone(), 0), + _ => Conf::new(binaryninja::types::Type::void(), 0), + }; - let parameters: Vec<FunctionParameter<CString>> = 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().1, - name.clone(), - None, - )), - _ => None, - }) - .collect(); + let parameters: Vec<FunctionParameter<CString>> = 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().1, + name.clone(), + None, + )), + _ => None, + }) + .collect(); + + // TODO : Handle + let variable_parameters = false; + binaryninja::types::Type::function(&return_type, ¶meters, variable_parameters) + } + + fn commit_functions(&self, debug_info: &mut DebugInfo) { + for function in self.functions() { // 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.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(function_type), + Some(self.get_function_type(function)), function.address, platform, )); } } - pub fn commit_info(&self, debug_info: &mut DebugInfo) { + pub(crate) fn post_process(&mut self, bv: &BinaryView, _debug_info: &mut DebugInfo) -> &Self { + // TODO : We don't need post-processing is we process correctly the first time.... + // 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 + + 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.as_c_str()) { + // 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.to_str().unwrap(); + 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(CString::new(symbol_full_name.to_string()).unwrap()); + } + } + } + } + } + + 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); diff --git a/rust/examples/dwarf/dwarf_import/src/functions.rs b/rust/examples/dwarf/dwarf_import/src/functions.rs index 127ec6f4..427b5c26 100644 --- a/rust/examples/dwarf/dwarf_import/src/functions.rs +++ b/rust/examples/dwarf/dwarf_import/src/functions.rs @@ -12,25 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::dwarfdebuginfo::{DebugInfoBuilder, TypeUID}; +use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID}; use crate::helpers::*; use crate::types::get_type; -use gimli::{constants, DebuggingInformationEntry, Dwarf, Reader, Unit}; +use gimli::{constants, DebuggingInformationEntry, Reader, Unit}; use std::ffi::CString; fn get_parameters<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, debug_info_builder: &mut DebugInfoBuilder, ) -> Vec<Option<(CString, TypeUID)>> { if !entry.has_children() { vec![] } else { // We make a new tree from the current entry to iterate over its children - // TODO : We could instead pass the `entries` object down from parse_dwarf to avoid parsing the same object multiple times let mut sub_die_tree = unit.entries_tree(Some(entry.offset())).unwrap(); let root = sub_die_tree.root().unwrap(); @@ -39,8 +38,13 @@ fn get_parameters<R: Reader<Offset = usize>>( while let Some(child) = children.next().unwrap() { match child.entry().tag() { constants::DW_TAG_formal_parameter => { - let name = debug_info_builder.get_name(dwarf, unit, child.entry()); - let type_ = get_type(dwarf, unit, child.entry(), debug_info_builder); + let name = debug_info_builder_context.get_name(unit, child.entry()); + let type_ = get_type( + unit, + child.entry(), + debug_info_builder_context, + debug_info_builder, + ); if let Some(parameter_name) = name { if let Some(parameter_type) = type_ { result.push(Some((parameter_name, parameter_type))); @@ -59,20 +63,18 @@ fn get_parameters<R: Reader<Offset = usize>>( } } -pub fn parse_function_entry<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, +pub(crate) fn parse_function_entry<R: Reader<Offset = usize>>( unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, debug_info_builder: &mut DebugInfoBuilder, ) { - // TODO : Handle OOT, stubs/trampolines - // Collect function properties (if they exist in this DIE) - let full_name = debug_info_builder.get_name(dwarf, unit, entry); - let raw_name = get_raw_name(dwarf, unit, entry); - let return_type = get_type(dwarf, unit, entry, debug_info_builder); - let address = get_start_address(dwarf, unit, entry); - let parameters = get_parameters(dwarf, unit, entry, debug_info_builder); + let full_name = debug_info_builder_context.get_name(unit, entry); + let raw_name = get_raw_name(unit, entry, debug_info_builder_context); + let return_type = get_type(unit, entry, debug_info_builder_context, debug_info_builder); + let address = get_start_address(unit, entry, debug_info_builder_context); + let parameters = get_parameters(unit, entry, debug_info_builder_context, debug_info_builder); debug_info_builder.insert_function(full_name, raw_name, return_type, address, parameters); } diff --git a/rust/examples/dwarf/dwarf_import/src/helpers.rs b/rust/examples/dwarf/dwarf_import/src/helpers.rs index f3b7dd81..05cf6f82 100644 --- a/rust/examples/dwarf/dwarf_import/src/helpers.rs +++ b/rust/examples/dwarf/dwarf_import/src/helpers.rs @@ -12,12 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::DebugInfoBuilderContext; + use gimli::{ constants, Attribute, AttributeValue, - AttributeValue::{DebugInfoRef, DebugInfoRefSup, UnitRef}, - DebuggingInformationEntry, Dwarf, Operation, Reader, Unit, UnitOffset, UnitSectionOffset, + AttributeValue::{DebugInfoRef, UnitRef}, + DebuggingInformationEntry, Operation, Reader, Unit, UnitOffset, UnitSectionOffset, }; +use log::warn; use std::ffi::CString; pub(crate) fn get_uid<R: Reader<Offset = usize>>( @@ -33,182 +36,136 @@ pub(crate) fn get_uid<R: Reader<Offset = usize>>( //////////////////////////////////// // DIE attr convenience functions -pub(crate) enum DieReference<R: Reader<Offset = usize>> { - Offset(UnitOffset), - UnitAndOffset((Unit<R>, UnitOffset)), -} - -fn get_unit_copy<'a, R: Reader<Offset = usize>>(dwarf: &'a Dwarf<R>, unit: &'a Unit<R>) -> Unit<R> { - let mut iter = dwarf.units(); - while let Ok(Some(header)) = iter.next() { - if header.offset() == unit.header.offset() { - return dwarf.unit(header).unwrap(); - } - } - unreachable!() +pub(crate) enum DieReference<'a, R: Reader<Offset = usize>> { + UnitAndOffset((&'a Unit<R>, UnitOffset)), + Err, } pub(crate) fn get_attr_die<'a, R: Reader<Offset = usize>>( - dwarf: &'a Dwarf<R>, - _unit: &'a Unit<R>, - entry: &'a DebuggingInformationEntry<R>, + unit: &'a Unit<R>, + entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &'a DebugInfoBuilderContext<R>, attr: constants::DwAt, -) -> Option<DieReference<R>> { +) -> Option<DieReference<'a, R>> { match entry.attr_value(attr) { - Ok(Some(UnitRef(offset))) => Some(DieReference::Offset(offset)), - Ok(Some(DebugInfoRef(offset))) | Ok(Some(DebugInfoRefSup(offset))) => { - let mut iter = dwarf.units(); - while let Ok(Some(header)) = iter.next() { - if let Some(new_offset) = offset.to_unit_offset(&header) { - return Some(DieReference::UnitAndOffset(( - dwarf.unit(header).unwrap(), - new_offset, - ))); + Ok(Some(UnitRef(offset))) => Some(DieReference::UnitAndOffset((unit, offset))), + Ok(Some(DebugInfoRef(offset))) => { + 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((source_unit, new_offset))); } } - unreachable!() //None + warn!("Failed to fetch DIE. Debug information may be incomplete."); + None } + // Ok(Some(DebugInfoRefSup(offset))) TODO - dwarf 5 stuff _ => None, } } pub(crate) fn resolve_specification<'a, R: Reader<Offset = usize>>( - dwarf: &'a Dwarf<R>, unit: &'a Unit<R>, - entry: &'a DebuggingInformationEntry<R>, -) -> DieReference<R> { - if let Some(die_reference) = get_attr_die(dwarf, unit, entry, constants::DW_AT_specification) { - match die_reference { - DieReference::Offset(entry_offset) => { - resolve_specification(dwarf, unit, &unit.entry(entry_offset).unwrap()) - } - DieReference::UnitAndOffset((entry_unit, entry_offset)) => { - resolve_specification_slowpath( - dwarf, - &entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - ) - } - } - } else if let Some(die_reference) = - get_attr_die(dwarf, unit, entry, constants::DW_AT_abstract_origin) - { - match die_reference { - DieReference::Offset(entry_offset) => { - resolve_specification(dwarf, unit, &unit.entry(entry_offset).unwrap()) - } - DieReference::UnitAndOffset((entry_unit, entry_offset)) => { - resolve_specification_slowpath( - dwarf, - &entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - ) - } - } - } else { - DieReference::Offset(entry.offset()) - } -} - -fn resolve_specification_slowpath<'a, R: Reader<Offset = usize>>( - dwarf: &'a Dwarf<R>, - unit: &'a Unit<R>, - entry: &'a DebuggingInformationEntry<R>, -) -> DieReference<R> { - if let Some(die_reference) = get_attr_die(dwarf, unit, entry, constants::DW_AT_specification) { + entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &'a DebugInfoBuilderContext<R>, +) -> DieReference<'a, R> { + if let Some(die_reference) = get_attr_die( + unit, + entry, + debug_info_builder_context, + constants::DW_AT_specification, + ) { match die_reference { - DieReference::Offset(entry_offset) => { - resolve_specification_slowpath(dwarf, unit, &unit.entry(entry_offset).unwrap()) - } DieReference::UnitAndOffset((entry_unit, entry_offset)) => { - resolve_specification_slowpath( - dwarf, - &entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - ) + if let Ok(entry) = entry_unit.entry(entry_offset) { + resolve_specification(entry_unit, &entry, debug_info_builder_context) + } else { + warn!("Failed to fetch DIE. Debug information may be incomplete."); + DieReference::Err + } } + DieReference::Err => DieReference::Err, } - } else if let Some(die_reference) = - get_attr_die(dwarf, unit, entry, constants::DW_AT_abstract_origin) - { + } else if let Some(die_reference) = get_attr_die( + unit, + entry, + debug_info_builder_context, + constants::DW_AT_abstract_origin, + ) { match die_reference { - DieReference::Offset(entry_offset) => { - resolve_specification_slowpath(dwarf, unit, &unit.entry(entry_offset).unwrap()) - } DieReference::UnitAndOffset((entry_unit, entry_offset)) => { - resolve_specification_slowpath( - dwarf, - &entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - ) + if let Ok(entry) = entry_unit.entry(entry_offset) { + resolve_specification(entry_unit, &entry, debug_info_builder_context) + } else { + warn!("Failed to fetch DIE. Debug information may be incomplete."); + DieReference::Err + } } + DieReference::Err => DieReference::Err, } } else { - DieReference::UnitAndOffset((get_unit_copy(dwarf, unit), entry.offset())) + DieReference::UnitAndOffset((unit, entry.offset())) } } // Get name from DIE, or referenced dependencies pub(crate) fn get_name<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, ) -> Option<CString> { - match resolve_specification(dwarf, unit, entry) { - DieReference::Offset(entry_offset) => { - if let Ok(Some(attr_val)) = unit - .entry(entry_offset) - .unwrap() - .attr_value(constants::DW_AT_name) - { - if let Ok(attr_string) = dwarf.attr_string(unit, attr_val) { - if let Ok(attr_string) = attr_string.to_string() { - return Some(CString::new(attr_string.to_string()).unwrap()); - } - } - } - None - } + match resolve_specification(unit, entry, debug_info_builder_context) { DieReference::UnitAndOffset((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) = dwarf.attr_string(&entry_unit, attr_val) { + if let Ok(attr_string) = debug_info_builder_context + .dwarf() + .attr_string(entry_unit, attr_val) + { if let Ok(attr_string) = attr_string.to_string() { return Some(CString::new(attr_string.to_string()).unwrap()); } } } + + // if let Some(raw_name) = get_raw_name(unit, entry, debug_info_builder_context) { + // if let Some(arch) = debug_info_builder_context.default_architecture() { + // if let Ok((_, names)) = demangle_gnu3(&arch, raw_name, true) { + // return Some(CString::new(names.join("::")).unwrap()); + // } + // } + // } None } + DieReference::Err => None, } } // Get raw name from DIE, or referenced dependencies -pub(crate) fn get_raw_name<R: Reader>( - dwarf: &Dwarf<R>, +pub(crate) fn get_raw_name<R: Reader<Offset = usize>>( unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, ) -> Option<CString> { if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_linkage_name) { - if let Ok(attr_string) = dwarf.attr_string(unit, attr_val) { + if let Ok(attr_string) = debug_info_builder_context + .dwarf() + .attr_string(unit, attr_val) + { if let Ok(attr_string) = attr_string.to_string() { - Some(CString::new(attr_string.to_string()).unwrap()) - } else { - None + return Some(CString::new(attr_string.to_string()).unwrap()); } - } else { - None } - } else { - None } + None } // Get the size of an object as a usize -pub(crate) fn get_size_as_usize<R: Reader>(entry: &DebuggingInformationEntry<R>) -> Option<usize> { +pub(crate) fn get_size_as_usize<R: Reader<Offset = usize>>( + entry: &DebuggingInformationEntry<R>, +) -> Option<usize> { if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) { get_attr_as_usize(attr) } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_bit_size) { @@ -219,7 +176,9 @@ pub(crate) fn get_size_as_usize<R: Reader>(entry: &DebuggingInformationEntry<R>) } // Get the size of an object as a u64 -pub(crate) fn get_size_as_u64<R: Reader>(entry: &DebuggingInformationEntry<R>) -> Option<u64> { +pub(crate) fn get_size_as_u64<R: Reader<Offset = usize>>( + entry: &DebuggingInformationEntry<R>, +) -> Option<u64> { if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) { get_attr_as_u64(&attr) } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_bit_size) { @@ -230,7 +189,9 @@ pub(crate) fn get_size_as_u64<R: Reader>(entry: &DebuggingInformationEntry<R>) - } // Get the size of a subrange as a u64 -pub(crate) fn get_subrange_size<R: Reader>(entry: &DebuggingInformationEntry<R>) -> u64 { +pub(crate) fn get_subrange_size<R: Reader<Offset = usize>>( + entry: &DebuggingInformationEntry<R>, +) -> u64 { if let Ok(Some(attr)) = entry.attr(constants::DW_AT_upper_bound) { get_attr_as_u64(&attr).map_or(0, |v| v + 1) } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_count) { @@ -243,24 +204,36 @@ pub(crate) fn get_subrange_size<R: Reader>(entry: &DebuggingInformationEntry<R>) } // Get the start address of a function -pub(crate) fn get_start_address<R: Reader>( - dwarf: &Dwarf<R>, +pub(crate) fn get_start_address<R: Reader<Offset = usize>>( unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, ) -> Option<u64> { if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_low_pc) { - match dwarf.attr_address(unit, attr_val) { + match debug_info_builder_context + .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 dwarf.attr_address(unit, attr_val) { + match debug_info_builder_context + .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)) = dwarf.attr_ranges_offset(unit, attr_value) { - if let Ok(mut ranges) = dwarf.ranges(unit, ranges_offset) { + if let Ok(Some(ranges_offset)) = debug_info_builder_context + .dwarf() + .attr_ranges_offset(unit, attr_value) + { + if let Ok(mut ranges) = debug_info_builder_context + .dwarf() + .ranges(unit, ranges_offset) + { if let Ok(Some(range)) = ranges.next() { return Some(range.begin); } @@ -273,7 +246,7 @@ pub(crate) fn get_start_address<R: Reader>( } // Get an attribute value as a u64 if it can be coerced -pub(crate) fn get_attr_as_u64<R: Reader>(attr: &Attribute<R>) -> Option<u64> { +pub(crate) fn get_attr_as_u64<R: Reader<Offset = usize>>(attr: &Attribute<R>) -> Option<u64> { if let Some(value) = attr.u8_value() { Some(value.into()) } else if let Some(value) = attr.u16_value() { @@ -286,7 +259,7 @@ pub(crate) fn get_attr_as_u64<R: Reader>(attr: &Attribute<R>) -> Option<u64> { } // Get an attribute value as a usize if it can be coerced -pub(crate) fn get_attr_as_usize<R: Reader>(attr: Attribute<R>) -> Option<usize> { +pub(crate) fn get_attr_as_usize<R: Reader<Offset = usize>>(attr: Attribute<R>) -> Option<usize> { if let Some(value) = attr.u8_value() { Some(value.into()) } else if let Some(value) = attr.u16_value() { @@ -300,7 +273,10 @@ pub(crate) fn get_attr_as_usize<R: Reader>(attr: Attribute<R>) -> Option<usize> // 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<R: Reader>(unit: &Unit<R>, attr: Attribute<R>) -> Option<u64> { +pub(crate) fn get_expr_value<R: Reader<Offset = usize>>( + unit: &Unit<R>, + attr: Attribute<R>, +) -> Option<u64> { if let AttributeValue::Exprloc(mut expression) = attr.value() { match Operation::parse(&mut expression.0, unit.encoding()) { Ok(Operation::PlusConstant { value }) => Some(value), diff --git a/rust/examples/dwarf/dwarf_import/src/lib.rs b/rust/examples/dwarf/dwarf_import/src/lib.rs index 6ec2e5ad..7aab3775 100644 --- a/rust/examples/dwarf/dwarf_import/src/lib.rs +++ b/rust/examples/dwarf/dwarf_import/src/lib.rs @@ -18,7 +18,7 @@ mod functions; mod helpers; mod types; -use crate::dwarfdebuginfo::DebugInfoBuilder; +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_data_variable; @@ -27,7 +27,6 @@ use binaryninja::{ binaryview::{BinaryView, BinaryViewExt}, debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser}, logger, - settings::Settings, templatesimplifier::simplify_str_to_str, }; use dwarfreader::{ @@ -36,18 +35,16 @@ use dwarfreader::{ use gimli::{constants, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, SectionId, Unit}; -use log::LevelFilter; +use log::{warn, LevelFilter}; use std::ffi::CString; fn recover_names<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, - debug_info_builder: &mut DebugInfoBuilder, + debug_info_builder_context: &mut DebugInfoBuilderContext<R>, progress: &dyn Fn(usize, usize) -> Result<(), ()>, -) -> usize { - let mut total_die_count = 0; - let mut iter = dwarf.units(); +) { + let mut iter = debug_info_builder_context.dwarf().units(); while let Ok(Some(header)) = iter.next() { - let unit = dwarf.unit(header).unwrap(); + let unit = debug_info_builder_context.dwarf().unit(header).unwrap(); let mut namespace_qualifiers: Vec<(isize, CString)> = vec![]; let mut entries = unit.entries(); let mut depth = 0; @@ -55,14 +52,14 @@ fn recover_names<R: Reader<Offset = usize>>( // The first entry in the unit is the header for the unit if let Ok(Some((delta_depth, _))) = entries.next_dfs() { depth += delta_depth; - total_die_count += 1; + debug_info_builder_context.total_die_count += 1; } while let Ok(Some((delta_depth, entry))) = entries.next_dfs() { - total_die_count += 1; + debug_info_builder_context.total_die_count += 1; - if (*progress)(0, total_die_count).is_err() { - return 0; // Parsing canceled + if (*progress)(0, debug_info_builder_context.total_die_count).is_err() { + return; // Parsing canceled }; depth += delta_depth; @@ -74,34 +71,37 @@ fn recover_names<R: Reader<Offset = usize>>( match entry.tag() { constants::DW_TAG_namespace => { fn resolve_namespace_name<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, namespace_qualifiers: &mut Vec<(isize, CString)>, depth: isize, ) { - if let Some(namespace_qualifier) = get_name(dwarf, unit, entry) { - namespace_qualifiers.push((depth, namespace_qualifier)); - } else if let Some(die_reference) = - get_attr_die(dwarf, unit, entry, constants::DW_AT_extension) + if let Some(namespace_qualifier) = + get_name(unit, entry, debug_info_builder_context) { + namespace_qualifiers.push((depth, namespace_qualifier)); + } else if let Some(die_reference) = get_attr_die( + unit, + entry, + debug_info_builder_context, + constants::DW_AT_extension, + ) { match die_reference { - DieReference::Offset(entry_offset) => resolve_namespace_name( - dwarf, - unit, - &unit.entry(entry_offset).unwrap(), - namespace_qualifiers, - depth, - ), DieReference::UnitAndOffset((entry_unit, entry_offset)) => { resolve_namespace_name( - dwarf, - &entry_unit, + entry_unit, &entry_unit.entry(entry_offset).unwrap(), + debug_info_builder_context, namespace_qualifiers, depth, ) } + DieReference::Err => { + warn!( + "Failed to fetch DIE. Debug information may be incomplete." + ); + } } } else { namespace_qualifiers @@ -109,12 +109,18 @@ fn recover_names<R: Reader<Offset = usize>>( } } - resolve_namespace_name(dwarf, &unit, entry, &mut namespace_qualifiers, depth); + resolve_namespace_name( + &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) { + if let Some(name) = get_name(&unit, entry, debug_info_builder_context) { namespace_qualifiers.push((depth, name)) } else { namespace_qualifiers.push(( @@ -128,7 +134,7 @@ fn recover_names<R: Reader<Offset = usize>>( .unwrap(), )) } - debug_info_builder.set_name( + debug_info_builder_context.set_name( get_uid(&unit, entry), CString::new( simplify_str_to_str( @@ -146,8 +152,8 @@ fn recover_names<R: Reader<Offset = usize>>( 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.set_name( + if let Some(name) = get_name(&unit, entry, debug_info_builder_context) { + debug_info_builder_context.set_name( get_uid(&unit, entry), CString::new( simplify_str_to_str( @@ -167,24 +173,21 @@ fn recover_names<R: Reader<Offset = usize>>( } } _ => { - if let Some(name) = get_name(dwarf, &unit, entry) { - debug_info_builder.set_name(get_uid(&unit, entry), name); + if let Some(name) = get_name(&unit, entry, debug_info_builder_context) { + debug_info_builder_context.set_name(get_uid(&unit, entry), name); } } } } } - - total_die_count } fn parse_unit<R: Reader<Offset = usize>>( - 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, - total_die_count: usize, ) { let mut entries = unit.entries(); @@ -192,16 +195,21 @@ fn parse_unit<R: Reader<Offset = usize>>( // 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((_, entry))) = entries.next_dfs() { *current_die_number += 1; - if (*progress)(*current_die_number, total_die_count).is_err() { + if (*progress)( + *current_die_number, + debug_info_builder_context.total_die_count, + ) + .is_err() + { return; // Parsing canceled } match entry.tag() { constants::DW_TAG_subprogram => { - parse_function_entry(dwarf, unit, entry, debug_info_builder) + parse_function_entry(unit, entry, debug_info_builder_context, debug_info_builder) } constants::DW_TAG_variable => { - parse_data_variable(dwarf, unit, entry, debug_info_builder) + parse_data_variable(unit, entry, debug_info_builder_context, debug_info_builder) } _ => (), } @@ -237,29 +245,25 @@ fn parse_dwarf( // 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(view); - if (*progress)(0, 1).is_err() { - return debug_info_builder; // Parsing canceled - }; - let total_die_count = recover_names(&dwarf, &mut debug_info_builder, &progress); - if total_die_count == 0 { - return debug_info_builder; - } + let mut debug_info_builder = DebugInfoBuilder::new(); + if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, dwarf) { + recover_names(&mut debug_info_builder_context, &progress); + if debug_info_builder_context.total_die_count == 0 { + return debug_info_builder; + } - // Parse all the compilation units - let mut iter = dwarf.units(); - let mut current_die_number = 0; - while let Ok(Some(header)) = iter.next() { - parse_unit( - &dwarf, - &dwarf.unit(header).unwrap(), - &mut debug_info_builder, - &progress, - &mut current_die_number, - total_die_count, - ); + // Parse all the compilation units + let mut current_die_number = 0; + for unit in debug_info_builder_context.units() { + parse_unit( + unit, + &debug_info_builder_context, + &mut debug_info_builder, + &progress, + &mut current_die_number, + ); + } } - debug_info_builder } @@ -267,24 +271,20 @@ struct DWARFParser; impl CustomDebugInfoParser for DWARFParser { fn is_valid(&self, view: &BinaryView) -> bool { - Settings::new("").get_bool("corePlugins.dwarfImport", None, None) - && dwarfreader::is_valid(view) + dwarfreader::is_valid(view) } fn parse_info( &self, debug_info: &mut DebugInfo, - _: &BinaryView, + bv: &BinaryView, debug_file: &BinaryView, progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>, ) -> bool { - if Settings::new("").get_bool("corePlugins.dwarfImport", None, None) { - // Parse dwarf info in raw view or from a separate file - parse_dwarf(debug_file, progress).commit_info(debug_info); - true - } else { - false - } + parse_dwarf(debug_file, progress) + .post_process(bv, debug_info) + .commit_info(debug_info); + true } } diff --git a/rust/examples/dwarf/dwarf_import/src/types.rs b/rust/examples/dwarf/dwarf_import/src/types.rs index 0fef5fae..e6af9130 100644 --- a/rust/examples/dwarf/dwarf_import/src/types.rs +++ b/rust/examples/dwarf/dwarf_import/src/types.rs @@ -13,7 +13,7 @@ // limitations under the License. use crate::die_handlers::*; -use crate::dwarfdebuginfo::{DebugInfoBuilder, TypeUID}; +use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID}; use crate::helpers::*; use binaryninja::{ @@ -23,18 +23,19 @@ use binaryninja::{ }, }; -use gimli::{constants, DebuggingInformationEntry, Dwarf, Reader, Unit}; +use gimli::{constants, DebuggingInformationEntry, Reader, Unit}; +use log::warn; use std::ffi::CString; pub(crate) fn parse_data_variable<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, debug_info_builder: &mut DebugInfoBuilder, ) { - let full_name = debug_info_builder.get_name(dwarf, unit, entry); - let type_uid = get_type(dwarf, unit, entry, debug_info_builder); + let full_name = debug_info_builder_context.get_name(unit, entry); + let type_uid = get_type(unit, entry, debug_info_builder_context, debug_info_builder); let address = if let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) { get_expr_value(unit, attr) @@ -49,9 +50,9 @@ pub(crate) fn parse_data_variable<R: Reader<Offset = usize>>( fn do_structure_parse<R: Reader<Offset = usize>>( structure_type: StructureType, - dwarf: &Dwarf<R>, unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, debug_info_builder: &mut DebugInfoBuilder, ) -> Option<usize> { // All struct, union, and class types will have: @@ -91,8 +92,8 @@ fn do_structure_parse<R: Reader<Offset = usize>>( return None; } - let full_name = if get_name(dwarf, unit, entry).is_some() { - debug_info_builder.get_name(dwarf, unit, entry) + let full_name = if get_name(unit, entry, debug_info_builder_context).is_some() { + debug_info_builder_context.get_name(unit, entry) } else { None }; @@ -117,6 +118,17 @@ fn do_structure_parse<R: Reader<Offset = usize>>( ), false, ); + } else { + // We _need_ to have initial typedefs or else we can enter infinite parsing loops + // These get overwritten in the last step with the actual type, however, so this + // is either perfectly fine or breaking a bunch of NTRs + let full_name = format!("anonymous_structure_{:x}", get_uid(unit, entry)); + debug_info_builder.add_type( + get_uid(unit, entry), + CString::new(full_name.clone()).unwrap(), + Type::named_type_from_type(full_name, &Type::structure(&structure_builder.finalize())), + false, + ); } // Get all the children and populate @@ -124,10 +136,15 @@ fn do_structure_parse<R: Reader<Offset = usize>>( let mut children = tree.root().unwrap().children(); while let Ok(Some(child)) = children.next() { if child.entry().tag() == constants::DW_TAG_member { - if let Some(child_type_id) = get_type(dwarf, unit, child.entry(), debug_info_builder) { + if let Some(child_type_id) = get_type( + unit, + child.entry(), + debug_info_builder_context, + debug_info_builder, + ) { if let Some((_, child_type)) = debug_info_builder.get_type(child_type_id) { - if let Some(child_name) = debug_info_builder - .get_name(dwarf, unit, child.entry()) + if let Some(child_name) = debug_info_builder_context + .get_name(unit, child.entry()) .map_or( if child_type.type_class() == TypeClass::StructureTypeClass { Some(CString::new("").unwrap()) @@ -189,11 +206,10 @@ fn do_structure_parse<R: Reader<Offset = usize>>( } // This function iterates up through the dependency references, adding all the types along the way until there are no more or stopping at the first one already tracked, then returns the UID of the type of the given DIE -// TODO : Add a fail_list of UnitOffsets that already haven't been able to be parsed as not to duplicate work pub(crate) fn get_type<R: Reader<Offset = usize>>( - dwarf: &Dwarf<R>, unit: &Unit<R>, entry: &DebuggingInformationEntry<R>, + debug_info_builder_context: &DebugInfoBuilderContext<R>, debug_info_builder: &mut DebugInfoBuilder, ) -> Option<TypeUID> { // If this node (and thus all its referenced nodes) has already been processed, just return the offset @@ -206,62 +222,46 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>( return None; } - let entry_type = - if let Some(die_reference) = get_attr_die(dwarf, unit, entry, constants::DW_AT_type) { - // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed - match die_reference { - DieReference::Offset(entry_offset) => get_type( - dwarf, - unit, - &unit.entry(entry_offset).unwrap(), - debug_info_builder, - ), - DieReference::UnitAndOffset((entry_unit, entry_offset)) => get_type( - dwarf, - &entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - debug_info_builder, - ), + let entry_type = if let Some(die_reference) = get_attr_die( + unit, + entry, + debug_info_builder_context, + constants::DW_AT_type, + ) { + // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed + match die_reference { + DieReference::UnitAndOffset((entry_unit, entry_offset)) => get_type( + entry_unit, + &entry_unit.entry(entry_offset).unwrap(), + debug_info_builder_context, + debug_info_builder, + ), + DieReference::Err => { + warn!("Failed to fetch DIE. Debug information may be incomplete."); + None } - } else if let Some(die_reference) = - get_attr_die(dwarf, unit, entry, constants::DW_AT_specification) - { - // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed - match die_reference { - DieReference::Offset(entry_offset) => get_type( - dwarf, - unit, - &unit.entry(entry_offset).unwrap(), - debug_info_builder, - ), - DieReference::UnitAndOffset((entry_unit, entry_offset)) => get_type( - dwarf, - &entry_unit, + } + } else { + // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed + match resolve_specification(unit, entry, debug_info_builder_context) { + DieReference::UnitAndOffset((entry_unit, entry_offset)) + if entry_unit.header.offset() != unit.header.offset() + && entry_offset != entry.offset() => + { + get_type( + entry_unit, &entry_unit.entry(entry_offset).unwrap(), + debug_info_builder_context, debug_info_builder, - ), + ) } - } else if let Some(die_reference) = - get_attr_die(dwarf, unit, entry, constants::DW_AT_abstract_origin) - { - // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed - match die_reference { - DieReference::Offset(entry_offset) => get_type( - dwarf, - unit, - &unit.entry(entry_offset).unwrap(), - debug_info_builder, - ), - DieReference::UnitAndOffset((entry_unit, entry_offset)) => get_type( - dwarf, - &entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - debug_info_builder, - ), + DieReference::UnitAndOffset(_) => None, + DieReference::Err => { + warn!("Failed to fetch DIE. Debug information may be incomplete."); + None } - } else { - None - }; + } + }; // If this node (and thus all its referenced nodes) has already been processed, just return the offset // This check is not redundant because this type might have been processes in the recursive calls above @@ -273,52 +273,55 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>( // Create the type, make a TypeInfo for it, and add it to the debug info let (type_def, mut commit): (Option<Ref<Type>>, bool) = match entry.tag() { constants::DW_TAG_base_type => ( - handle_base_type(dwarf, unit, entry, debug_info_builder), + handle_base_type(unit, entry, debug_info_builder_context), false, ), constants::DW_TAG_structure_type => { return do_structure_parse( StructureType::StructStructureType, - dwarf, unit, entry, + debug_info_builder_context, debug_info_builder, ) } constants::DW_TAG_class_type => { return do_structure_parse( StructureType::ClassStructureType, - dwarf, unit, entry, + debug_info_builder_context, debug_info_builder, ) } constants::DW_TAG_union_type => { return do_structure_parse( StructureType::UnionStructureType, - dwarf, unit, entry, + debug_info_builder_context, debug_info_builder, ) } // Enum constants::DW_TAG_enumeration_type => { - (handle_enum(dwarf, unit, entry, debug_info_builder), true) + (handle_enum(unit, entry, debug_info_builder_context), true) } // Basic types - constants::DW_TAG_typedef => handle_typedef( - debug_info_builder, - entry_type, - debug_info_builder.get_name(dwarf, unit, entry).unwrap(), - ), + constants::DW_TAG_typedef => { + if let Some(name) = debug_info_builder_context.get_name(unit, entry) { + handle_typedef(debug_info_builder, entry_type, name) + } else { + (None, false) + } + } constants::DW_TAG_pointer_type => ( handle_pointer( entry, + debug_info_builder_context, debug_info_builder, entry_type, ReferenceType::PointerReferenceType, @@ -328,6 +331,7 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>( constants::DW_TAG_reference_type => ( handle_pointer( entry, + debug_info_builder_context, debug_info_builder, entry_type, ReferenceType::ReferenceReferenceType, @@ -337,6 +341,7 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>( constants::DW_TAG_rvalue_reference_type => ( handle_pointer( entry, + debug_info_builder_context, debug_info_builder, entry_type, ReferenceType::RValueReferenceType, @@ -351,7 +356,13 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>( // Strange Types constants::DW_TAG_unspecified_type => (Some(Type::void()), false), constants::DW_TAG_subroutine_type => ( - handle_function(dwarf, unit, entry, debug_info_builder, entry_type), + handle_function( + unit, + entry, + debug_info_builder_context, + debug_info_builder, + entry_type, + ), false, ), @@ -365,8 +376,8 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>( // Wrap our resultant type in a TypeInfo so that the internal DebugInfo class can manage it if let Some(type_def) = type_def { - let name = if get_name(dwarf, unit, entry).is_some() { - debug_info_builder.get_name(dwarf, unit, entry) + let name = if get_name(unit, entry, debug_info_builder_context).is_some() { + debug_info_builder_context.get_name(unit, entry) } else { None } diff --git a/rust/examples/dwarfdump/Cargo.lock b/rust/examples/dwarfdump/Cargo.lock deleted file mode 100644 index 2c728549..00000000 --- a/rust/examples/dwarfdump/Cargo.lock +++ /dev/null @@ -1,399 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "binaryninja" -version = "0.1.0" -dependencies = [ - "binaryninjacore-sys", - "libc", - "log", -] - -[[package]] -name = "binaryninjacore-sys" -version = "0.1.0" -dependencies = [ - "bindgen", -] - -[[package]] -name = "bindgen" -version = "0.58.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "clap", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "which", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "cexpr" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clang-sys" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "dwarfdump" -version = "0.1.0" -dependencies = [ - "binaryninja", - "gimli", -] - -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "gimli" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" -dependencies = [ - "fallible-iterator", - "indexmap", - "stable_deref_trait", -] - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "indexmap" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.112" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" - -[[package]] -name = "libloading" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "memchr", - "version_check", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "regex" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "shlex" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "which" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" -dependencies = [ - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" |
