summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyleMiles <krm504@nyu.edu>2023-04-18 12:38:09 -0400
committerKyleMiles <krm504@nyu.edu>2023-07-10 12:58:35 -0400
commit6325884782a47e0c89154cf7ce0952368e25ea2a (patch)
treeaacb435dc9e386035b75471715abcc912ce18697
parent068a2ff42ef4e15e67d2904166e53a51420b65a1 (diff)
DWARF Import DebugInfo Plugin
Resolves #3206
-rw-r--r--.gitignore2
-rw-r--r--docs/guide/settings.md2
-rw-r--r--rust/Cargo.toml4
-rw-r--r--rust/examples/dwarf/dwarf_import/CMakeLists.txt88
-rw-r--r--rust/examples/dwarf/dwarf_import/Cargo.toml14
-rw-r--r--rust/examples/dwarf/dwarf_import/src/die_handlers.rs369
-rw-r--r--rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs263
-rw-r--r--rust/examples/dwarf/dwarf_import/src/functions.rs74
-rw-r--r--rust/examples/dwarf/dwarf_import/src/helpers.rs192
-rw-r--r--rust/examples/dwarf/dwarf_import/src/lib.rs252
-rw-r--r--rust/examples/dwarf/dwarf_import/src/types.rs367
-rw-r--r--rust/examples/dwarf/dwarfdump/Cargo.toml (renamed from rust/examples/dwarfdump/Cargo.toml)5
-rw-r--r--rust/examples/dwarf/dwarfdump/readme.md (renamed from rust/examples/dwarfdump/readme.md)0
-rw-r--r--rust/examples/dwarf/dwarfdump/src/lib.rs (renamed from rust/examples/dwarfdump/src/lib.rs)91
-rw-r--r--rust/examples/dwarf/shared/Cargo.toml9
-rw-r--r--rust/examples/dwarf/shared/src/lib.rs300
-rw-r--r--rust/src/lib.rs1
-rw-r--r--rust/src/string.rs6
-rw-r--r--rust/src/templatesimplifier.rs15
19 files changed, 1966 insertions, 88 deletions
diff --git a/.gitignore b/.gitignore
index 05cb602a..55a0920a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -83,6 +83,8 @@ rust/binaryninjacore-sys/Cargo.lock
rust/binaryninjacore-sys/target/
rust/examples/*/Cargo.lock
rust/examples/*/target/
+rust/examples/dwarf/*/target/
+rust/examples/dwarf/*/Cargo.lock
# Debugger docs
docs/img/debugger
diff --git a/docs/guide/settings.md b/docs/guide/settings.md
index ba882e9e..51893113 100644
--- a/docs/guide/settings.md
+++ b/docs/guide/settings.md
@@ -126,6 +126,8 @@ All settings are uniquely identified with an identifier string. Identifiers are
|corePlugins|Mach-O View|Enable the built-in Mach-O view module.|`boolean`|`True`|[`SettingsUserScope`]|<a id='corePlugins.view.macho'>corePlugins.view.macho</a>|
|corePlugins|PE/COFF View|Enable the built-in PE/COFF view module.|`boolean`|`True`|[`SettingsUserScope`]|<a id='corePlugins.view.pe'>corePlugins.view.pe</a>|
|corePlugins|Objective-C|Enable the built-in Objective-C plugin.|`boolean`|`True`|[`SettingsUserScope`]|<a id='corePlugins.workflows.objc'>corePlugins.workflows.objc</a>|
+|corePlugins|DWARF DebugInfo Parser|Enable the built-in DWARF export plugin.|`boolean`|`False`|[`SettingsUserScope`]|<a id='corePlugins.dwarfExport'>corePlugins.dwarfExport</a>|
+|corePlugins|DWARF DebugInfo Parser|Enable the built-in DWARF debug info parser.|`boolean`|`False`|[`SettingsUserScope`]|<a id='corePlugins.dwarfImport'>corePlugins.dwarfImport</a>|
|debugger|Update the analysis aggressively|Whether to aggressively update the memory cache and analysis. If the target has self-modifying code, turning this on makes sure every function is re-analyzed every time the target stops, which gives the most accurate analysis. However, for large binaries with lots of functions, this may cause performance issues.|`boolean`|`False`|[`SettingsUserScope`]|<a id='debugger.aggressiveAnalysisUpdate'>debugger.aggressiveAnalysisUpdate</a>|
|debugger|Confirm on first launch|Asks the user to confirm the operation when the target is launched for the first time.|`boolean`|`True`|[`SettingsUserScope`]|<a id='debugger.confirmFirstLaunch'>debugger.confirmFirstLaunch</a>|
|debugger|Safe Mode|When enabled, this prevents the debugger from launching any file.|`boolean`|`False`|[`SettingsUserScope`]|<a id='debugger.safeMode'>debugger.safeMode</a>|
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 09058847..5bd61635 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -19,7 +19,9 @@ members = [
"examples/basic_script",
"examples/decompile",
"examples/dwarf/dwarf_export",
- "examples/dwarfdump",
+ "examples/dwarf/dwarf_import",
+ "examples/dwarf/dwarfdump",
+ "examples/dwarf/shared",
"examples/flowgraph",
"examples/minidump",
"examples/template"
diff --git a/rust/examples/dwarf/dwarf_import/CMakeLists.txt b/rust/examples/dwarf/dwarf_import/CMakeLists.txt
new file mode 100644
index 00000000..f84920a8
--- /dev/null
+++ b/rust/examples/dwarf/dwarf_import/CMakeLists.txt
@@ -0,0 +1,88 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(dwarf_import)
+
+file(GLOB PLUGIN_SOURCES
+ ${PROJECT_SOURCE_DIR}/Cargo.toml
+ ${PROJECT_SOURCE_DIR}/src/*.rs)
+
+file(GLOB API_SOURCES
+ ${PROJECT_SOURCE_DIR}/../../../binaryninjacore.h
+ ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/build.rs
+ ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/Cargo.toml
+ ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/src/*
+ ${PROJECT_SOURCE_DIR}/../../Cargo.toml
+ ${PROJECT_SOURCE_DIR}/../../src/*.rs)
+
+if(CMAKE_BUILD_TYPE MATCHES Debug)
+ set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug)
+ set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target)
+else()
+ set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/release)
+ set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --release)
+endif()
+
+set(OUTPUT_FILE ${CMAKE_STATIC_LIBRARY_PREFIX}dwarf_import${CMAKE_SHARED_LIBRARY_SUFFIX})
+set(PLUGIN_PATH ${TARGET_DIR}/${OUTPUT_FILE})
+
+add_custom_target(dwarf_import ALL DEPENDS ${PLUGIN_PATH})
+add_dependencies(dwarf_import binaryninjaapi)
+find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
+if(CARGO_API_VERSION)
+ set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_API_VERSION} cargo build)
+else()
+ set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_STABLE_VERSION} cargo build)
+endif()
+
+if(APPLE)
+ if(UNIVERSAL)
+ if(CMAKE_BUILD_TYPE MATCHES Debug)
+ set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE})
+ set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE})
+ else()
+ set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE})
+ set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE})
+ endif()
+
+ add_custom_command(
+ OUTPUT ${PLUGIN_PATH}
+ COMMAND ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
+ ${RUSTUP_COMMAND} --target=aarch64-apple-darwin ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
+ ${RUSTUP_COMMAND} --target=x86_64-apple-darwin ${CARGO_OPTS}
+ COMMAND mkdir -p ${TARGET_DIR}
+ COMMAND lipo -create ${AARCH64_LIB_PATH} ${X86_64_LIB_PATH} -output ${PLUGIN_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
+ else()
+ if(CMAKE_BUILD_TYPE MATCHES Debug)
+ set(LIB_PATH ${PROJECT_BINARY_DIR}/target/debug/${OUTPUT_FILE})
+ else()
+ set(LIB_PATH ${PROJECT_BINARY_DIR}/target/release/${OUTPUT_FILE})
+ endif()
+
+ add_custom_command(
+ OUTPUT ${PLUGIN_PATH}
+ COMMAND ${CMAKE_COMMAND} -E env MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
+ endif()
+elseif(WIN32)
+ add_custom_command(
+ OUTPUT ${PLUGIN_PATH}
+ COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
+else()
+ add_custom_command(
+ OUTPUT ${PLUGIN_PATH}
+ COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
+endif()
diff --git a/rust/examples/dwarf/dwarf_import/Cargo.toml b/rust/examples/dwarf/dwarf_import/Cargo.toml
new file mode 100644
index 00000000..322a4dd6
--- /dev/null
+++ b/rust/examples/dwarf/dwarf_import/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "dwarf_import"
+version = "0.1.0"
+authors = ["KyleMiles <kyle@vector35.com>"]
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+dwarfreader = { path = "../shared/" }
+binaryninja = { path = "../../../" }
+gimli = "0.26.1"
+log = "0.4.17"
diff --git a/rust/examples/dwarf/dwarf_import/src/die_handlers.rs b/rust/examples/dwarf/dwarf_import/src/die_handlers.rs
new file mode 100644
index 00000000..bc7624b3
--- /dev/null
+++ b/rust/examples/dwarf/dwarf_import/src/die_handlers.rs
@@ -0,0 +1,369 @@
+// 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.
+
+use crate::dwarfdebuginfo::{DebugInfoBuilder, TypeUID};
+use crate::helpers::*;
+use crate::types::get_type;
+
+use binaryninja::{
+ rc::*,
+ types::{EnumerationBuilder, FunctionParameter, ReferenceType, Type, TypeBuilder},
+};
+
+use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Dwarf, Reader, Unit};
+
+use std::ffi::CString;
+
+pub fn handle_base_type<R: Reader<Offset = usize>>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<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_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 = get_name(dwarf, unit, entry).expect("DW_TAG_base does not have name attribute");
+
+ // TODO : Handle other size specifiers (bits, offset, high_pc?, etc)
+ let size = get_size_as_usize(entry).expect("DW_TAG_base does not have size attribute");
+
+ match entry.attr_value(constants::DW_AT_encoding) {
+ // TODO : Need more binaries to see what's going on
+ Ok(Some(Encoding(encoding))) => {
+ match encoding {
+ constants::DW_ATE_address => None,
+ constants::DW_ATE_boolean => Some(Type::bool()),
+ constants::DW_ATE_complex_float => None,
+ constants::DW_ATE_float => Some(Type::named_float(size, name)),
+ constants::DW_ATE_signed => Some(Type::named_int(size, true, name)),
+ constants::DW_ATE_signed_char => Some(Type::named_int(size, true, name)),
+ constants::DW_ATE_unsigned => Some(Type::named_int(size, false, name)),
+ constants::DW_ATE_unsigned_char => Some(Type::named_int(size, false, name)),
+ constants::DW_ATE_imaginary_float => None,
+ constants::DW_ATE_packed_decimal => None,
+ constants::DW_ATE_numeric_string => None,
+ 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_UTF => Some(Type::named_int(size, false, name)), // TODO : Verify
+ constants::DW_ATE_UCS => None,
+ constants::DW_ATE_ASCII => None, // Some sort of array?
+ constants::DW_ATE_lo_user => None,
+ constants::DW_ATE_hi_user => None,
+ _ => None, // Anything else is invalid at time of writing (gimli v0.23.0)
+ }
+ }
+ _ => None,
+ }
+}
+
+pub fn handle_enum<R: Reader<Offset = usize>>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+) -> Option<Ref<Type>> {
+ // All base types have:
+ // DW_AT_byte_size
+ // *DW_AT_name
+ // *DW_AT_enum_class
+ // *DW_AT_type
+ // ?DW_AT_abstract_origin
+ // ?DW_AT_accessibility
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_bit_size
+ // ?DW_AT_bit_stride
+ // ?DW_AT_byte_stride
+ // ?DW_AT_data_location
+ // ?DW_AT_declaration
+ // ?DW_AT_description
+ // ?DW_AT_sibling
+ // ?DW_AT_signature
+ // ?DW_AT_specification
+ // ?DW_AT_start_scope
+ // ?DW_AT_visibility
+ // * = Optional
+
+ // Children of enumeration_types are enumerators which contain:
+ // DW_AT_name
+ // DW_AT_const_value
+ // *DW_AT_description
+
+ let enumeration_builder = EnumerationBuilder::new();
+
+ 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_enumerator {
+ let name =
+ get_name(dwarf, unit, child.entry()) // TODO : Might need to recover the full name here
+ .expect("DW_TAG_enumeration_type does not have name attribute");
+ let value = get_attr_as_u64(
+ &child
+ .entry()
+ .attr(constants::DW_AT_const_value)
+ .unwrap()
+ .unwrap(),
+ )
+ .unwrap();
+
+ enumeration_builder.insert(name, value);
+ }
+ }
+
+ Some(Type::enumeration(
+ &enumeration_builder.finalize(),
+ get_size_as_usize(entry).unwrap_or(8),
+ false,
+ ))
+}
+
+pub fn handle_typedef(
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+ typedef_name: CString,
+) -> (Option<Ref<Type>>, bool) {
+ // All base types have:
+ // DW_AT_name
+ // *DW_AT_type
+ // * = Optional
+
+ // 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 {
+ return (Some(t), true);
+ }
+ }
+ }
+
+ // 5.3: "typedef represents a declaration of the type that is not also a definition"
+ (None, false)
+}
+
+pub fn handle_pointer<R: Reader<Offset = usize>>(
+ entry: &DebuggingInformationEntry<R>,
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+ reference_type: ReferenceType,
+) -> Option<Ref<Type>> {
+ // All pointer types have:
+ // DW_AT_type
+ // *DW_AT_byte_size
+ // ?DW_AT_name
+ // ?DW_AT_address
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // * = Optional
+
+ if let Some(pointer_size) = get_size_as_usize(entry) {
+ if let Some(entry_type_offset) = entry_type {
+ let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
+ Some(Type::pointer_of_width(
+ parent_type.as_ref(),
+ pointer_size,
+ false,
+ false,
+ Some(reference_type),
+ ))
+ } else {
+ Some(Type::pointer_of_width(
+ Type::void().as_ref(),
+ pointer_size,
+ false,
+ false,
+ Some(reference_type),
+ ))
+ }
+ } else {
+ None
+ }
+}
+
+pub fn handle_array<R: Reader<Offset = usize>>(
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+) -> Option<Ref<Type>> {
+ // All array types have:
+ // DW_AT_type
+ // *DW_AT_name
+ // *DW_AT_ordering
+ // *DW_AT_byte_stride or DW_AT_bit_stride
+ // *DW_AT_byte_size or DW_AT_bit_size
+ // *DW_AT_allocated
+ // *DW_AT_associated and
+ // *DW_AT_data_location
+ // * = Optional
+ // For multidimensional arrays, DW_TAG_subrange_type or DW_TAG_enumeration_type
+
+ if let Some(entry_type_offset) = entry_type {
+ let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
+
+ let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
+ let mut children = tree.root().unwrap().children();
+
+ // TODO : This is currently applying the size in reverse order
+ let mut result_type: Option<Ref<Type>> = None;
+ while let Ok(Some(child)) = children.next() {
+ if let Some(inner_type) = result_type {
+ result_type = Some(Type::array(
+ inner_type.as_ref(),
+ get_subrange_size(child.entry()),
+ ));
+ } else {
+ result_type = Some(Type::array(
+ parent_type.as_ref(),
+ get_subrange_size(child.entry()),
+ ));
+ }
+ }
+
+ result_type.map_or(Some(Type::array(parent_type.as_ref(), 0)), Some)
+ } else {
+ None
+ }
+}
+
+pub fn handle_function<R: Reader<Offset = usize>>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+) -> Option<Ref<Type>> {
+ // All subroutine types have:
+ // *DW_AT_name
+ // *DW_AT_type (if not provided, void)
+ // *DW_AT_prototyped
+ // ?DW_AT_abstract_origin
+ // ?DW_AT_accessibility
+ // ?DW_AT_address_class
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // ?DW_AT_declaration
+ // ?DW_AT_description
+ // ?DW_AT_sibling
+ // ?DW_AT_start_scope
+ // ?DW_AT_visibility
+ // * = Optional
+
+ // May have children, including DW_TAG_formal_parameters, which all have:
+ // *DW_AT_type
+ // * = Optional
+ // or is otherwise DW_TAG_unspecified_parameters
+
+ let return_type = match entry_type {
+ Some(entry_type_offset) => {
+ debug_info_builder
+ .get_type(entry_type_offset)
+ .expect("Subroutine return type was not processed")
+ .1
+ }
+ None => Type::void(),
+ };
+
+ let mut parameters: Vec<FunctionParameter<CString>> = vec![];
+ 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),
+ get_name(dwarf, unit, child.entry()),
+ )
+ } {
+ let child_type = debug_info_builder.get_type(child_uid).unwrap().1;
+ parameters.push(FunctionParameter::new(child_type, name, None));
+ }
+ } else if child.entry().tag() == constants::DW_TAG_unspecified_parameters {
+ variable_arguments = true;
+ }
+ }
+
+ Some(Type::function(
+ return_type.as_ref(),
+ &parameters,
+ variable_arguments,
+ ))
+}
+
+pub fn handle_const(
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+) -> Option<Ref<Type>> {
+ // All const types have:
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // ?DW_AT_name
+ // ?DW_AT_sibling
+ // ?DW_AT_type
+
+ if let Some(entry_type_offset) = entry_type {
+ let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
+ Some((*parent_type).to_builder().set_const(true).finalize())
+ } else {
+ Some(TypeBuilder::void().set_const(true).finalize())
+ }
+}
+
+pub fn handle_volatile(
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+) -> Option<Ref<Type>> {
+ // All const types have:
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // ?DW_AT_name
+ // ?DW_AT_sibling
+ // ?DW_AT_type
+
+ if let Some(entry_type_offset) = entry_type {
+ let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
+ Some((*parent_type).to_builder().set_volatile(true).finalize())
+ } else {
+ 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
new file mode 100644
index 00000000..0cb9ede2
--- /dev/null
+++ b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
@@ -0,0 +1,263 @@
+// 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.
+
+use crate::helpers::{get_uid, resolve_specification};
+
+use binaryninja::{
+ debuginfo::{DebugFunctionInfo, DebugInfo},
+ rc::*,
+ templatesimplifier::simplify_str_to_str,
+ types::{Conf, FunctionParameter, Type},
+};
+
+use gimli::{DebuggingInformationEntry, Reader, Unit};
+
+use std::{
+ collections::{hash_map::Values, HashMap},
+ ffi::CString,
+ hash::Hash,
+};
+
+pub(crate) type TypeUID = usize;
+
+/////////////////////////
+// FunctionInfoBuilder
+
+// 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)>>,
+}
+
+impl FunctionInfoBuilder {
+ pub fn update(
+ &mut self,
+ full_name: Option<CString>,
+ raw_name: Option<CString>,
+ return_type: Option<TypeUID>,
+ address: Option<u64>,
+ parameters: Vec<Option<(CString, TypeUID)>>,
+ ) {
+ if full_name.is_some() {
+ self.full_name = full_name;
+ }
+
+ if raw_name.is_some() {
+ self.raw_name = raw_name;
+ }
+
+ if return_type.is_some() {
+ self.return_type = return_type;
+ }
+
+ if address.is_some() {
+ self.address = address;
+ }
+
+ 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);
+ }
+ }
+ }
+}
+
+//////////////////////
+// DebugInfoBuilder
+
+// TODO : Don't make this pub...fix the value thing
+pub(crate) struct DebugType {
+ name: CString,
+ t: Ref<Type>,
+ commit: bool,
+}
+
+// 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 {
+ functions: Vec<FunctionInfoBuilder>,
+ types: HashMap<TypeUID, DebugType>,
+ data_variables: HashMap<u64, (Option<CString>, TypeUID)>,
+ names: HashMap<TypeUID, CString>,
+}
+
+impl DebugInfoBuilder {
+ pub fn new() -> Self {
+ DebugInfoBuilder {
+ functions: vec![],
+ types: HashMap::new(),
+ data_variables: HashMap::new(),
+ names: HashMap::new(),
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn insert_function(
+ &mut self,
+ full_name: Option<CString>,
+ raw_name: Option<CString>,
+ return_type: Option<TypeUID>,
+ 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)
+ }) {
+ function.update(full_name, raw_name, return_type, address, parameters);
+ } else {
+ self.functions.push(FunctionInfoBuilder {
+ full_name,
+ raw_name,
+ return_type,
+ address,
+ parameters,
+ });
+ }
+ }
+
+ pub fn functions(&self) -> &[FunctionInfoBuilder] {
+ &self.functions
+ }
+
+ pub(crate) fn types(&self) -> Values<'_, TypeUID, DebugType> {
+ self.types.values()
+ }
+
+ pub fn add_type(&mut self, type_uid: TypeUID, name: CString, t: Ref<Type>, commit: bool) {
+ assert!(self
+ .types
+ .insert(type_uid, DebugType { name, t, commit })
+ .is_none());
+ }
+
+ // TODO : Non-copy?
+ pub 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 {
+ self.types.get(&type_uid).is_some()
+ }
+
+ pub fn add_data_variable(&mut self, address: u64, name: Option<CString>, type_uid: TypeUID) {
+ assert!(self
+ .data_variables
+ .insert(address, (name, type_uid))
+ .is_none());
+ }
+
+ 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,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+ ) -> Option<CString> {
+ self.names
+ .get(&get_uid(
+ unit,
+ &unit.entry(resolve_specification(unit, entry)).unwrap(),
+ ))
+ .cloned()
+ }
+
+ fn commit_types(&self, debug_info: &mut DebugInfo) {
+ for debug_type in self.types() {
+ if debug_type.commit {
+ debug_info.add_type(debug_type.name.clone(), debug_type.t.as_ref());
+ }
+ }
+ }
+
+ // TODO : Consume data?
+ fn commit_data_variables(&self, debug_info: &mut DebugInfo) {
+ for (&address, (name, type_uid)) in &self.data_variables {
+ assert!(debug_info.add_data_variable(
+ address,
+ &self.get_type(*type_uid).unwrap().1,
+ name.clone()
+ ));
+ }
+ }
+
+ 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),
+ };
+
+ let parameters: Vec<FunctionParameter<CString>> = function
+ .parameters
+ .iter()
+ .filter_map(|parameter| {
+ if let Some((name, uid)) = parameter {
+ Some(FunctionParameter::new(
+ self.get_type(*uid).unwrap().1,
+ name.clone(),
+ None,
+ ))
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ // 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, &parameters, 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.raw_name.clone(),
+ Some(function_type),
+ function.address,
+ platform,
+ ));
+ }
+ }
+
+ pub 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
new file mode 100644
index 00000000..20858917
--- /dev/null
+++ b/rust/examples/dwarf/dwarf_import/src/functions.rs
@@ -0,0 +1,74 @@
+// 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.
+
+use crate::dwarfdebuginfo::{DebugInfoBuilder, TypeUID};
+use crate::helpers::*;
+use crate::types::get_type;
+
+use gimli::{constants, DebuggingInformationEntry, Dwarf, 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: &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();
+
+ let mut result = vec![];
+ let mut children = root.children();
+ while let Some(child) = children.next().unwrap() {
+ match child.entry().tag() {
+ constants::DW_TAG_formal_parameter => {
+ let name = get_name(dwarf, unit, child.entry());
+ let type_ = get_type(dwarf, unit, child.entry(), debug_info_builder);
+ if let (Some(parameter_name), Some(parameter_type)) = (name, type_) {
+ result.push(Some((parameter_name, parameter_type)));
+ } else {
+ result.push(None)
+ }
+ }
+ constants::DW_TAG_unspecified_parameters => (),
+ _ => (),
+ }
+ }
+ result
+ }
+}
+
+pub fn parse_function_entry<R: Reader<Offset = usize>>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<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(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);
+
+ 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
new file mode 100644
index 00000000..fa9d011d
--- /dev/null
+++ b/rust/examples/dwarf/dwarf_import/src/helpers.rs
@@ -0,0 +1,192 @@
+// 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.
+
+use gimli::{
+ constants, Attribute, AttributeValue, AttributeValue::UnitRef, DebuggingInformationEntry,
+ Dwarf, Operation, Reader, Unit, UnitOffset, UnitSectionOffset,
+};
+
+use std::ffi::CString;
+
+pub(crate) fn get_uid<R: Reader<Offset = usize>>(
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+) -> usize {
+ match entry.offset().to_unit_section_offset(unit) {
+ UnitSectionOffset::DebugInfoOffset(o) => o.0,
+ UnitSectionOffset::DebugTypesOffset(o) => o.0,
+ }
+}
+
+////////////////////////////////////
+// DIE attr convenience functions
+
+pub fn resolve_specification<R: Reader<Offset = usize>>(
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+) -> UnitOffset {
+ if let Ok(Some(UnitRef(offset))) = entry.attr_value(constants::DW_AT_specification) {
+ resolve_specification(unit, &unit.entry(offset).unwrap())
+ } else if let Ok(Some(UnitRef(offset))) = entry.attr_value(constants::DW_AT_abstract_origin) {
+ resolve_specification(unit, &unit.entry(offset).unwrap())
+ } else {
+ 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>,
+) -> Option<CString> {
+ let entry = unit.entry(resolve_specification(unit, entry)).unwrap();
+
+ if let Ok(Some(attr_val)) = entry.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
+}
+
+// Get raw name from DIE, or referenced dependencies
+pub(crate) fn get_raw_name<R: Reader>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<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) = attr_string.to_string() {
+ Some(CString::new(attr_string.to_string()).unwrap())
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
+
+// Get the size of an object as a usize
+pub(crate) fn get_size_as_usize<R: Reader>(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: Reader>(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: Reader>(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: Reader>(
+ 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: Reader>(attr: &Attribute<R>) -> Option<u64> {
+ 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)
+ } else {
+ attr.sdata_value().map(|value| value as 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> {
+ 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: Reader>(unit: &Unit<R>, attr: Attribute<R>) -> Option<u64> {
+ if let AttributeValue::Exprloc(mut expression) = attr.value() {
+ match Operation::parse(&mut expression.0, unit.encoding()).unwrap() {
+ Operation::PlusConstant { value } => Some(value),
+ Operation::UnsignedConstant { value } => Some(value),
+ Operation::Address { address: 0 } => None,
+ Operation::Address { address } => Some(address),
+ _ => None,
+ }
+ } else {
+ None
+ }
+}
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(&section_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
+}
diff --git a/rust/examples/dwarf/dwarf_import/src/types.rs b/rust/examples/dwarf/dwarf_import/src/types.rs
new file mode 100644
index 00000000..2d61d64e
--- /dev/null
+++ b/rust/examples/dwarf/dwarf_import/src/types.rs
@@ -0,0 +1,367 @@
+// 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.
+
+use crate::die_handlers::*;
+use crate::dwarfdebuginfo::{DebugInfoBuilder, TypeUID};
+use crate::helpers::*;
+
+use binaryninja::{
+ rc::*,
+ types::{
+ MemberAccess, MemberScope, ReferenceType, StructureBuilder, StructureType, Type, TypeClass,
+ },
+};
+
+use gimli::{constants, AttributeValue::UnitRef, DebuggingInformationEntry, Dwarf, Reader, Unit};
+
+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: &mut DebugInfoBuilder,
+) {
+ let full_name = debug_info_builder.get_name(unit, entry);
+ let type_uid = get_type(dwarf, unit, entry, debug_info_builder);
+
+ let address = if let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) {
+ get_expr_value(unit, attr)
+ } else {
+ None
+ };
+
+ if let (Some(address), Some(type_uid)) = (address, type_uid) {
+ debug_info_builder.add_data_variable(address, full_name, type_uid);
+ }
+}
+
+fn do_structure_parse<R: Reader<Offset = usize>>(
+ structure_type: StructureType,
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+ debug_info_builder: &mut DebugInfoBuilder,
+) -> Option<usize> {
+ // All struct, union, and class types will have:
+ // *DW_AT_name
+ // *DW_AT_byte_size or *DW_AT_bit_size
+ // *DW_AT_declaration
+ // *DW_AT_signature
+ // *DW_AT_specification
+ // ?DW_AT_abstract_origin
+ // ?DW_AT_accessibility
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // ?DW_AT_description
+ // ?DW_AT_start_scope
+ // ?DW_AT_visibility
+ // * = Optional
+
+ // Structure/Class/Union _Children_ consist of:
+ // Data members:
+ // DW_AT_type
+ // *DW_AT_name
+ // *DW_AT_accessibility (default private for classes, public for everything else)
+ // *DW_AT_mutable
+ // *DW_AT_data_member_location xor *DW_AT_data_bit_offset (otherwise assume zero) <- there are some deprecations for DWARF 4
+ // *DW_AT_byte_size xor DW_AT_bit_size, iff the storage size is different than it usually would be for the given member type
+ // Function members:
+ // *DW_AT_accessibility (default private for classes, public for everything else)
+ // *DW_AT_virtuality (assume false)
+ // If true: DW_AT_vtable_elem_location
+ // *DW_AT_explicit (assume false)
+ // *DW_AT_object_pointer (assume false; for non-static member function; references the formal parameter that has "DW_AT_artificial = true" and represents "self" or "this" (language specified))
+ // *DW_AT_specification
+ // * = Optional
+
+ if let Ok(Some(_)) = entry.attr(constants::DW_AT_declaration) {
+ return None;
+ }
+
+ let full_name = if get_name(dwarf, unit, entry).is_some() {
+ debug_info_builder.get_name(unit, entry)
+ } else {
+ None
+ };
+
+ // Create structure with proper size
+ let size = get_size_as_u64(entry).unwrap_or(0);
+ let mut structure_builder: StructureBuilder = StructureBuilder::new();
+ structure_builder
+ .set_packed(true)
+ .set_width(size)
+ .set_structure_type(structure_type);
+
+ // This reference type will be used by any children to grab while we're still building this type
+ // it will also be how any other types refer to this struct
+ if let Some(full_name) = &full_name {
+ debug_info_builder.add_type(
+ get_uid(unit, entry),
+ full_name.clone(),
+ Type::named_type_from_type(
+ full_name.clone(),
+ &Type::structure(&structure_builder.finalize()),
+ ),
+ false,
+ );
+ }
+
+ // Get all the children and populate
+ 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_member {
+ if let Some(child_type_id) = get_type(dwarf, unit, child.entry(), debug_info_builder) {
+ if let Some((_, child_type)) = debug_info_builder.get_type(child_type_id) {
+ if let Some(child_name) = get_name(dwarf, unit, child.entry()).map_or(
+ if child_type.type_class() == TypeClass::StructureTypeClass {
+ Some(CString::new("").unwrap())
+ } else {
+ None
+ },
+ Some,
+ ) {
+ // TODO : support DW_AT_data_bit_offset for offset as well
+ if let Ok(Some(raw_struct_offset)) =
+ child.entry().attr(constants::DW_AT_data_member_location)
+ {
+ // TODO : Let this fail; don't unwrap_or_default get_expr_value
+ let struct_offset =
+ get_attr_as_u64(&raw_struct_offset).unwrap_or_else(|| {
+ get_expr_value(unit, raw_struct_offset).unwrap_or_default()
+ });
+
+ structure_builder.insert(
+ child_type.as_ref(),
+ child_name,
+ struct_offset,
+ false,
+ MemberAccess::NoAccess, // TODO : Resolve actual scopes, if possible
+ MemberScope::NoScope,
+ );
+ } else {
+ structure_builder.append(
+ child_type.as_ref(),
+ child_name,
+ MemberAccess::NoAccess,
+ MemberScope::NoScope,
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ let finalized_structure = Type::structure(&structure_builder.finalize());
+ if let Some(full_name) = full_name {
+ debug_info_builder.add_type(
+ get_uid(unit, entry) + 1, // TODO : This is super broke (uid + 1 is not guaranteed to be unique)
+ full_name,
+ finalized_structure,
+ true,
+ );
+ } else {
+ debug_info_builder.add_type(
+ get_uid(unit, entry),
+ CString::new(format!("{}", finalized_structure)).unwrap(),
+ finalized_structure,
+ false, // Don't commit anonymous unions (because I think it'll break things)
+ );
+ }
+ Some(get_uid(unit, entry))
+}
+
+// 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: &mut DebugInfoBuilder,
+) -> Option<TypeUID> {
+ // If this node (and thus all its referenced nodes) has already been processed, just return the offset
+ if debug_info_builder.contains_type(get_uid(unit, entry)) {
+ return Some(get_uid(unit, entry));
+ }
+
+ // Don't parse types that are just declarations and not definitions
+ if let Ok(Some(_)) = entry.attr(constants::DW_AT_declaration) {
+ return None;
+ }
+
+ // Passing through typedef was necessary at one point, but it seems that parsing has gotten robust enough not to explode on this....leaving it here just in case though
+ // // Do not trust typedefs; typedefs should be transparent; typedefs mask the base type they refer to, not other typedefs
+ // // This is the DIE that contains the type of the current DIE
+ // let entry_type = if entry.tag() == constants::DW_TAG_typedef {
+ // let mut typedef_type = entry.clone();
+ // while typedef_type.tag() == constants::DW_TAG_typedef {
+ // if let Ok(Some(UnitRef(typedef_type_offset))) =
+ // typedef_type.attr_value(constants::DW_AT_type)
+ // {
+ // typedef_type = unit.entry(typedef_type_offset).unwrap();
+ // } else {
+ // return None;
+ // }
+ // }
+ // get_type(dwarf, unit, &typedef_type, debug_info_builder)
+ // } else
+ let entry_type =
+ if let Ok(Some(UnitRef(entry_type_offset))) = entry.attr_value(constants::DW_AT_type) {
+ // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
+ get_type(
+ dwarf,
+ unit,
+ &unit.entry(entry_type_offset).unwrap(),
+ debug_info_builder,
+ )
+ } else if let Ok(Some(UnitRef(entry_type_offset))) =
+ entry.attr_value(constants::DW_AT_specification)
+ {
+ // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
+ get_type(
+ dwarf,
+ unit,
+ &unit.entry(entry_type_offset).unwrap(),
+ debug_info_builder,
+ )
+ } else if let Ok(Some(UnitRef(entry_type_offset))) =
+ entry.attr_value(constants::DW_AT_abstract_origin)
+ {
+ // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
+ get_type(
+ dwarf,
+ unit,
+ &unit.entry(entry_type_offset).unwrap(),
+ debug_info_builder,
+ )
+ } 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
+ if debug_info_builder.contains_type(get_uid(unit, entry)) {
+ return Some(get_uid(unit, entry));
+ }
+
+ // Collect the required information to create a type and add it to the type map. Also, add the dependencies of this type to the type's typeinfo
+ // 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), false),
+
+ constants::DW_TAG_structure_type => {
+ return do_structure_parse(
+ StructureType::StructStructureType,
+ dwarf,
+ unit,
+ entry,
+ debug_info_builder,
+ )
+ }
+ constants::DW_TAG_class_type => {
+ return do_structure_parse(
+ StructureType::ClassStructureType,
+ dwarf,
+ unit,
+ entry,
+ debug_info_builder,
+ )
+ }
+ constants::DW_TAG_union_type => {
+ return do_structure_parse(
+ StructureType::UnionStructureType,
+ dwarf,
+ unit,
+ entry,
+ debug_info_builder,
+ )
+ }
+
+ // Enum
+ constants::DW_TAG_enumeration_type => (handle_enum(dwarf, unit, entry), true),
+
+ // Basic types
+ constants::DW_TAG_typedef => handle_typedef(
+ debug_info_builder,
+ entry_type,
+ debug_info_builder.get_name(unit, entry).unwrap(),
+ ),
+ constants::DW_TAG_pointer_type => (
+ handle_pointer(
+ entry,
+ debug_info_builder,
+ entry_type,
+ ReferenceType::PointerReferenceType,
+ ),
+ false,
+ ),
+ constants::DW_TAG_reference_type => (
+ handle_pointer(
+ entry,
+ debug_info_builder,
+ entry_type,
+ ReferenceType::ReferenceReferenceType,
+ ),
+ false,
+ ),
+ constants::DW_TAG_rvalue_reference_type => (
+ handle_pointer(
+ entry,
+ debug_info_builder,
+ entry_type,
+ ReferenceType::RValueReferenceType,
+ ),
+ false,
+ ),
+ constants::DW_TAG_array_type => (
+ handle_array(unit, entry, debug_info_builder, entry_type),
+ false,
+ ),
+
+ // Strange Types
+ constants::DW_TAG_unspecified_type => (Some(Type::void()), false), // TODO : Maybe true here
+ constants::DW_TAG_subroutine_type => (
+ handle_function(dwarf, unit, entry, debug_info_builder, entry_type),
+ false,
+ ),
+
+ // Weird types
+ constants::DW_TAG_const_type => (handle_const(debug_info_builder, entry_type), false),
+ constants::DW_TAG_volatile_type => (handle_volatile(debug_info_builder, entry_type), true), // TODO : Maybe false here
+
+ // Pass-through everything else!
+ _ => return entry_type,
+ };
+
+ // 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(unit, entry)
+ } else {
+ None
+ }
+ .unwrap_or_else(|| {
+ commit = false;
+ CString::new(format!("{}", type_def)).unwrap()
+ });
+
+ debug_info_builder.add_type(get_uid(unit, entry), name, type_def, commit);
+ Some(get_uid(unit, entry))
+ } else {
+ None
+ }
+}
diff --git a/rust/examples/dwarfdump/Cargo.toml b/rust/examples/dwarf/dwarfdump/Cargo.toml
index c561c22a..da25a756 100644
--- a/rust/examples/dwarfdump/Cargo.toml
+++ b/rust/examples/dwarf/dwarfdump/Cargo.toml
@@ -8,5 +8,6 @@ edition = "2021"
crate-type = ["cdylib"]
[dependencies]
-binaryninja = {path="../../"}
-gimli = "0.23.0"
+dwarfreader = { path = "../shared/" }
+binaryninja = {path="../../../"}
+gimli = "0.26"
diff --git a/rust/examples/dwarfdump/readme.md b/rust/examples/dwarf/dwarfdump/readme.md
index ae3a193b..ae3a193b 100644
--- a/rust/examples/dwarfdump/readme.md
+++ b/rust/examples/dwarf/dwarfdump/readme.md
diff --git a/rust/examples/dwarfdump/src/lib.rs b/rust/examples/dwarf/dwarfdump/src/lib.rs
index 9b3cb376..71af87c4 100644
--- a/rust/examples/dwarfdump/src/lib.rs
+++ b/rust/examples/dwarf/dwarfdump/src/lib.rs
@@ -15,11 +15,11 @@
use binaryninja::{
binaryview::{BinaryView, BinaryViewExt},
command::{register, Command},
- databuffer::DataBuffer,
disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents},
flowgraph::{BranchType, EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption},
string::BnString,
};
+use dwarfreader::is_valid;
use gimli::{
AttributeValue::{Encoding, Flag, UnitRef},
@@ -27,18 +27,13 @@ use gimli::{
DebuggingInformationEntry,
Dwarf,
EntriesTreeNode,
- Error,
- LittleEndian,
Reader,
ReaderOffset,
- SectionId,
Unit,
UnitSectionOffset,
};
-use std::{fmt, ops::Deref, sync::Arc};
-
-static PADDING: [&str; 23] = [
+static PADDING: [&'static str; 23] = [
"",
" ",
" ",
@@ -64,48 +59,6 @@ static PADDING: [&str; 23] = [
" ",
];
-// TODO : This isn't comprehensive
-fn is_valid(view: &BinaryView) -> bool {
- view.section_by_name(".debug_info").is_ok()
- || (view.parent_view().is_ok()
- && view
- .parent_view()
- .unwrap()
- .section_by_name(".debug_info")
- .is_ok())
-}
-
-// gimli::read::load only takes structures containing &[u8]'s, but we need to keep the data buffer alive until it's done using that
-// I don't think that the `Arc` is needed, but I couldn't figure out how else to implement the traits properly without it
-#[derive(Clone)]
-struct DataBufferWrapper(Arc<DataBuffer>);
-
-impl DataBufferWrapper {
- fn new(buf: DataBuffer) -> Self {
- DataBufferWrapper(Arc::new(buf))
- }
-}
-
-impl Deref for DataBufferWrapper {
- type Target = [u8];
- fn deref(&self) -> &[u8] {
- self.0.get_data()
- }
-}
-
-impl fmt::Debug for DataBufferWrapper {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("DataBufferWrapper")
- .field("0", &"I'm too lazy to do this right")
- .finish()
- }
-}
-
-unsafe impl gimli::StableDeref for DataBufferWrapper {}
-unsafe impl gimli::CloneStableDeref for DataBufferWrapper {}
-
-type CustomReader<Endian> = gimli::EndianReader<Endian, DataBufferWrapper>;
-
// TODO : This is very much not comprehensive: see https://github.com/gimli-rs/gimli/blob/master/examples/dwarfdump.rs
fn get_info_string<R: Reader>(
view: &BinaryView,
@@ -236,7 +189,7 @@ fn get_info_string<R: Reader>(
let value_string = format!("{}", value);
attr_line.push(InstructionTextToken::new(
BnString::new(value_string),
- InstructionTextTokenContents::Integer(value),
+ InstructionTextTokenContents::Integer(value.into()),
));
} else if let Some(value) = attr.sdata_value() {
let value_string = format!("{}", value);
@@ -297,40 +250,6 @@ fn dump_dwarf(bv: &BinaryView) {
bv.parent_view().unwrap()
};
- // TODO : Accommodate Endianity
- let get_section_data_little =
- |section_id: SectionId| -> Result<CustomReader<LittleEndian>, Error> {
- if let Ok(section) = view.section_by_name(section_id.name()) {
- let offset = section.start();
- let len = section.len();
- if len == 0 {
- Ok(CustomReader::new(
- DataBufferWrapper::new(DataBuffer::default()),
- LittleEndian,
- ))
- } else if let Ok(read_buffer) = view.read_buffer(offset, len) {
- Ok(CustomReader::new(
- DataBufferWrapper::new(read_buffer),
- LittleEndian,
- ))
- } else {
- Err(Error::Io)
- }
- } else {
- Ok(CustomReader::new(
- DataBufferWrapper::new(DataBuffer::default()),
- LittleEndian,
- ))
- }
- };
-
- let empty_reader_little = |_: SectionId| -> Result<CustomReader<LittleEndian>, Error> {
- Ok(CustomReader::new(
- DataBufferWrapper::new(DataBuffer::default()),
- LittleEndian,
- ))
- };
-
let graph = FlowGraph::new();
graph.set_option(FlowGraphOption::FlowGraphUsesBlockHighlights, true);
graph.set_option(FlowGraphOption::FlowGraphUsesInstructionHighlights, true);
@@ -339,7 +258,9 @@ fn dump_dwarf(bv: &BinaryView) {
graph_root.set_lines(vec!["Graph Root"]);
graph.append(&graph_root);
- let dwarf = Dwarf::load(&get_section_data_little, &empty_reader_little).unwrap();
+ let endian = dwarfreader::get_endian(bv);
+ let section_reader = dwarfreader::create_section_reader(bv, endian, false);
+ let dwarf = Dwarf::load(&section_reader).unwrap();
let mut iter = dwarf.units();
while let Some(header) = iter.next().unwrap() {
diff --git a/rust/examples/dwarf/shared/Cargo.toml b/rust/examples/dwarf/shared/Cargo.toml
new file mode 100644
index 00000000..121e9a91
--- /dev/null
+++ b/rust/examples/dwarf/shared/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "dwarfreader"
+version = "0.1.0"
+authors = ["Kyle Martin <kyle@vector35.com>"]
+edition = "2021"
+
+[dependencies]
+binaryninja = {path="../../../"}
+gimli = "0.26"
diff --git a/rust/examples/dwarf/shared/src/lib.rs b/rust/examples/dwarf/shared/src/lib.rs
new file mode 100644
index 00000000..cb3e9dde
--- /dev/null
+++ b/rust/examples/dwarf/shared/src/lib.rs
@@ -0,0 +1,300 @@
+// Copyright 2021-2022 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 gimli::{Endianity, Error, Reader, ReaderOffsetId, RunTimeEndian, SectionId};
+
+use binaryninja::binaryninjacore_sys::*;
+
+use binaryninja::{
+ binaryview::{BinaryView, BinaryViewBase, BinaryViewExt},
+ databuffer::DataBuffer,
+ Endianness,
+};
+
+use std::borrow::Cow;
+use std::convert::TryInto;
+use std::ffi::CString;
+use std::{fmt, str};
+
+//////////////////////
+// Dwarf Validation
+
+pub fn is_non_dwo_dwarf(view: &BinaryView) -> bool {
+ view.section_by_name(".debug_info").is_ok()
+}
+
+pub fn is_dwo_dwarf(view: &BinaryView) -> bool {
+ view.section_by_name(".debug_info.dwo").is_ok()
+}
+
+pub fn is_raw_non_dwo_dwarf(view: &BinaryView) -> bool {
+ if let Ok(raw_view) = view.raw_view() {
+ raw_view.section_by_name(".debug_info").is_ok()
+ } else {
+ false
+ }
+}
+
+pub fn is_raw_dwo_dwarf(view: &BinaryView) -> bool {
+ if let Ok(raw_view) = view.raw_view() {
+ raw_view.section_by_name(".debug_info.dwo").is_ok()
+ } else {
+ false
+ }
+}
+
+pub fn is_valid(view: &BinaryView) -> bool {
+ is_non_dwo_dwarf(view)
+ || is_raw_non_dwo_dwarf(view)
+ || is_dwo_dwarf(view)
+ || is_raw_dwo_dwarf(view)
+}
+
+pub fn get_endian(view: &BinaryView) -> RunTimeEndian {
+ match view.default_endianness() {
+ Endianness::LittleEndian => RunTimeEndian::Little,
+ Endianness::BigEndian => RunTimeEndian::Big,
+ }
+}
+
+#[derive(Clone)]
+pub struct DWARFReader<Endian: Endianity> {
+ data: Vec<u8>,
+ endian: Endian,
+ data_offset: usize,
+ section_offset: usize,
+}
+
+impl<Endian: Endianity> DWARFReader<Endian> {
+ pub fn new(data: Vec<u8>, endian: Endian) -> Self {
+ Self {
+ data,
+ endian,
+ data_offset: 0,
+ section_offset: 0,
+ }
+ }
+}
+
+impl<Endian: Endianity> fmt::Debug for DWARFReader<Endian> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let data = if self.data.len() < 6 {
+ self.data.clone()
+ } else {
+ let mut vec = vec![0; 6];
+ vec.clone_from_slice(&self.data[0..6]);
+ vec
+ };
+ f.debug_struct("DWARFReader")
+ .field("data", &data)
+ .field("endian", &self.endian)
+ .field("data_offset", &self.data_offset)
+ .field("section_offset", &self.section_offset)
+ .finish()
+ }
+}
+
+impl<Endian: Endianity> Reader for DWARFReader<Endian> {
+ type Endian = Endian;
+ type Offset = usize;
+
+ fn endian(&self) -> Endian {
+ self.endian
+ }
+
+ fn len(&self) -> usize {
+ self.data.len() - self.data_offset
+ }
+
+ fn empty(&mut self) {
+ self.data.clear();
+ self.data_offset = 0;
+ }
+
+ fn truncate(&mut self, len: usize) -> Result<(), Error> {
+ self.data.truncate(self.data_offset + len);
+ Ok(())
+ }
+
+ fn offset_from(&self, base: &Self) -> usize {
+ (self.section_offset + self.data_offset) - (base.section_offset + base.data_offset)
+ }
+
+ fn offset_id(&self) -> ReaderOffsetId {
+ ReaderOffsetId(self.data_offset.try_into().unwrap())
+ }
+
+ fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<usize> {
+ Some(id.0.try_into().unwrap())
+ }
+
+ fn find(&self, byte: u8) -> Result<usize, Error> {
+ match self
+ .data
+ .iter()
+ .skip(self.data_offset)
+ .position(|&b| b == byte)
+ {
+ Some(value) => Ok(value),
+ _ => Err(Error::UnexpectedEof(self.offset_id())),
+ }
+ }
+
+ fn skip(&mut self, len: usize) -> Result<(), Error> {
+ if self.data.len() < self.data_offset + len {
+ Err(Error::UnexpectedEof(self.offset_id()))
+ } else {
+ self.data_offset += len;
+ Ok(())
+ }
+ }
+
+ fn split(&mut self, len: usize) -> Result<Self, Error> {
+ if self.data.len() < self.data_offset + len {
+ unreachable!();
+ // Err(Error::UnexpectedEof(self.offset_id()))
+ } else {
+ self.data_offset += len;
+
+ Ok(Self {
+ data: self.data[(self.data_offset - len)..self.data_offset].to_vec(),
+ endian: self.endian,
+ data_offset: 0,
+ section_offset: self.section_offset + self.data_offset - len,
+ })
+ }
+ }
+
+ fn to_slice(&self) -> Result<Cow<'_, [u8]>, Error> {
+ Ok(self.data[self.data_offset..].into())
+ }
+
+ fn to_string(&self) -> Result<Cow<'_, str>, Error> {
+ Ok(str::from_utf8(&self.data[self.data_offset..])
+ .unwrap()
+ .into())
+ }
+
+ fn to_string_lossy(&self) -> Result<Cow<'_, str>, Error> {
+ Ok(str::from_utf8(&self.data[self.data_offset..])
+ .unwrap()
+ .into())
+ }
+
+ fn read_slice(&mut self, buf: &mut [u8]) -> Result<(), Error> {
+ if self.len() >= 4 {
+ let mut vec = vec![0; 4];
+ vec.clone_from_slice(&self.data[self.data_offset..self.data_offset + 4]);
+ }
+
+ if self.data.len() < self.data_offset + buf.len() {
+ Err(Error::UnexpectedEof(self.offset_id()))
+ } else {
+ for b in buf {
+ *b = self.data[self.data_offset];
+ self.data_offset += 1;
+ }
+
+ Ok(())
+ }
+ }
+}
+
+pub fn create_section_reader<'a, Endian: 'a + Endianity>(
+ view: &'a BinaryView,
+ endian: Endian,
+ dwo_file: bool,
+) -> Box<dyn Fn(SectionId) -> Result<DWARFReader<Endian>, Error> + 'a> {
+ Box::new(move |section_id: SectionId| {
+ let section_name = if dwo_file && section_id.dwo_name().is_some() {
+ section_id.dwo_name().unwrap()
+ } else {
+ section_id.name()
+ };
+
+ if let Ok(section) = view.section_by_name(section_name) {
+ // TODO : This is kinda broke....should add rust wrappers for some of this
+ if let Some(symbol) = view
+ .symbols()
+ .iter()
+ .find(|symbol| symbol.full_name().as_str() == "__elf_section_headers")
+ {
+ if let Some(data_var) = view
+ .data_variables()
+ .iter()
+ .find(|var| var.address == symbol.address())
+ {
+ // TODO : This should eventually be wrapped by some DataView sorta thingy thing, like how python does it
+ let data_type = data_var.type_with_confidence().contents;
+ let data = view.read_vec(data_var.address, data_type.width() as usize);
+ let element_type = data_type.element_type().unwrap().contents;
+
+ // TODO : broke af?
+ if let Some(current_section_header) = data
+ .chunks(element_type.width() as usize)
+ .find(|section_header| {
+ endian.read_u64(&section_header[24..32]) == section.start()
+ })
+ {
+ if (endian.read_u64(&current_section_header[8..16]) & 2048) != 0 {
+ // Get section, trim header, decompress, return
+ let offset = section.start() + 24; // TODO : Super broke AF
+ let len = section.len() - 24;
+
+ if let Ok(buffer) = view.read_buffer(offset, len) {
+ // Incredibly broke as fuck
+ use std::ptr;
+ let transform_name =
+ CString::new("Zlib").unwrap().into_bytes_with_nul();
+ let transform = unsafe {
+ BNGetTransformByName(transform_name.as_ptr() as *mut _)
+ };
+
+ // Omega broke
+ let raw_buf: *mut BNDataBuffer =
+ unsafe { BNCreateDataBuffer(ptr::null_mut(), 0) };
+ if unsafe {
+ BNDecode(
+ transform,
+ std::mem::transmute(buffer),
+ raw_buf,
+ ptr::null_mut(),
+ 0,
+ )
+ } {
+ let output_buffer: DataBuffer =
+ unsafe { std::mem::transmute(raw_buf) };
+
+ return Ok(DWARFReader::new(
+ output_buffer.get_data().into(),
+ endian,
+ ));
+ }
+ }
+ }
+ }
+ }
+ }
+ let offset = section.start();
+ let len = section.len();
+ if len == 0 {
+ Ok(DWARFReader::new(vec![], endian))
+ } else {
+ Ok(DWARFReader::new(view.read_vec(offset, len), endian))
+ }
+ } else {
+ Ok(DWARFReader::new(vec![], endian))
+ }
+ })
+}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index f5d96592..7039a69d 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -162,6 +162,7 @@ pub mod settings;
pub mod string;
pub mod symbol;
pub mod tags;
+pub mod templatesimplifier;
pub mod types;
use std::path::PathBuf;
diff --git a/rust/src/string.rs b/rust/src/string.rs
index 21972b34..5bfddcee 100644
--- a/rust/src/string.rs
+++ b/rust/src/string.rs
@@ -33,6 +33,8 @@ pub(crate) fn raw_to_string(ptr: *const raw::c_char) -> Option<String> {
}
}
+/// These are strings that the core will both allocate and free.
+/// We just have a reference to these strings and want to be able use them, but aren't responsible for cleanup
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(C)]
pub struct BnStr {
@@ -95,6 +97,10 @@ pub struct BnString {
/// Received from a variety of core function calls, and
/// must be used when giving strings to the core from many
/// core-invoked callbacks.
+///
+/// These are strings we're responsible for freeing, such as
+/// strings allocated by the core and given to us through the API
+/// and then forgotten about by the core.
impl BnString {
pub fn new<S: BnStrCompatible>(s: S) -> Self {
use binaryninjacore_sys::BNAllocString;
diff --git a/rust/src/templatesimplifier.rs b/rust/src/templatesimplifier.rs
new file mode 100644
index 00000000..2f7bba92
--- /dev/null
+++ b/rust/src/templatesimplifier.rs
@@ -0,0 +1,15 @@
+use crate::{
+ string::{BnStrCompatible, BnString},
+ types::{QualifiedName},
+};
+use binaryninjacore_sys::{BNRustSimplifyStrToFQN, BNRustSimplifyStrToStr};
+
+pub fn simplify_str_to_str<S: BnStrCompatible>(input: S) -> BnString {
+ let name = input.into_bytes_with_nul();
+ unsafe { BnString::from_raw(BNRustSimplifyStrToStr(name.as_ref().as_ptr() as *mut _)) }
+}
+
+pub fn simplify_str_to_fqn<S: BnStrCompatible>(input: S, simplify: bool) -> QualifiedName {
+ let name = input.into_bytes_with_nul();
+ unsafe { QualifiedName(BNRustSimplifyStrToFQN(name.as_ref().as_ptr() as *mut _, simplify)) }
+}