summaryrefslogtreecommitdiff
path: root/rust/examples/dwarf/dwarf_import/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/lib.rs')
-rw-r--r--rust/examples/dwarf/dwarf_import/src/lib.rs723
1 files changed, 0 insertions, 723 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/lib.rs b/rust/examples/dwarf/dwarf_import/src/lib.rs
deleted file mode 100644
index 6050593f..00000000
--- a/rust/examples/dwarf/dwarf_import/src/lib.rs
+++ /dev/null
@@ -1,723 +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.
-
-mod die_handlers;
-mod dwarfdebuginfo;
-mod functions;
-mod helpers;
-mod types;
-
-use std::collections::HashMap;
-
-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_variable;
-
-use binaryninja::binaryview::BinaryViewBase;
-use binaryninja::{
- binaryview::{BinaryView, BinaryViewExt},
- debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
- settings::Settings,
- templatesimplifier::simplify_str_to_str,
-};
-use dwarfreader::{
- create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf,
-};
-
-use functions::parse_lexical_block;
-use gimli::{constants, CfaRule, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section, SectionId, Unit, UnwindContext, UnwindSection};
-
-use helpers::{get_build_id, load_debug_info_for_build_id};
-use log::{debug, error, warn};
-use binaryninja::logger::Logger;
-
-trait ReaderType: Reader<Offset = usize> {}
-impl<T: Reader<Offset = usize>> ReaderType for T {}
-
-
-pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<(), ()> + 'b>(
- original_fn: F,
- subpart: usize,
- subpart_weights: &[f64],
-) -> Box<dyn Fn(usize, usize) -> Result<(), ()> + 'b> {
- // Normalize weights
- let weight_sum: f64 = subpart_weights.iter().sum();
- if weight_sum < 0.0001 {
- return Box::new(|_, _| Ok(()));
- }
-
- // Keep a running count of weights for the start
- let mut subpart_starts = vec![];
- let mut start = 0f64;
- for w in subpart_weights {
- subpart_starts.push(start);
- start += *w;
- }
-
- let subpart_start = subpart_starts[subpart] / weight_sum;
- let weight = subpart_weights[subpart] / weight_sum;
-
- Box::new(move |cur: usize, max: usize| {
- // Just use a large number for easy divisibility
- let steps = 1000000f64;
- let subpart_size = steps * weight;
- let subpart_progress = ((cur as f64) / (max as f64)) * subpart_size;
-
- original_fn(
- (subpart_start * steps + subpart_progress) as usize,
- steps as usize,
- )
- })
-}
-
-
-fn calculate_total_unit_bytes<R: ReaderType>(
- dwarf: &Dwarf<R>,
- debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
-)
-{
- let mut iter = dwarf.units();
- let mut total_size: usize = 0;
- while let Ok(Some(header)) = iter.next()
- {
- total_size += header.length_including_self();
- }
- debug_info_builder_context.total_unit_size_bytes = total_size;
-}
-
-fn recover_names<R: ReaderType>(
- dwarf: &Dwarf<R>,
- debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
- progress: &dyn Fn(usize, usize) -> Result<(), ()>,
-) -> bool {
-
- let mut res = true;
- if let Some(sup_dwarf) = dwarf.sup() {
- res = recover_names_internal(sup_dwarf, debug_info_builder_context, progress);
- }
-
- if res {
- res = recover_names_internal(dwarf, debug_info_builder_context, progress);
- }
- res
-}
-
-fn recover_names_internal<R: ReaderType>(
- dwarf: &Dwarf<R>,
- debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
- progress: &dyn Fn(usize, usize) -> Result<(), ()>,
-) -> bool {
- let mut iter = dwarf.units();
- let mut current_byte_offset: usize = 0;
- while let Ok(Some(header)) = iter.next() {
- let unit_offset = header.offset().as_debug_info_offset().unwrap().0;
- let unit = dwarf.unit(header).unwrap();
- let mut namespace_qualifiers: Vec<(isize, String)> = 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;
- debug_info_builder_context.total_die_count += 1;
- }
-
- while let Ok(Some((delta_depth, entry))) = entries.next_dfs() {
- debug_info_builder_context.total_die_count += 1;
-
- if (*progress)(current_byte_offset, debug_info_builder_context.total_unit_size_bytes).is_err() {
- return false; // Parsing canceled
- };
- current_byte_offset = unit_offset + entry.offset().0;
-
- depth += delta_depth;
- if depth < 0 {
- error!("DWARF information is seriously malformed. Aborting parsing.");
- return false;
- }
-
- // 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: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<R>,
- namespace_qualifiers: &mut Vec<(isize, String)>,
- depth: isize,
- ) {
- if let Some(namespace_qualifier) =
- get_name(dwarf, unit, entry, debug_info_builder_context)
- {
- namespace_qualifiers.push((depth, namespace_qualifier));
- } else if let Some(die_reference) = get_attr_die(
- dwarf,
- unit,
- entry,
- debug_info_builder_context,
- constants::DW_AT_extension,
- ) {
- match die_reference {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
- resolve_namespace_name(
- dwarf,
- entry_unit,
- &entry_unit.entry(entry_offset).unwrap(),
- debug_info_builder_context,
- namespace_qualifiers,
- depth,
- )
- }
- DieReference::Err => {
- warn!(
- "Failed to fetch DIE when resolving namespace. Debug information may be incomplete."
- );
- }
- }
- } else {
- namespace_qualifiers.push((depth, "anonymous_namespace".to_string()));
- }
- }
-
- resolve_namespace_name(
- dwarf,
- &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, debug_info_builder_context) {
- namespace_qualifiers.push((depth, name))
- } else {
- namespace_qualifiers.push((
- depth,
- match entry.tag() {
- constants::DW_TAG_class_type => "anonymous_class".to_string(),
- constants::DW_TAG_structure_type => {
- "anonymous_structure".to_string()
- }
- constants::DW_TAG_union_type => "anonymous_union".to_string(),
- _ => unreachable!(),
- },
- ))
- }
- debug_info_builder_context.set_name(
- get_uid(dwarf, &unit, entry),
- simplify_str_to_str(
- namespace_qualifiers
- .iter()
- .map(|(_, namespace)| namespace.to_owned())
- .collect::<Vec<String>>()
- .join("::"),
- )
- .to_string(),
- );
- }
- 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_context) {
- debug_info_builder_context.set_name(
- get_uid(dwarf, &unit, entry),
- simplify_str_to_str(
- namespace_qualifiers
- .iter()
- .chain(vec![&(-1, name)].into_iter())
- .map(|(_, namespace)| namespace.to_owned())
- .collect::<Vec<String>>()
- .join("::"),
- )
- .to_string(),
- );
- }
- }
- _ => {
- if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
- debug_info_builder_context.set_name(get_uid(dwarf, &unit, entry), name);
- }
- }
- }
- }
- }
-
- true
-}
-
-fn parse_unit<R: ReaderType>(
- 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,
-) {
- let mut entries = unit.entries();
-
- let mut current_depth: isize = 0;
- let mut functions_by_depth: Vec<(Option<usize>, isize)> = vec![];
- let mut lexical_blocks_by_depth: Vec<(iset::IntervalSet<u64>, isize)> = vec![];
-
- // 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((depth_delta, entry))) = entries.next_dfs() {
- *current_die_number += 1;
- if (*progress)(
- *current_die_number,
- debug_info_builder_context.total_die_count,
- )
- .is_err()
- {
- return; // Parsing canceled
- }
-
- current_depth = current_depth.saturating_add(depth_delta);
-
- loop {
- if let Some((_fn_idx, depth)) = functions_by_depth.last() {
- if current_depth <= *depth {
- functions_by_depth.pop();
- }
- else {
- break
- }
- }
- else {
- break;
- }
-
- if let Some((_lexical_block, depth)) = lexical_blocks_by_depth.last() {
- if current_depth <= *depth {
- lexical_blocks_by_depth.pop();
- }
- else {
- break
- }
- }
- else {
- break;
- }
- }
-
- match entry.tag() {
- constants::DW_TAG_subprogram => {
- let fn_idx = parse_function_entry(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
- functions_by_depth.push((fn_idx, current_depth));
- },
- constants::DW_TAG_lexical_block => {
- if let Some(block_ranges) = parse_lexical_block(dwarf, unit, entry) {
- lexical_blocks_by_depth.push((block_ranges, current_depth));
- }
- },
- constants::DW_TAG_variable => {
- let current_fn_idx = functions_by_depth.last().and_then(|x| x.0);
- let current_lexical_block = lexical_blocks_by_depth.last().and_then(|x| Some(&x.0));
- parse_variable(dwarf, unit, entry, debug_info_builder_context, debug_info_builder, current_fn_idx, current_lexical_block)
- },
- constants::DW_TAG_class_type |
- constants::DW_TAG_enumeration_type |
- constants::DW_TAG_structure_type |
- constants::DW_TAG_union_type |
- constants::DW_TAG_typedef => {
- // Ensure types are loaded even if they're unused
- types::get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
- },
- _ => (),
- }
- }
-}
-
-fn parse_unwind_section<R: Reader, U: UnwindSection<R>>(
- view: &BinaryView,
- unwind_section: U,
-) -> gimli::Result<iset::IntervalMap<u64, i64>>
-where <U as UnwindSection<R>>::Offset: std::hash::Hash {
- let mut bases = gimli::BaseAddresses::default();
-
- if let Ok(section) = view.section_by_name(".eh_frame_hdr").or(view.section_by_name("__eh_frame_hdr")) {
- bases = bases.set_eh_frame_hdr(section.start());
- }
-
- if let Ok(section) = view.section_by_name(".eh_frame").or(view.section_by_name("__eh_frame")) {
- bases = bases.set_eh_frame(section.start());
- } else if let Ok(section) = view.section_by_name(".debug_frame").or(view.section_by_name("__debug_frame")) {
- bases = bases.set_eh_frame(section.start());
- }
-
- if let Ok(section) = view.section_by_name(".text").or(view.section_by_name("__text")) {
- bases = bases.set_text(section.start());
- }
-
- if let Ok(section) = view.section_by_name(".got").or(view.section_by_name("__got")) {
- bases = bases.set_got(section.start());
- }
-
- let mut cies = HashMap::new();
- let mut cfa_offsets = iset::IntervalMap::new();
-
- let mut entries = unwind_section.entries(&bases);
- let mut unwind_context = UnwindContext::new();
- loop {
- match entries.next()? {
- None => return Ok(cfa_offsets),
- Some(gimli::CieOrFde::Cie(_cie)) => {
- // TODO: do we want to do anything with standalone CIEs?
- }
- Some(gimli::CieOrFde::Fde(partial)) => {
- let fde = match partial.parse(|_, bases, o| {
- cies.entry(o)
- .or_insert_with(|| unwind_section.cie_from_offset(bases, o))
- .clone()
- }) {
- Ok(fde) => fde,
- Err(e) => {
- error!("Failed to parse FDE: {}", e);
- continue;
- }
- };
-
- if fde.len() == 0 {
- // This FDE is a terminator
- return Ok(cfa_offsets);
- }
-
- if fde.initial_address().overflowing_add(fde.len()).1 {
- warn!("FDE at offset {:?} exceeds bounds of memory space! {:#x} + length {:#x}", fde.offset(), fde.initial_address(), fde.len());
- } else {
- // Walk the FDE table rows and store their CFA
- let mut fde_table = fde.rows(&unwind_section, &bases, &mut unwind_context)?;
-
- while let Some(row) = fde_table.next_row()? {
- match row.cfa() {
- CfaRule::RegisterAndOffset {register: _, offset} => {
- // TODO: we should store offsets by register
- if row.start_address() < row.end_address() {
- cfa_offsets.insert(
- row.start_address()..row.end_address(),
- *offset,
- );
- }
- else {
- debug!("Invalid FDE table row addresses: {:#x}..{:#x}", row.start_address(), row.end_address());
- }
- },
- CfaRule::Expression(_) => {
- debug!("Unhandled CFA expression when determining offset");
- }
- };
-
- }
- }
- }
- }
- }
-}
-
-fn get_supplementary_build_id(bv: &BinaryView) -> Option<String> {
- let raw_view = bv.raw_view().ok()?;
- if let Ok(section) = raw_view.section_by_name(".gnu_debugaltlink") {
- let start = section.start();
- let len = section.len();
-
- if len < 20 {
- // Not large enough to hold a build id
- return None;
- }
-
- raw_view
- .read_vec(start, len)
- .splitn(2, |x| *x == 0)
- .last()
- .map(|a| {
- a.iter().map(|b| format!("{:02x}", b)).collect()
- })
- }
- else {
- None
- }
-}
-
-fn parse_dwarf(
- _bv: &BinaryView,
- debug_bv: &BinaryView,
- supplementary_bv: Option<&BinaryView>,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
-) -> Result<DebugInfoBuilder, ()> {
- // TODO: warn if no supplementary file and .gnu_debugaltlink section present
-
- // Determine if this is a DWO
- // TODO : Make this more robust...some DWOs follow non-DWO conventions
-
- // Figure out if it's the given view or the raw view that has the dwarf info in it
- let raw_view = &debug_bv.raw_view()?;
- let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) {
- debug_bv
- } else {
- raw_view
- };
-
- let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view);
-
- // gimli setup
- let endian = get_endian(view);
- let mut section_reader =
- |section_id: SectionId| -> _ { create_section_reader(section_id, view, endian, dwo_file) };
-
- let mut dwarf = match Dwarf::load(&mut section_reader) {
- Ok(x) => x,
- Err(e) => {
- error!("Failed to load DWARF info: {}", e);
- return Err(());
- }
- };
-
- if dwo_file {
- dwarf.file_type = DwarfFileType::Dwo;
- }
- else {
- dwarf.file_type = DwarfFileType::Main;
- }
-
- if let Some(sup_bv) = supplementary_bv {
- let sup_endian = get_endian(sup_bv);
- let sup_dwo_file = is_dwo_dwarf(sup_bv) || is_raw_dwo_dwarf(sup_bv);
- let sup_section_reader =
- |section_id: SectionId| -> _ { create_section_reader(section_id, sup_bv, sup_endian, sup_dwo_file) };
- if let Err(e) = dwarf.load_sup(sup_section_reader) {
- error!("Failed to load supplementary file: {}", e);
- }
- }
-
- let range_data_offsets;
- if view.section_by_name(".eh_frame").is_ok() || view.section_by_name("__eh_frame").is_ok() {
- let eh_frame_endian = get_endian(view);
- let mut eh_frame_section_reader =
- |section_id: SectionId| -> _ { create_section_reader(section_id, view, eh_frame_endian, dwo_file) };
- let mut eh_frame = gimli::EhFrame::load(&mut eh_frame_section_reader).unwrap();
- eh_frame.set_address_size(view.address_size() as u8);
- range_data_offsets = parse_unwind_section(view, eh_frame)
- .map_err(|e| error!("Error parsing .eh_frame: {}", e))?;
- }
- else if view.section_by_name(".debug_frame").is_ok() || view.section_by_name("__debug_frame").is_ok() {
- let debug_frame_endian = get_endian(view);
- let mut debug_frame_section_reader =
- |section_id: SectionId| -> _ { create_section_reader(section_id, view, debug_frame_endian, dwo_file) };
- let mut debug_frame = gimli::DebugFrame::load(&mut debug_frame_section_reader).unwrap();
- debug_frame.set_address_size(view.address_size() as u8);
- range_data_offsets = parse_unwind_section(view, debug_frame)
- .map_err(|e| error!("Error parsing .debug_frame: {}", e))?;
- }
- else {
- range_data_offsets = Default::default();
- }
-
-
- // 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();
- debug_info_builder.set_range_data_offsets(range_data_offsets);
-
- if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, &dwarf) {
- calculate_total_unit_bytes(&dwarf, &mut debug_info_builder_context);
-
- let progress_weights = [0.5, 0.5];
- let name_progress = split_progress(&progress, 0, &progress_weights);
- let parse_progress = split_progress(&progress, 1, &progress_weights);
-
- if !recover_names(&dwarf, &mut debug_info_builder_context, &name_progress)
- || debug_info_builder_context.total_die_count == 0
- {
- return Ok(debug_info_builder);
- }
-
- // Parse all the compilation units
- let mut current_die_number = 0;
-
- for unit in debug_info_builder_context.sup_units() {
- parse_unit(
- dwarf.sup().unwrap(),
- &unit,
- &debug_info_builder_context,
- &mut debug_info_builder,
- &parse_progress,
- &mut current_die_number,
- );
- }
-
- for unit in debug_info_builder_context.units() {
- parse_unit(
- &dwarf,
- &unit,
- &debug_info_builder_context,
- &mut debug_info_builder,
- &parse_progress,
- &mut current_die_number,
- );
- }
- }
-
- Ok(debug_info_builder)
-}
-
-struct DWARFParser;
-
-impl CustomDebugInfoParser for DWARFParser {
- fn is_valid(&self, view: &BinaryView) -> bool {
- if dwarfreader::is_valid(view) || dwarfreader::can_use_debuginfod(view) {
- return true;
- }
- if dwarfreader::has_build_id_section(view) {
- if let Ok(build_id) = get_build_id(view) {
- if helpers::find_local_debug_file_for_build_id(&build_id, view).is_some() {
- return true;
- }
- }
- }
- if helpers::find_sibling_debug_file(view).is_some() {
- return true;
- }
- false
- }
-
- fn parse_info(
- &self,
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- ) -> bool {
- let (external_file, close_external) = if !dwarfreader::is_valid(bv) {
- if let (Some(debug_view), x) = helpers::load_sibling_debug_file(bv) {
- (Some(debug_view), x)
- }
- else if let Ok(build_id) = get_build_id(bv) {
- helpers::load_debug_info_for_build_id(&build_id, bv)
- }
- else {
- (None, false)
- }
- }
- else {
- (None, false)
- };
-
- let sup_bv = get_supplementary_build_id(
- external_file
- .as_deref()
- .unwrap_or(debug_file)
- )
- .and_then(|build_id| {
- load_debug_info_for_build_id(&build_id, bv)
- .0
- .map(|x| x.raw_view().unwrap())
- });
-
- let result = match parse_dwarf(
- bv,
- external_file.as_deref().unwrap_or(debug_file),
- sup_bv.as_deref(),
- progress
- )
- {
- Ok(mut builder) => {
- builder.post_process(bv, debug_info).commit_info(debug_info);
- true
- }
- Err(_) => false,
- };
-
- if let (Some(ext), true) = (external_file, close_external) {
- ext.file().close();
- }
-
- result
- }
-}
-
-#[no_mangle]
-pub extern "C" fn CorePluginInit() -> bool {
- Logger::new("DWARF").init();
-
- let settings = Settings::new("");
-
- settings.register_setting_json(
- "network.enableDebuginfod",
- r#"{
- "title" : "Enable Debuginfod Support",
- "type" : "boolean",
- "default" : false,
- "description" : "Enable using Debuginfod servers to fetch DWARF debug info for files with a .note.gnu.build-id section.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "network.debuginfodServers",
- r#"{
- "title" : "Debuginfod Server URLs",
- "type" : "array",
- "sorted" : true,
- "default" : [],
- "description" : "Servers to use for fetching DWARF debug info for files with a .note.gnu.build-id section.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "analysis.debugInfo.enableDebugDirectories",
- r#"{
- "title" : "Enable Debug File Directories",
- "type" : "boolean",
- "default" : true,
- "description" : "Enable searching local debug directories for DWARF debug info.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "analysis.debugInfo.debugDirectories",
- r#"{
- "title" : "Debug File Directories",
- "type" : "array",
- "sorted" : true,
- "default" : [],
- "description" : "Paths to folder containing DWARF debug info stored by build id.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "analysis.debugInfo.loadSiblingDebugFiles",
- r#"{
- "title" : "Enable Loading of Sibling Debug Files",
- "type" : "boolean",
- "default" : true,
- "description" : "Enable automatic loading of X.debug and X.dSYM files next to a file named X.",
- "ignore" : []
- }"#,
- );
-
- DebugInfoParser::register("DWARF", DWARFParser {});
- true
-}