diff options
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/helpers.rs')
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/helpers.rs | 600 |
1 files changed, 0 insertions, 600 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/helpers.rs b/rust/examples/dwarf/dwarf_import/src/helpers.rs deleted file mode 100644 index 6aba99d4..00000000 --- a/rust/examples/dwarf/dwarf_import/src/helpers.rs +++ /dev/null @@ -1,600 +0,0 @@ -// Copyright 2021-2024 Vector 35 Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::ffi::OsStr; -use std::path::{Path, PathBuf}; -use std::{ - collections::HashMap, - ops::Deref, - sync::mpsc, - str::FromStr -}; - -use crate::{DebugInfoBuilderContext, ReaderType}; -use binaryninja::binaryview::BinaryViewBase; -use binaryninja::filemetadata::FileMetadata; -use binaryninja::Endianness; -use binaryninja::{binaryview::{BinaryView, BinaryViewExt}, downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider}, rc::Ref, settings::Settings}; -use gimli::Dwarf; -use gimli::{ - constants, Attribute, AttributeValue, - AttributeValue::{DebugInfoRef, DebugInfoRefSup, UnitRef}, - DebuggingInformationEntry, Operation, Unit, UnitOffset, UnitSectionOffset, -}; - -use log::warn; - -pub(crate) fn get_uid<R: ReaderType>( - dwarf: &Dwarf<R>, - unit: &Unit<R>, - entry: &DebuggingInformationEntry<R>, -) -> usize { - // We set a large gap between supplementary and main entries - let adj = dwarf.sup().map_or(0, |_| 0x1000000000000000); - let entry_offset = match entry.offset().to_unit_section_offset(unit) { - UnitSectionOffset::DebugInfoOffset(o) => o.0, - UnitSectionOffset::DebugTypesOffset(o) => o.0, - }; - entry_offset + adj -} - -//////////////////////////////////// -// DIE attr convenience functions - -pub(crate) enum DieReference<'a, R: ReaderType> { - UnitAndOffset((&'a Dwarf<R>, &'a Unit<R>, UnitOffset)), - Err, -} - -pub(crate) fn get_attr_die<'a, R: ReaderType>( - dwarf: &'a Dwarf<R>, - unit: &'a Unit<R>, - entry: &DebuggingInformationEntry<R>, - debug_info_builder_context: &'a DebugInfoBuilderContext<R>, - attr: constants::DwAt, -) -> Option<DieReference<'a, R>> { - match entry.attr_value(attr) { - Ok(Some(UnitRef(offset))) => Some(DieReference::UnitAndOffset((dwarf, unit, offset))), - Ok(Some(DebugInfoRef(offset))) => { - if dwarf.sup().is_some() { - 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((dwarf, source_unit, new_offset))); - } - } - } - else { - // This could either have no supplementary file because it is one or because it just doesn't have one - // operate on supplementary file if dwarf is a supplementary file, else self - - // It's possible this is a reference in the supplementary file to itself - for source_unit in debug_info_builder_context.sup_units() { - if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { - return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset))); - } - } - - // ... or it just doesn't have a supplementary file - 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((dwarf, source_unit, new_offset))); - } - } - } - - None - }, - Ok(Some(DebugInfoRefSup(offset))) => { - for source_unit in debug_info_builder_context.sup_units() { - if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { - return Some(DieReference::UnitAndOffset((dwarf.sup().unwrap(), source_unit, new_offset))); - } - } - warn!("Failed to fetch DIE. Supplementary debug information may be incomplete."); - None - }, - _ => None, - } -} - -pub(crate) fn resolve_specification<'a, R: ReaderType>( - dwarf: &'a Dwarf<R>, - unit: &'a Unit<R>, - entry: &DebuggingInformationEntry<R>, - debug_info_builder_context: &'a DebugInfoBuilderContext<R>, -) -> DieReference<'a, R> { - if let Some(die_reference) = get_attr_die( - dwarf, - unit, - entry, - debug_info_builder_context, - constants::DW_AT_specification, - ) { - match die_reference { - DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { - if let Ok(entry) = entry_unit.entry(entry_offset) { - resolve_specification(dwarf, entry_unit, &entry, debug_info_builder_context) - } else { - warn!("Failed to fetch DIE for attr DW_AT_specification. Debug information may be incomplete."); - DieReference::Err - } - } - DieReference::Err => DieReference::Err, - } - } else if let Some(die_reference) = get_attr_die( - dwarf, - unit, - entry, - debug_info_builder_context, - constants::DW_AT_abstract_origin, - ) { - match die_reference { - DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { - if entry_offset == entry.offset() && unit.header.offset() == entry_unit.header.offset() { - warn!("DWARF information is invalid (infinite abstract origin reference cycle). Debug information may be incomplete."); - DieReference::Err - } else if let Ok(new_entry) = entry_unit.entry(entry_offset) { - resolve_specification(dwarf, entry_unit, &new_entry, debug_info_builder_context) - } else { - warn!("Failed to fetch DIE for attr DW_AT_abstract_origin. Debug information may be incomplete."); - DieReference::Err - } - } - DieReference::Err => DieReference::Err, - } - } else { - DieReference::UnitAndOffset((dwarf, unit, entry.offset())) - } -} - -// Get name from DIE, or referenced dependencies -pub(crate) fn get_name<R: ReaderType>( - dwarf: &Dwarf<R>, - unit: &Unit<R>, - entry: &DebuggingInformationEntry<R>, - debug_info_builder_context: &DebugInfoBuilderContext<R>, -) -> Option<String> { - match resolve_specification(dwarf, unit, entry, debug_info_builder_context) { - DieReference::UnitAndOffset((dwarf, 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.clone()) - { - if let Ok(attr_string) = attr_string.to_string() { - return Some(attr_string.to_string()); - } - } - else if let Some(dwarf) = &dwarf.sup { - if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val) - { - if let Ok(attr_string) = attr_string.to_string() { - return Some(attr_string.to_string()); - } - } - } - } - - // 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(names.join("::")); - // } - // } - // } - None - } - DieReference::Err => None, - } -} - -// Get raw name from DIE, or referenced dependencies -pub(crate) fn get_raw_name<R: ReaderType>( - dwarf: &Dwarf<R>, - unit: &Unit<R>, - entry: &DebuggingInformationEntry<R>, -) -> Option<String> { - 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.clone()) - { - if let Ok(attr_string) = attr_string.to_string() { - return Some(attr_string.to_string()); - } - } - else if let Some(dwarf) = dwarf.sup() { - if let Ok(attr_string) = dwarf.attr_string(unit, attr_val) - { - if let Ok(attr_string) = attr_string.to_string() { - return Some(attr_string.to_string()); - } - } - } - } - None -} - -// Get the size of an object as a usize -pub(crate) fn get_size_as_usize<R: ReaderType>( - 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) { - get_attr_as_usize(attr).map(|attr_value| attr_value / 8) - } else { - None - } -} - -// Get the size of an object as a u64 -pub(crate) fn get_size_as_u64<R: ReaderType>( - 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) { - get_attr_as_u64(&attr).map(|attr_value| attr_value / 8) - } else { - None - } -} - -// Get the size of a subrange as a u64 -pub(crate) fn get_subrange_size<R: ReaderType>( - 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) { - get_attr_as_u64(&attr).unwrap_or(0) - } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_lower_bound) { - get_attr_as_u64(&attr).map_or(0, |v| v + 1) - } else { - 0 - } -} - -// Get the start address of a function -pub(crate) fn get_start_address<R: ReaderType>( - dwarf: &Dwarf<R>, - unit: &Unit<R>, - entry: &DebuggingInformationEntry<R>, -) -> Option<u64> { - if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_low_pc) { - match 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) - { - 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(range)) = ranges.next() { - return Some(range.begin); - } - } - } - return None; - } else { - None - } -} - -// Get an attribute value as a u64 if it can be coerced -pub(crate) fn get_attr_as_u64<R: ReaderType>(attr: &Attribute<R>) -> Option<u64> { - if let Some(value) = attr.udata_value() { - Some(value) - } else if let Some(value) = attr.sdata_value() { - Some(value as u64) - } else if let AttributeValue::Block(mut data) = attr.value() { - match data.len() { - 1 => data.read_u8().map(u64::from).ok(), - 2 => data.read_u16().map(u64::from).ok(), - 4 => data.read_u32().map(u64::from).ok(), - 8 => data.read_u64().ok(), - _ => None - } - } else { - None - } -} - -// Get an attribute value as a usize if it can be coerced -pub(crate) fn get_attr_as_usize<R: ReaderType>(attr: Attribute<R>) -> Option<usize> { - if let Some(value) = attr.u8_value() { - Some(value.into()) - } else if let Some(value) = attr.u16_value() { - Some(value.into()) - } else if let Some(value) = attr.udata_value() { - Some(value as usize) - } else { - attr.sdata_value().map(|value| value as 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: ReaderType>( - 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), - Ok(Operation::UnsignedConstant { value }) => Some(value), - Ok(Operation::Address { address: 0 }) => None, - Ok(Operation::Address { address }) => Some(address), - _ => None - } - } else { - None - } -} - - -pub(crate) fn get_build_id(view: &BinaryView) -> Result<String, String> { - let mut build_id: Option<String> = None; - - if let Ok(raw_view) = view.raw_view() { - if let Ok(build_id_section) = raw_view.section_by_name(".note.gnu.build-id") { - // Name size - 4 bytes - // Desc size - 4 bytes - // Type - 4 bytes - // Name - n bytes - // Desc - n bytes - let build_id_bytes = raw_view.read_vec(build_id_section.start(), build_id_section.len()); - if build_id_bytes.len() < 12 { - return Err("Build id section must be at least 12 bytes".to_string()); - } - - let name_len: u32; - let desc_len: u32; - let note_type: u32; - match raw_view.default_endianness() { - Endianness::LittleEndian => { - name_len = u32::from_le_bytes(build_id_bytes[0..4].try_into().unwrap()); - desc_len = u32::from_le_bytes(build_id_bytes[4..8].try_into().unwrap()); - note_type = u32::from_le_bytes(build_id_bytes[8..12].try_into().unwrap()); - }, - Endianness::BigEndian => { - name_len = u32::from_be_bytes(build_id_bytes[0..4].try_into().unwrap()); - desc_len = u32::from_be_bytes(build_id_bytes[4..8].try_into().unwrap()); - note_type = u32::from_be_bytes(build_id_bytes[8..12].try_into().unwrap()); - } - }; - - if note_type != 3 { - return Err(format!("Build id section has wrong type: {}", note_type)); - } - - let expected_len = (12 + name_len + desc_len) as usize; - - if build_id_bytes.len() < expected_len { - return Err(format!("Build id section not expected length: expected {}, got {}", expected_len, build_id_bytes.len())); - } - - let desc: &[u8] = &build_id_bytes[(12+name_len as usize)..expected_len]; - build_id = Some(desc.iter().map(|b| format!("{:02x}", b)).collect()); - } - } - - if let Some(x) = build_id { - Ok(x) - } - else { - Err("Failed to get build id".to_string()) - } -} - - -pub(crate) fn download_debug_info(build_id: &String, view: &BinaryView) -> Result<Ref<BinaryView>, String> { - let settings = Settings::new(""); - - let debug_server_urls = settings.get_string_list("network.debuginfodServers", Some(view), None); - - for debug_server_url in debug_server_urls.iter() { - let artifact_url = format!("{}/buildid/{}/debuginfo", debug_server_url, build_id); - - // Download from remote - let (tx, rx) = mpsc::channel(); - let write = move |data: &[u8]| -> usize { - if let Ok(_) = tx.send(Vec::from(data)) { - data.len() - } else { - 0 - } - }; - - let dp = DownloadProvider::try_default().map_err(|_| "No default download provider")?; - let mut inst = dp - .create_instance() - .map_err(|_| "Couldn't create download instance")?; - let result = inst - .perform_custom_request( - "GET", - artifact_url, - HashMap::<String, String>::new(), - DownloadInstanceInputOutputCallbacks { - read: None, - write: Some(Box::new(write)), - progress: None, - }, - ) - .map_err(|e| e.to_string())?; - if result.status_code != 200 { - continue; - } - - let mut expected_length = None; - for (k, v) in result.headers.iter() { - if k.to_lowercase() == "content-length" { - expected_length = Some(usize::from_str(v).map_err(|e| e.to_string())?); - } - } - - let mut data = vec![]; - while let Ok(packet) = rx.try_recv() { - data.extend(packet.into_iter()); - } - - if let Some(length) = expected_length { - if data.len() != length { - return Err(format!( - "Bad length: expected {} got {}", - length, - data.len() - )); - } - } - - let options = "{\"analysis.debugInfo.internal\": false}"; - let bv = BinaryView::from_data(FileMetadata::new().deref(), &data) - .map_err(|_| "Unable to create binary view from downloaded data".to_string())?; - - return binaryninja::load_view(bv.deref(), false, Some(options)) - .ok_or("Unable to load binary view from downloaded data".to_string()); - } - return Err("Could not find a server with debug info for this file".to_string()); -} - - -pub(crate) fn find_local_debug_file_for_build_id(build_id: &String, view: &BinaryView) -> Option<String> { - let settings = Settings::new(""); - let debug_dirs_enabled = settings.get_bool("analysis.debugInfo.enableDebugDirectories", Some(view), None); - - if !debug_dirs_enabled { - return None; - } - - let debug_info_paths = settings.get_string_list("analysis.debugInfo.debugDirectories", Some(view), None); - - if debug_info_paths.is_empty() { - return None - } - - for debug_info_path in debug_info_paths.into_iter() { - if let Ok(path) = PathBuf::from_str(&debug_info_path.to_string()) - { - let elf_path = path - .join(&build_id[..2]) - .join(&build_id[2..]) - .join("elf"); - - let debug_ext_path = path - .join(&build_id[..2]) - .join(format!("{}.debug", &build_id[2..])); - - let final_path = if debug_ext_path.exists() { - debug_ext_path - } - else if elf_path.exists() { - elf_path - } - else { - // No paths exist in this dir, try the next one - continue; - }; - return final_path - .to_str() - .and_then(|x| Some(x.to_string())); - } - } - None -} - - -pub(crate) fn load_debug_info_for_build_id(build_id: &String, view: &BinaryView) -> (Option<Ref<BinaryView>>, bool) { - if let Some(debug_file_path) = find_local_debug_file_for_build_id(build_id, view) { - return - ( - binaryninja::load_with_options( - debug_file_path, - false, - Some("{\"analysis.debugInfo.internal\": false}") - ), - false - ); - } - else if Settings::new("").get_bool("network.enableDebuginfod", Some(view), None) { - return ( - download_debug_info(build_id, view).ok(), - true - ); - } - (None, false) -} - - -pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option<String> { - let settings = Settings::new(""); - let load_sibling_debug = settings.get_bool("analysis.debugInfo.loadSiblingDebugFiles", Some(view), None); - - if !load_sibling_debug { - return None; - } - - let full_file_path = view.file().filename().to_string(); - - let debug_file = PathBuf::from(format!("{}.debug", full_file_path)); - let dsym_folder = PathBuf::from(format!("{}.dSYM", full_file_path)); - if debug_file.exists() && debug_file.is_file() { - return Some(debug_file.to_string_lossy().to_string()); - } - - if dsym_folder.exists() && dsym_folder.is_dir() { - let filename = Path::new(&full_file_path) - .file_name() - .unwrap_or(OsStr::new("")); - - let dsym_file = dsym_folder - .join("Contents/Resources/DWARF/") - .join(filename); // TODO: should this just pull any file out? Can there be multiple files? - if dsym_file.exists() { - return Some(dsym_file.to_string_lossy().to_string()); - } - } - - None -} - - -pub(crate) fn load_sibling_debug_file(view: &BinaryView) -> (Option<Ref<BinaryView>>, bool) { - let Some(debug_file) = find_sibling_debug_file(view) else { - return (None, false); - }; - - let load_settings = match view.default_platform() { - Some(plat) => format!("{{\"analysis.debugInfo.internal\": false, \"loader.platform\": \"{}\"}}", plat.name()), - None => "{\"analysis.debugInfo.internal\": false}".to_string() - }; - - ( - binaryninja::load_with_options( - debug_file, - false, - Some(load_settings) - ), - false - ) -} |
