summaryrefslogtreecommitdiff
path: root/rust/examples/dwarf/dwarf_import/src/helpers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/helpers.rs')
-rw-r--r--rust/examples/dwarf/dwarf_import/src/helpers.rs600
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
- )
-}