diff options
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/lib.rs')
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/lib.rs | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/lib.rs b/rust/examples/dwarf/dwarf_import/src/lib.rs new file mode 100644 index 00000000..0e5cfc6a --- /dev/null +++ b/rust/examples/dwarf/dwarf_import/src/lib.rs @@ -0,0 +1,252 @@ +// 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. + +mod die_handlers; +mod dwarfdebuginfo; +mod functions; +mod helpers; +mod types; + +use crate::dwarfdebuginfo::DebugInfoBuilder; +use crate::functions::parse_function_entry; +use crate::helpers::{get_name, get_uid}; +use crate::types::parse_data_variable; + +use binaryninja::{ + binaryview::{BinaryView, BinaryViewExt}, + debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser}, + logger, + templatesimplifier::simplify_str_to_str, +}; +use dwarfreader::{ + create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf, +}; + +use gimli::{ + constants, AttributeValue::UnitRef, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, + Unit, +}; + +use log::LevelFilter; +use std::ffi::CString; + +fn recover_names<R: Reader<Offset = usize>>( + dwarf: &Dwarf<R>, + debug_info_builder: &mut DebugInfoBuilder, +) { + let mut iter = dwarf.units(); + while let Some(header) = iter.next().unwrap() { + let unit = dwarf.unit(header).unwrap(); + let mut namespace_qualifiers: Vec<(isize, CString)> = vec![]; + let mut entries = unit.entries(); + let mut depth = 0; + + // The first entry in the unit is the header for the unit + if let Ok(Some((delta_depth, _))) = entries.next_dfs() { + depth += delta_depth; + } + + while let Ok(Some((delta_depth, entry))) = entries.next_dfs() { + depth += delta_depth; + assert!(depth >= 0); + + // TODO : Better module/component support + namespace_qualifiers.retain(|&(entry_depth, _)| entry_depth < depth); + + match entry.tag() { + constants::DW_TAG_namespace => { + fn resolve_namespace_name<R: Reader<Offset = usize>>( + dwarf: &Dwarf<R>, + unit: &Unit<R>, + entry: &DebuggingInformationEntry<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 Ok(Some(UnitRef(offset))) = + entry.attr_value(constants::DW_AT_extension) + { + resolve_namespace_name( + dwarf, + unit, + &unit.entry(offset).unwrap(), + namespace_qualifiers, + depth, + ); + } else { + namespace_qualifiers + .push((depth, CString::new("anonymous_namespace").unwrap())); + } + } + + resolve_namespace_name(dwarf, &unit, entry, &mut namespace_qualifiers, depth); + } + constants::DW_TAG_class_type => { + let class_name = get_name(dwarf, &unit, entry).unwrap(); + namespace_qualifiers.push((depth, class_name)); + + debug_info_builder.set_name( + get_uid(&unit, entry), + CString::new( + simplify_str_to_str( + namespace_qualifiers + .iter() + .map(|(_, namespace)| namespace.to_string_lossy().to_string()) + .collect::<Vec<String>>() + .join("::"), + ) + .as_str(), + ) + .unwrap(), + ); + } + constants::DW_TAG_structure_type => { + if let Some(name) = get_name(dwarf, &unit, entry) { + namespace_qualifiers.push((depth, name)) + } else { + namespace_qualifiers + .push((depth, CString::new("anonymous_structure").unwrap())) + } + debug_info_builder.set_name( + get_uid(&unit, entry), + CString::new( + simplify_str_to_str( + namespace_qualifiers + .iter() + .map(|(_, namespace)| namespace.to_string_lossy().to_string()) + .collect::<Vec<String>>() + .join("::"), + ) + .as_str(), + ) + .unwrap(), + ); + } + _ => { + if let Some(name) = get_name(dwarf, &unit, entry) { + debug_info_builder.set_name( + get_uid(&unit, entry), + CString::new( + simplify_str_to_str( + namespace_qualifiers + .iter() + .chain(vec![&(-1, name)].into_iter()) + .map(|(_, namespace)| { + namespace.to_string_lossy().to_string() + }) + .collect::<Vec<String>>() + .join("::"), + ) + .as_str(), + ) + .unwrap(), + ); + } + } + } + } + } +} + +fn parse_unit<R: Reader<Offset = usize>>( + dwarf: &Dwarf<R>, + unit: &Unit<R>, + debug_info_builder: &mut DebugInfoBuilder, +) { + let mut entries = unit.entries(); + + // Really all we care about as we iterate the entries in a given unit is how they modify state (our perception of the file) + // There's a lot of junk we don't care about in DWARF info, so we choose a couple DIEs and mutate state (add functions (which adds the types it uses) and keep track of what namespace we're in) + while let Ok(Some((_, entry))) = entries.next_dfs() { + match entry.tag() { + constants::DW_TAG_subprogram => { + parse_function_entry(dwarf, unit, entry, debug_info_builder) + } + constants::DW_TAG_variable => { + parse_data_variable(dwarf, unit, entry, debug_info_builder) + } + _ => (), + } + } +} + +fn parse_dwarf(view: &BinaryView) -> DebugInfoBuilder { + // Determine if this is a DWO + // TODO : Make this more robust...some DWOs follow non-DWO conventions + let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view); + + // Figure out if it's the given view or the raw view that has the dwarf info in it + let raw_view = &view.raw_view().unwrap(); + let view = if is_dwo_dwarf(view) || is_non_dwo_dwarf(view) { + view + } else { + raw_view + }; + + // gimli setup + let endian = get_endian(view); + let section_reader = create_section_reader(view, endian, dwo_file); + let mut dwarf = Dwarf::load(§ion_reader).unwrap(); + if dwo_file { + dwarf.file_type = DwarfFileType::Dwo; + } + + // Create debug info builder and recover name mapping first + // Since DWARF is stored as a tree with arbitrary implicit edges among leaves, + // it is not possible to correctly track namespaces while you're parsing "in order" without backtracking, + // so we just do it up front + let mut debug_info_builder = DebugInfoBuilder::new(); + recover_names(&dwarf, &mut debug_info_builder); + + // Parse all the compilation units + let mut iter = dwarf.units(); + while let Some(header) = iter.next().unwrap() { + parse_unit( + &dwarf, + &dwarf.unit(header).unwrap(), + &mut debug_info_builder, + ); + } + + debug_info_builder +} + +struct DWARFParser; + +impl CustomDebugInfoParser for DWARFParser { + fn is_valid(&self, view: &BinaryView) -> bool { + dwarfreader::is_valid(view) + } + + fn parse_info( + &self, + debug_info: &mut DebugInfo, + _: &BinaryView, + debug_file: &BinaryView, + _: Box<dyn Fn(usize, usize) -> Result<(), ()>>, + ) -> bool { + // Parse dwarf info in raw view or from a separate file + parse_dwarf(debug_file).commit_info(debug_info); + true + } +} + +#[no_mangle] +pub extern "C" fn CorePluginInit() -> bool { + logger::init(LevelFilter::Debug).unwrap(); + + DebugInfoParser::register("DWARF", DWARFParser {}); + true +} |
