From 83d041e4cfd64179f8bbc60752ded78c6f037b85 Mon Sep 17 00:00:00 2001 From: KyleMiles Date: Wed, 10 May 2023 10:47:48 -0400 Subject: Move DWARF Export in to DWARF folder in Rust API examples folder --- rust/examples/dwarf/dwarf_export/CMakeLists.txt | 89 +++ rust/examples/dwarf/dwarf_export/Cargo.toml | 13 + rust/examples/dwarf/dwarf_export/README.md | 1 + rust/examples/dwarf/dwarf_export/build.rs | 68 +++ rust/examples/dwarf/dwarf_export/src/lib.rs | 690 +++++++++++++++++++++++ rust/examples/dwarfexport/CMakeLists.txt | 89 --- rust/examples/dwarfexport/Cargo.toml | 13 - rust/examples/dwarfexport/README.md | 1 - rust/examples/dwarfexport/build.rs | 68 --- rust/examples/dwarfexport/src/lib.rs | 703 ------------------------ 10 files changed, 861 insertions(+), 874 deletions(-) create mode 100644 rust/examples/dwarf/dwarf_export/CMakeLists.txt create mode 100644 rust/examples/dwarf/dwarf_export/Cargo.toml create mode 100644 rust/examples/dwarf/dwarf_export/README.md create mode 100644 rust/examples/dwarf/dwarf_export/build.rs create mode 100644 rust/examples/dwarf/dwarf_export/src/lib.rs delete mode 100644 rust/examples/dwarfexport/CMakeLists.txt delete mode 100644 rust/examples/dwarfexport/Cargo.toml delete mode 100644 rust/examples/dwarfexport/README.md delete mode 100644 rust/examples/dwarfexport/build.rs delete mode 100644 rust/examples/dwarfexport/src/lib.rs (limited to 'rust/examples') diff --git a/rust/examples/dwarf/dwarf_export/CMakeLists.txt b/rust/examples/dwarf/dwarf_export/CMakeLists.txt new file mode 100644 index 00000000..aa4631dc --- /dev/null +++ b/rust/examples/dwarf/dwarf_export/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(dwarf_export) + +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_export${CMAKE_SHARED_LIBRARY_SUFFIX}) +set(PLUGIN_PATH ${TARGET_DIR}/${OUTPUT_FILE}) + +add_custom_target(dwarf_export ALL DEPENDS ${PLUGIN_PATH}) +add_dependencies(dwarf_export 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_export/Cargo.toml b/rust/examples/dwarf/dwarf_export/Cargo.toml new file mode 100644 index 00000000..022d3e00 --- /dev/null +++ b/rust/examples/dwarf/dwarf_export/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "dwarf_export" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +binaryninja = {path="../../../"} +gimli = "^0.27" +log = "^0.4" +object = { version = "0.30.3", features = ["write"] } diff --git a/rust/examples/dwarf/dwarf_export/README.md b/rust/examples/dwarf/dwarf_export/README.md new file mode 100644 index 00000000..cea8e3b4 --- /dev/null +++ b/rust/examples/dwarf/dwarf_export/README.md @@ -0,0 +1 @@ +# DWARF Export diff --git a/rust/examples/dwarf/dwarf_export/build.rs b/rust/examples/dwarf/dwarf_export/build.rs new file mode 100644 index 00000000..5ba9bcde --- /dev/null +++ b/rust/examples/dwarf/dwarf_export/build.rs @@ -0,0 +1,68 @@ +use std::env; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; + +#[cfg(target_os = "macos")] +static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); + +#[cfg(target_os = "linux")] +static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); + +#[cfg(windows)] +static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); + +// Check last run location for path to BinaryNinja; Otherwise check the default install locations +fn link_path() -> PathBuf { + use std::io::prelude::*; + + let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); + let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1); + + File::open(lastrun) + .and_then(|f| { + let mut binja_path = String::new(); + let mut reader = BufReader::new(f); + + reader.read_line(&mut binja_path)?; + Ok(PathBuf::from(binja_path.trim())) + }) + .unwrap_or_else(|_| { + #[cfg(target_os = "macos")] + return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); + + #[cfg(target_os = "linux")] + return home.join("binaryninja"); + + #[cfg(windows)] + return PathBuf::from(env::var("PROGRAMFILES").unwrap()) + .join("Vector35\\BinaryNinja\\"); + }) +} + +fn main() { + // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults + let install_path = env::var("BINARYNINJADIR") + .map(PathBuf::from) + .unwrap_or_else(|_| link_path()); + + #[cfg(target_os = "linux")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "macos")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "windows")] + { + println!("cargo:rustc-link-lib=binaryninjacore"); + println!("cargo:rustc-link-search={}", install_path.to_str().unwrap()); + } +} diff --git a/rust/examples/dwarf/dwarf_export/src/lib.rs b/rust/examples/dwarf/dwarf_export/src/lib.rs new file mode 100644 index 00000000..c47be66c --- /dev/null +++ b/rust/examples/dwarf/dwarf_export/src/lib.rs @@ -0,0 +1,690 @@ +use gimli::{ + constants, + write::{ + Address, AttributeValue, DwarfUnit, EndianVec, Expression, Range, RangeList, Sections, + UnitEntryId, + }, +}; +use object::{write, Architecture, BinaryFormat, SectionKind}; +use std::fs; + +use binaryninja::{ + binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}, + command::{register, Command}, + interaction, + interaction::{FormResponses, FormResponses::Index}, + logger::init, + rc::Ref, + symbol::SymbolType, + types::{Conf, MemberAccess, StructureType, Type, TypeClass}, +}; +use log::{error, LevelFilter}; + +fn export_type( + t: &Type, + bv: &BinaryView, + defined_types: &mut Vec<(Ref, UnitEntryId)>, + dwarf: &mut DwarfUnit, +) -> UnitEntryId { + if let Some((_, die)) = defined_types + .iter() + .find(|(defined_type, _)| defined_type.as_ref() == t) + { + return *die; + } + + let root = dwarf.unit.root(); + match t.type_class() { + TypeClass::VoidTypeClass => { + let void_die_uid = dwarf.unit.add(root, constants::DW_TAG_unspecified_type); + defined_types.push((t.to_owned(), void_die_uid)); + + dwarf.unit.get_mut(void_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String("void".as_bytes().to_vec()), + ); + void_die_uid + } + TypeClass::BoolTypeClass => { + let bool_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type); + defined_types.push((t.to_owned(), bool_die_uid)); + + dwarf.unit.get_mut(bool_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", t).as_bytes().to_vec()), + ); + dwarf.unit.get_mut(bool_die_uid).set( + gimli::DW_AT_byte_size, + AttributeValue::Data1(t.width() as u8), + ); + dwarf.unit.get_mut(bool_die_uid).set( + gimli::DW_AT_encoding, + AttributeValue::Encoding(constants::DW_ATE_float), + ); + bool_die_uid + } + TypeClass::IntegerTypeClass => { + let int_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type); + defined_types.push((t.to_owned(), int_die_uid)); + + dwarf.unit.get_mut(int_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", t).as_bytes().to_vec()), + ); + dwarf.unit.get_mut(int_die_uid).set( + gimli::DW_AT_byte_size, + AttributeValue::Data1(t.width() as u8), + ); + dwarf.unit.get_mut(int_die_uid).set( + gimli::DW_AT_encoding, + if t.is_signed().contents { + AttributeValue::Encoding(constants::DW_ATE_signed) + } else { + AttributeValue::Encoding(constants::DW_ATE_unsigned) + }, + ); + int_die_uid + } + TypeClass::FloatTypeClass => { + let float_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type); + defined_types.push((t.to_owned(), float_die_uid)); + + dwarf.unit.get_mut(float_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", t).as_bytes().to_vec()), + ); + dwarf.unit.get_mut(float_die_uid).set( + gimli::DW_AT_byte_size, + AttributeValue::Data1(t.width() as u8), + ); + dwarf.unit.get_mut(float_die_uid).set( + gimli::DW_AT_encoding, + AttributeValue::Encoding(constants::DW_ATE_float), + ); + float_die_uid + } + TypeClass::StructureTypeClass => { + let structure_die_uid = match t.get_structure().unwrap().structure_type() { + StructureType::ClassStructureType => { + dwarf.unit.add(root, constants::DW_TAG_class_type) + } + StructureType::StructStructureType => { + dwarf.unit.add(root, constants::DW_TAG_structure_type) + } + StructureType::UnionStructureType => { + dwarf.unit.add(root, constants::DW_TAG_union_type) + } + }; + defined_types.push((t.to_owned(), structure_die_uid)); + + // TODO : I think I should technically get the NTR for this type and pull the name from that? Not sure if this will fail spectacularly with anonymous structs + dwarf.unit.get_mut(structure_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", t).as_bytes().to_vec()), + ); + dwarf.unit.get_mut(structure_die_uid).set( + gimli::DW_AT_byte_size, + AttributeValue::Data1(t.width() as u8), + ); + + for struct_member in t.get_structure().unwrap().members().unwrap() { + let struct_member_die_uid = + dwarf.unit.add(structure_die_uid, constants::DW_TAG_member); + dwarf.unit.get_mut(struct_member_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(struct_member.name.as_bytes().to_vec()), + ); + match struct_member.access { + MemberAccess::PrivateAccess => { + dwarf.unit.get_mut(struct_member_die_uid).set( + gimli::DW_AT_accessibility, + AttributeValue::Accessibility(gimli::DW_ACCESS_private), + ); + } + MemberAccess::ProtectedAccess => { + dwarf.unit.get_mut(struct_member_die_uid).set( + gimli::DW_AT_accessibility, + AttributeValue::Accessibility(gimli::DW_ACCESS_protected), + ); + } + MemberAccess::PublicAccess => { + dwarf.unit.get_mut(struct_member_die_uid).set( + gimli::DW_AT_accessibility, + AttributeValue::Accessibility(gimli::DW_ACCESS_public), + ); + } + _ => (), + }; + dwarf.unit.get_mut(struct_member_die_uid).set( + gimli::DW_AT_data_bit_offset, + AttributeValue::Data8(struct_member.offset * 8), + ); + + let target_die_uid = AttributeValue::UnitRef(export_type( + struct_member.ty.contents.as_ref(), + bv, + defined_types, + dwarf, + )); + dwarf + .unit + .get_mut(struct_member_die_uid) + .set(gimli::DW_AT_type, target_die_uid); + } + + structure_die_uid + } + TypeClass::EnumerationTypeClass => { + let enum_die_uid = dwarf.unit.add(root, constants::DW_TAG_enumeration_type); + defined_types.push((t.to_owned(), enum_die_uid)); + + dwarf.unit.get_mut(enum_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", t).as_bytes().to_vec()), + ); + dwarf.unit.get_mut(enum_die_uid).set( + gimli::DW_AT_byte_size, + AttributeValue::Data1(t.width() as u8), + ); + + for enum_field in t.get_enumeration().unwrap().members() { + let enum_field_die_uid = dwarf.unit.add(enum_die_uid, constants::DW_TAG_enumerator); + dwarf.unit.get_mut(enum_field_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(enum_field.name.as_bytes().to_vec()), + ); + dwarf.unit.get_mut(enum_field_die_uid).set( + gimli::DW_AT_const_value, + AttributeValue::Data4(enum_field.value as u32), + ); + } + + enum_die_uid + } + TypeClass::PointerTypeClass => { + let pointer_die_uid = dwarf.unit.add(root, constants::DW_TAG_pointer_type); + defined_types.push((t.to_owned(), pointer_die_uid)); + + dwarf.unit.get_mut(pointer_die_uid).set( + gimli::DW_AT_byte_size, + AttributeValue::Data1(t.width() as u8), + ); + if let Ok(Conf { + contents: target_type, + .. + }) = t.target() + { + let target_die_uid = + AttributeValue::UnitRef(export_type(&target_type, bv, defined_types, dwarf)); + dwarf + .unit + .get_mut(pointer_die_uid) + .set(gimli::DW_AT_type, target_die_uid); + } + pointer_die_uid + } + TypeClass::ArrayTypeClass => { + let array_die_uid = dwarf.unit.add(root, constants::DW_TAG_array_type); + defined_types.push((t.to_owned(), array_die_uid)); + + dwarf.unit.get_mut(array_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", t).as_bytes().to_vec()), + ); + if let Ok(Conf { + contents: element_type, + .. + }) = t.element_type() + { + let target_die_uid = + AttributeValue::UnitRef(export_type(&element_type, bv, defined_types, dwarf)); + dwarf + .unit + .get_mut(array_die_uid) + .set(gimli::DW_AT_type, target_die_uid); + } + dwarf + .unit + .get_mut(array_die_uid) + .set(gimli::DW_AT_byte_size, AttributeValue::Data8(t.width())); + array_die_uid + } + TypeClass::FunctionTypeClass => dwarf.unit.add(root, constants::DW_TAG_unspecified_type), + TypeClass::VarArgsTypeClass => dwarf.unit.add(root, constants::DW_TAG_unspecified_type), + TypeClass::ValueTypeClass => dwarf.unit.add(root, constants::DW_TAG_unspecified_type), + TypeClass::NamedTypeReferenceClass => { + let typedef_die_uid = dwarf.unit.add(root, constants::DW_TAG_typedef); + defined_types.push((t.to_owned(), typedef_die_uid)); + + dwarf.unit.get_mut(typedef_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", t).as_bytes().to_vec()), + ); + let t = t.get_named_type_reference().unwrap(); + if let Some(target_type) = t.target(bv) { + let target_die_uid = + AttributeValue::UnitRef(export_type(&target_type, bv, defined_types, dwarf)); + dwarf + .unit + .get_mut(typedef_die_uid) + .set(gimli::DW_AT_type, target_die_uid); + } + typedef_die_uid + } + TypeClass::WideCharTypeClass => { + let wide_char_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type); + defined_types.push((t.to_owned(), wide_char_die_uid)); + + dwarf.unit.get_mut(wide_char_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", t).as_bytes().to_vec()), + ); + dwarf.unit.get_mut(wide_char_die_uid).set( + gimli::DW_AT_byte_size, + AttributeValue::Data1(t.width() as u8), + ); + dwarf.unit.get_mut(wide_char_die_uid).set( + gimli::DW_AT_encoding, + if t.is_signed().contents { + AttributeValue::Encoding(constants::DW_ATE_signed_char) + } else { + AttributeValue::Encoding(constants::DW_ATE_unsigned_char) + }, + ); + wide_char_die_uid + } + } +} + +fn export_types( + bv: &BinaryView, + dwarf: &mut DwarfUnit, + defined_types: &mut Vec<(Ref, UnitEntryId)>, +) { + for t in &bv.types() { + export_type(&t.type_object(), bv, defined_types, dwarf); + } +} + +fn export_functions( + bv: &BinaryView, + dwarf: &mut DwarfUnit, + defined_types: &mut Vec<(Ref, UnitEntryId)>, +) { + let entry_point = bv.entry_point_function(); + + for function in &bv.functions() { + // Create function DIE as child of the compilation unit DIE + let root = dwarf.unit.root(); + let function_die_uid = dwarf.unit.add(root, constants::DW_TAG_subprogram); + // let function_die = dwarf.unit.get_mut(function_die_uid); + + // Set subprogram DIE attributes + dwarf.unit.get_mut(function_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(function.symbol().full_name().as_bytes().to_vec()), // TODO: Which name to use? + ); + + // TODO : (DW_AT_main_subprogram VS DW_TAG_entry_point) + // TODO : This attribute seems maybe usually unused? + if let Ok(entry_point_function) = &entry_point { + if entry_point_function.as_ref() == function.as_ref() { + dwarf + .unit + .get_mut(function_die_uid) + .set(gimli::DW_AT_main_subprogram, AttributeValue::Flag(true)); + dwarf.unit.get_mut(function_die_uid).set( + gimli::DW_AT_low_pc, + AttributeValue::Address(Address::Constant(function.start())), // TODO: Relocations + ); + } + } + + let address_ranges = function.address_ranges(); + if address_ranges.len() == 1 { + let address_range = address_ranges.get(0); + dwarf.unit.get_mut(function_die_uid).set( + gimli::DW_AT_low_pc, + AttributeValue::Address(Address::Constant(address_range.start() - bv.start())), // TODO: Relocations + ); + dwarf.unit.get_mut(function_die_uid).set( + gimli::DW_AT_high_pc, + AttributeValue::Address(Address::Constant( + address_range.end() - address_range.start(), + )), + ); + } else { + let range_list = RangeList( + address_ranges + .into_iter() + .map(|range| Range::StartLength { + begin: Address::Constant(range.start() - bv.start()), // TODO: Relocations? + length: range.end() - range.start(), + }) + .collect(), + ); + let range_list_id = dwarf.unit.ranges.add(range_list); + dwarf.unit.get_mut(function_die_uid).set( + gimli::DW_AT_ranges, + AttributeValue::RangeListRef(range_list_id), + ); + } + + // DWARFv4 2.18: " If no DW_AT_entry_pc attribute is present, then the entry address is assumed to be the same as the value of the DW_AT_low_pc attribute" + if address_ranges.get(0).start() != function.start() { + dwarf.unit.get_mut(function_die_uid).set( + gimli::DW_AT_entry_pc, + AttributeValue::Address(Address::Constant(function.start() - bv.start())), + ); + } + + if function.return_type().contents.type_class() != TypeClass::VoidTypeClass { + let return_type_die_uid = AttributeValue::UnitRef(export_type( + function.return_type().contents.as_ref(), + bv, + defined_types, + dwarf, + )); + dwarf + .unit + .get_mut(function_die_uid) + .set(gimli::DW_AT_type, return_type_die_uid); + } + + for parameter in function.function_type().parameters().unwrap() { + let param_die_uid = dwarf + .unit + .add(function_die_uid, constants::DW_TAG_formal_parameter); + + dwarf.unit.get_mut(param_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(parameter.name.as_bytes().to_vec()), + ); + + let target_die_uid = AttributeValue::UnitRef(export_type( + ¶meter.t.contents, + bv, + defined_types, + dwarf, + )); + dwarf + .unit + .get_mut(param_die_uid) + .set(gimli::DW_AT_type, target_die_uid); + } + + if function.function_type().has_variable_arguments().contents { + dwarf + .unit + .add(function_die_uid, constants::DW_TAG_unspecified_parameters); + } + + if function.symbol().external() { + dwarf + .unit + .get_mut(function_die_uid) + .set(gimli::DW_AT_external, AttributeValue::Flag(true)); + } + + // TODO : calling convention attr + // TODO : local vars + } +} + +fn export_data_vars( + bv: &BinaryView, + dwarf: &mut DwarfUnit, + defined_types: &mut Vec<(Ref, UnitEntryId)>, +) { + let root = dwarf.unit.root(); + + for data_variable in &bv.data_variables() { + if let Some(symbol) = data_variable.symbol(bv) { + if symbol.sym_type() == SymbolType::External { + continue; + } + } + + let var_die_uid = dwarf.unit.add(root, constants::DW_TAG_variable); + + if let Some(symbol) = data_variable.symbol(bv) { + dwarf.unit.get_mut(var_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String(symbol.full_name().as_bytes().to_vec()), + ); + + if symbol.external() { + dwarf + .unit + .get_mut(var_die_uid) + .set(gimli::DW_AT_external, AttributeValue::Flag(true)); + } + } else { + dwarf.unit.get_mut(var_die_uid).set( + gimli::DW_AT_name, + AttributeValue::String( + format!("data_{:x}", data_variable.address) + .as_bytes() + .to_vec(), + ), + ); + } + + let mut variable_location = Expression::new(); + variable_location.op_addr(Address::Constant(data_variable.address)); + dwarf.unit.get_mut(var_die_uid).set( + gimli::DW_AT_location, + AttributeValue::Exprloc(variable_location), + ); + + let target_die_uid = AttributeValue::UnitRef(export_type( + data_variable.t.contents.as_ref(), + bv, + defined_types, + dwarf, + )); + dwarf + .unit + .get_mut(var_die_uid) + .set(gimli::DW_AT_type, target_die_uid); + } +} + +fn present_form() -> Vec { + // TODO : Verify inputs (like save location) so that we can fail early + // TODO : Add Language field + // TODO : Choose to export types/functions/etc + interaction::FormInputBuilder::new() + .save_file_field("Save Location", None, None, None) + .choice_field( + "Architecture", + &[ + "Unknown", + "Aarch64", + "Aarch64_Ilp32", + "Arm", + "Avr", + "Bpf", + "I386", + "X86_64", + "X86_64_X32", + "Hexagon", + "LoongArch64", + "Mips", + "Mips64", + "Msp430", + "PowerPc", + "PowerPc64", + "Riscv32", + "Riscv64", + "S390x", + "Sbf", + "Sparc64", + "Wasm32", + "Xtensa", + ], + None, + ) + .choice_field( + "Container Format", + &["Coff", "Elf", "MachO", "Pe", "Wasm", "Xcoff"], + None, + ) + .get_form_input("Export as DWARF") +} + +fn write_dwarf( + responses: Vec, + endian: T, + dwarf: &mut DwarfUnit, +) { + if responses.len() < 3 { + return; + } + + let arch = match responses[1] { + Index(0) => Architecture::Unknown, + Index(1) => Architecture::Aarch64, + Index(2) => Architecture::Aarch64_Ilp32, + Index(3) => Architecture::Arm, + Index(4) => Architecture::Avr, + Index(5) => Architecture::Bpf, + Index(6) => Architecture::I386, + Index(7) => Architecture::X86_64, + Index(8) => Architecture::X86_64_X32, + Index(9) => Architecture::Hexagon, + Index(10) => Architecture::LoongArch64, + Index(11) => Architecture::Mips, + Index(12) => Architecture::Mips64, + Index(13) => Architecture::Msp430, + Index(14) => Architecture::PowerPc, + Index(15) => Architecture::PowerPc64, + Index(16) => Architecture::Riscv32, + Index(17) => Architecture::Riscv64, + Index(18) => Architecture::S390x, + Index(19) => Architecture::Sbf, + Index(20) => Architecture::Sparc64, + Index(21) => Architecture::Wasm32, + Index(22) => Architecture::Xtensa, + _ => Architecture::Unknown, + }; + + let format = match responses[2] { + Index(0) => BinaryFormat::Coff, + Index(1) => BinaryFormat::Elf, + Index(2) => BinaryFormat::MachO, + Index(3) => BinaryFormat::Pe, + Index(4) => BinaryFormat::Wasm, + Index(5) => BinaryFormat::Xcoff, + _ => BinaryFormat::Elf, + }; + + // TODO : Properly determine output format (without user input) + // TODO : Properly determine architecture (without user input) + // TODO : Look in to other options (mangling, flags, etc (see Object::new)) + let mut out_object = write::Object::new( + format, + arch, + if endian.is_little_endian() { + object::Endianness::Little + } else { + object::Endianness::Big + }, + ); + + // Finally, write the DWARF data to the sections. + let mut sections = Sections::new(EndianVec::new(endian)); + dwarf.write(&mut sections).unwrap(); + + sections + .for_each(|input_id, input_data| { + // Create section in output object + let output_id = out_object.add_section( + vec![], // Only machos have segment names? see object::write::Object::segment_name + input_id + .dwo_name() + .unwrap_or_else(|| input_id.name()) + .as_bytes() + .to_vec(), + SectionKind::Debug, // TODO: Might be wrong + ); + + // Write data to section in output object + let out_section = out_object.section_mut(output_id); + if out_section.is_bss() { + panic!("Please report this as a bug: output section is bss"); + } else { + out_section.set_data(input_data.clone().into_vec(), 1); + } + // out_section.flags = in_section.flags(); // TODO + + Ok::<(), ()>(()) + }) + .unwrap(); + + if let interaction::FormResponses::String(filename) = &responses[0] { + if let Ok(out_data) = out_object.write() { + if let Err(err) = fs::write(filename, out_data) { + error!("Failed to write DWARF file: {}", err); + } + } else { + error!("Failed to write DWARF with requested settings"); + } + } +} + +fn export_dwarf(bv: &BinaryView) { + let responses = present_form(); + + let encoding = gimli::Encoding { + format: gimli::Format::Dwarf32, + version: 4, + address_size: bv.address_size() as u8, + }; + + // Create a container for a single compilation unit. + // TODO : Add attributes to the compilation unit DIE? + let mut dwarf = DwarfUnit::new(encoding); + dwarf.unit.get_mut(dwarf.unit.root()).set( + gimli::DW_AT_producer, + AttributeValue::String("Binary Ninja DWARF Export Plugin".as_bytes().to_vec()), + ); + + // Everything has types, so we need to track what is already defined globally as to not duplicate type entries + let mut defined_types: Vec<(Ref, UnitEntryId)> = vec![]; + export_types(bv, &mut dwarf, &mut defined_types); + export_functions(bv, &mut dwarf, &mut defined_types); + export_data_vars(bv, &mut dwarf, &mut defined_types); + // TODO: Export all symbols instead of just data vars? + // TODO: Sections? Segments? + + if bv.default_endianness() == binaryninja::Endianness::LittleEndian { + write_dwarf(responses, gimli::LittleEndian, &mut dwarf); + } else { + write_dwarf(responses, gimli::BigEndian, &mut dwarf); + }; +} + +struct MyCommand; +impl Command for MyCommand { + fn action(&self, view: &BinaryView) { + export_dwarf(view) + } + + fn valid(&self, _view: &BinaryView) -> bool { + true + } +} + +#[no_mangle] +pub extern "C" fn CorePluginInit() -> bool { + init(LevelFilter::Info).expect("Unable to initialize logger"); + + register( + "Export as DWARF", + "Export current analysis state and annotations as DWARF for import into other tools", + MyCommand {}, + ); + + true +} diff --git a/rust/examples/dwarfexport/CMakeLists.txt b/rust/examples/dwarfexport/CMakeLists.txt deleted file mode 100644 index cb4d5790..00000000 --- a/rust/examples/dwarfexport/CMakeLists.txt +++ /dev/null @@ -1,89 +0,0 @@ -cmake_minimum_required(VERSION 3.9 FATAL_ERROR) - -project(dwarfexport) - -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}dwarfexport${CMAKE_SHARED_LIBRARY_SUFFIX}) -set(PLUGIN_PATH ${TARGET_DIR}/${OUTPUT_FILE}) - -add_custom_target(dwarfexport ALL DEPENDS ${PLUGIN_PATH}) -add_dependencies(dwarfexport 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/dwarfexport/Cargo.toml b/rust/examples/dwarfexport/Cargo.toml deleted file mode 100644 index a95bc4e6..00000000 --- a/rust/examples/dwarfexport/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "dwarfexport" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -binaryninja = {path="../../"} -gimli = "^0.27" -log = "^0.4" -object = { version = "0.30.3", features = ["write"] } diff --git a/rust/examples/dwarfexport/README.md b/rust/examples/dwarfexport/README.md deleted file mode 100644 index cea8e3b4..00000000 --- a/rust/examples/dwarfexport/README.md +++ /dev/null @@ -1 +0,0 @@ -# DWARF Export diff --git a/rust/examples/dwarfexport/build.rs b/rust/examples/dwarfexport/build.rs deleted file mode 100644 index 5ba9bcde..00000000 --- a/rust/examples/dwarfexport/build.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::BufReader; -use std::path::PathBuf; - -#[cfg(target_os = "macos")] -static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); - -#[cfg(target_os = "linux")] -static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); - -#[cfg(windows)] -static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); - -// Check last run location for path to BinaryNinja; Otherwise check the default install locations -fn link_path() -> PathBuf { - use std::io::prelude::*; - - let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); - let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1); - - File::open(lastrun) - .and_then(|f| { - let mut binja_path = String::new(); - let mut reader = BufReader::new(f); - - reader.read_line(&mut binja_path)?; - Ok(PathBuf::from(binja_path.trim())) - }) - .unwrap_or_else(|_| { - #[cfg(target_os = "macos")] - return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); - - #[cfg(target_os = "linux")] - return home.join("binaryninja"); - - #[cfg(windows)] - return PathBuf::from(env::var("PROGRAMFILES").unwrap()) - .join("Vector35\\BinaryNinja\\"); - }) -} - -fn main() { - // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults - let install_path = env::var("BINARYNINJADIR") - .map(PathBuf::from) - .unwrap_or_else(|_| link_path()); - - #[cfg(target_os = "linux")] - println!( - "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1", - install_path.to_str().unwrap(), - install_path.to_str().unwrap(), - ); - - #[cfg(target_os = "macos")] - println!( - "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore", - install_path.to_str().unwrap(), - install_path.to_str().unwrap(), - ); - - #[cfg(target_os = "windows")] - { - println!("cargo:rustc-link-lib=binaryninjacore"); - println!("cargo:rustc-link-search={}", install_path.to_str().unwrap()); - } -} diff --git a/rust/examples/dwarfexport/src/lib.rs b/rust/examples/dwarfexport/src/lib.rs deleted file mode 100644 index 8090f1b3..00000000 --- a/rust/examples/dwarfexport/src/lib.rs +++ /dev/null @@ -1,703 +0,0 @@ -use gimli::{ - constants, - write::{ - Address, AttributeValue, DwarfUnit, EndianVec, Expression, Range, RangeList, Sections, - UnitEntryId, - }, -}; -use object::{write, Architecture, BinaryFormat, SectionKind}; -use std::fs; - -use binaryninja::{ - binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}, - command::{register, Command}, - interaction, - interaction::{FormResponses, FormResponses::Index}, - logger::init, - rc::Ref, - settings::Settings, - symbol::SymbolType, - types::{Conf, MemberAccess, StructureType, Type, TypeClass}, -}; -use log::{error, LevelFilter}; - -fn export_type( - t: &Type, - bv: &BinaryView, - defined_types: &mut Vec<(Ref, UnitEntryId)>, - dwarf: &mut DwarfUnit, -) -> UnitEntryId { - if let Some((_, die)) = defined_types - .iter() - .find(|(defined_type, _)| defined_type.as_ref() == t) - { - return *die; - } - - let root = dwarf.unit.root(); - match t.type_class() { - TypeClass::VoidTypeClass => { - let void_die_uid = dwarf.unit.add(root, constants::DW_TAG_unspecified_type); - defined_types.push((t.to_owned(), void_die_uid)); - - dwarf.unit.get_mut(void_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String("void".as_bytes().to_vec()), - ); - void_die_uid - } - TypeClass::BoolTypeClass => { - let bool_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type); - defined_types.push((t.to_owned(), bool_die_uid)); - - dwarf.unit.get_mut(bool_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", t).as_bytes().to_vec()), - ); - dwarf.unit.get_mut(bool_die_uid).set( - gimli::DW_AT_byte_size, - AttributeValue::Data1(t.width() as u8), - ); - dwarf.unit.get_mut(bool_die_uid).set( - gimli::DW_AT_encoding, - AttributeValue::Encoding(constants::DW_ATE_float), - ); - bool_die_uid - } - TypeClass::IntegerTypeClass => { - let int_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type); - defined_types.push((t.to_owned(), int_die_uid)); - - dwarf.unit.get_mut(int_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", t).as_bytes().to_vec()), - ); - dwarf.unit.get_mut(int_die_uid).set( - gimli::DW_AT_byte_size, - AttributeValue::Data1(t.width() as u8), - ); - dwarf.unit.get_mut(int_die_uid).set( - gimli::DW_AT_encoding, - if t.is_signed().contents { - AttributeValue::Encoding(constants::DW_ATE_signed) - } else { - AttributeValue::Encoding(constants::DW_ATE_unsigned) - }, - ); - int_die_uid - } - TypeClass::FloatTypeClass => { - let float_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type); - defined_types.push((t.to_owned(), float_die_uid)); - - dwarf.unit.get_mut(float_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", t).as_bytes().to_vec()), - ); - dwarf.unit.get_mut(float_die_uid).set( - gimli::DW_AT_byte_size, - AttributeValue::Data1(t.width() as u8), - ); - dwarf.unit.get_mut(float_die_uid).set( - gimli::DW_AT_encoding, - AttributeValue::Encoding(constants::DW_ATE_float), - ); - float_die_uid - } - TypeClass::StructureTypeClass => { - let structure_die_uid = match t.get_structure().unwrap().structure_type() { - StructureType::ClassStructureType => { - dwarf.unit.add(root, constants::DW_TAG_class_type) - } - StructureType::StructStructureType => { - dwarf.unit.add(root, constants::DW_TAG_structure_type) - } - StructureType::UnionStructureType => { - dwarf.unit.add(root, constants::DW_TAG_union_type) - } - }; - defined_types.push((t.to_owned(), structure_die_uid)); - - // TODO : I think I should technically get the NTR for this type and pull the name from that? Not sure if this will fail spectacularly with anonymous structs - dwarf.unit.get_mut(structure_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", t).as_bytes().to_vec()), - ); - dwarf.unit.get_mut(structure_die_uid).set( - gimli::DW_AT_byte_size, - AttributeValue::Data1(t.width() as u8), - ); - - for struct_member in t.get_structure().unwrap().members().unwrap() { - let struct_member_die_uid = - dwarf.unit.add(structure_die_uid, constants::DW_TAG_member); - dwarf.unit.get_mut(struct_member_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(struct_member.name.as_bytes().to_vec()), - ); - match struct_member.access { - MemberAccess::PrivateAccess => { - dwarf.unit.get_mut(struct_member_die_uid).set( - gimli::DW_AT_accessibility, - AttributeValue::Accessibility(gimli::DW_ACCESS_private), - ); - } - MemberAccess::ProtectedAccess => { - dwarf.unit.get_mut(struct_member_die_uid).set( - gimli::DW_AT_accessibility, - AttributeValue::Accessibility(gimli::DW_ACCESS_protected), - ); - } - MemberAccess::PublicAccess => { - dwarf.unit.get_mut(struct_member_die_uid).set( - gimli::DW_AT_accessibility, - AttributeValue::Accessibility(gimli::DW_ACCESS_public), - ); - } - _ => (), - }; - dwarf.unit.get_mut(struct_member_die_uid).set( - gimli::DW_AT_data_bit_offset, - AttributeValue::Data8(struct_member.offset * 8), - ); - - let target_die_uid = AttributeValue::UnitRef(export_type( - struct_member.ty.contents.as_ref(), - bv, - defined_types, - dwarf, - )); - dwarf - .unit - .get_mut(struct_member_die_uid) - .set(gimli::DW_AT_type, target_die_uid); - } - - structure_die_uid - } - TypeClass::EnumerationTypeClass => { - let enum_die_uid = dwarf.unit.add(root, constants::DW_TAG_enumeration_type); - defined_types.push((t.to_owned(), enum_die_uid)); - - dwarf.unit.get_mut(enum_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", t).as_bytes().to_vec()), - ); - dwarf.unit.get_mut(enum_die_uid).set( - gimli::DW_AT_byte_size, - AttributeValue::Data1(t.width() as u8), - ); - - for enum_field in t.get_enumeration().unwrap().members() { - let enum_field_die_uid = dwarf.unit.add(enum_die_uid, constants::DW_TAG_enumerator); - dwarf.unit.get_mut(enum_field_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(enum_field.name.as_bytes().to_vec()), - ); - dwarf.unit.get_mut(enum_field_die_uid).set( - gimli::DW_AT_const_value, - AttributeValue::Data4(enum_field.value as u32), - ); - } - - enum_die_uid - } - TypeClass::PointerTypeClass => { - let pointer_die_uid = dwarf.unit.add(root, constants::DW_TAG_pointer_type); - defined_types.push((t.to_owned(), pointer_die_uid)); - - dwarf.unit.get_mut(pointer_die_uid).set( - gimli::DW_AT_byte_size, - AttributeValue::Data1(t.width() as u8), - ); - if let Ok(Conf { - contents: target_type, - .. - }) = t.target() - { - let target_die_uid = - AttributeValue::UnitRef(export_type(&target_type, bv, defined_types, dwarf)); - dwarf - .unit - .get_mut(pointer_die_uid) - .set(gimli::DW_AT_type, target_die_uid); - } - pointer_die_uid - } - TypeClass::ArrayTypeClass => { - let array_die_uid = dwarf.unit.add(root, constants::DW_TAG_array_type); - defined_types.push((t.to_owned(), array_die_uid)); - - dwarf.unit.get_mut(array_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", t).as_bytes().to_vec()), - ); - if let Ok(Conf { - contents: element_type, - .. - }) = t.element_type() - { - let target_die_uid = - AttributeValue::UnitRef(export_type(&element_type, bv, defined_types, dwarf)); - dwarf - .unit - .get_mut(array_die_uid) - .set(gimli::DW_AT_type, target_die_uid); - } - dwarf - .unit - .get_mut(array_die_uid) - .set(gimli::DW_AT_byte_size, AttributeValue::Data8(t.width())); - array_die_uid - } - TypeClass::FunctionTypeClass => dwarf.unit.add(root, constants::DW_TAG_unspecified_type), - TypeClass::VarArgsTypeClass => dwarf.unit.add(root, constants::DW_TAG_unspecified_type), - TypeClass::ValueTypeClass => dwarf.unit.add(root, constants::DW_TAG_unspecified_type), - TypeClass::NamedTypeReferenceClass => { - let typedef_die_uid = dwarf.unit.add(root, constants::DW_TAG_typedef); - defined_types.push((t.to_owned(), typedef_die_uid)); - - dwarf.unit.get_mut(typedef_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", t).as_bytes().to_vec()), - ); - let t = t.get_named_type_reference().unwrap(); - if let Some(target_type) = t.target(bv) { - let target_die_uid = - AttributeValue::UnitRef(export_type(&target_type, bv, defined_types, dwarf)); - dwarf - .unit - .get_mut(typedef_die_uid) - .set(gimli::DW_AT_type, target_die_uid); - } - typedef_die_uid - } - TypeClass::WideCharTypeClass => { - let wide_char_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type); - defined_types.push((t.to_owned(), wide_char_die_uid)); - - dwarf.unit.get_mut(wide_char_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", t).as_bytes().to_vec()), - ); - dwarf.unit.get_mut(wide_char_die_uid).set( - gimli::DW_AT_byte_size, - AttributeValue::Data1(t.width() as u8), - ); - dwarf.unit.get_mut(wide_char_die_uid).set( - gimli::DW_AT_encoding, - if t.is_signed().contents { - AttributeValue::Encoding(constants::DW_ATE_signed_char) - } else { - AttributeValue::Encoding(constants::DW_ATE_unsigned_char) - }, - ); - wide_char_die_uid - } - } -} - -fn export_types( - bv: &BinaryView, - dwarf: &mut DwarfUnit, - defined_types: &mut Vec<(Ref, UnitEntryId)>, -) { - for t in &bv.types() { - export_type(&t.type_object(), bv, defined_types, dwarf); - } -} - -fn export_functions( - bv: &BinaryView, - dwarf: &mut DwarfUnit, - defined_types: &mut Vec<(Ref, UnitEntryId)>, -) { - let entry_point = bv.entry_point_function(); - - for function in &bv.functions() { - // Create function DIE as child of the compilation unit DIE - let root = dwarf.unit.root(); - let function_die_uid = dwarf.unit.add(root, constants::DW_TAG_subprogram); - // let function_die = dwarf.unit.get_mut(function_die_uid); - - // Set subprogram DIE attributes - dwarf.unit.get_mut(function_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(function.symbol().full_name().as_bytes().to_vec()), // TODO: Which name to use? - ); - - // TODO : (DW_AT_main_subprogram VS DW_TAG_entry_point) - // TODO : This attribute seems maybe usually unused? - if let Ok(entry_point_function) = &entry_point { - if entry_point_function.as_ref() == function.as_ref() { - dwarf - .unit - .get_mut(function_die_uid) - .set(gimli::DW_AT_main_subprogram, AttributeValue::Flag(true)); - dwarf.unit.get_mut(function_die_uid).set( - gimli::DW_AT_low_pc, - AttributeValue::Address(Address::Constant(function.start())), // TODO: Relocations - ); - } - } - - let address_ranges = function.address_ranges(); - if address_ranges.len() == 1 { - let address_range = address_ranges.get(0); - dwarf.unit.get_mut(function_die_uid).set( - gimli::DW_AT_low_pc, - AttributeValue::Address(Address::Constant(address_range.start() - bv.start())), // TODO: Relocations - ); - dwarf.unit.get_mut(function_die_uid).set( - gimli::DW_AT_high_pc, - AttributeValue::Address(Address::Constant( - address_range.end() - address_range.start(), - )), - ); - } else { - let range_list = RangeList( - address_ranges - .into_iter() - .map(|range| Range::StartLength { - begin: Address::Constant(range.start() - bv.start()), // TODO: Relocations? - length: range.end() - range.start(), - }) - .collect(), - ); - let range_list_id = dwarf.unit.ranges.add(range_list); - dwarf.unit.get_mut(function_die_uid).set( - gimli::DW_AT_ranges, - AttributeValue::RangeListRef(range_list_id), - ); - } - - // DWARFv4 2.18: " If no DW_AT_entry_pc attribute is present, then the entry address is assumed to be the same as the value of the DW_AT_low_pc attribute" - if address_ranges.get(0).start() != function.start() { - dwarf.unit.get_mut(function_die_uid).set( - gimli::DW_AT_entry_pc, - AttributeValue::Address(Address::Constant(function.start() - bv.start())), - ); - } - - if function.return_type().contents.type_class() != TypeClass::VoidTypeClass { - let return_type_die_uid = AttributeValue::UnitRef(export_type( - function.return_type().contents.as_ref(), - bv, - defined_types, - dwarf, - )); - dwarf - .unit - .get_mut(function_die_uid) - .set(gimli::DW_AT_type, return_type_die_uid); - } - - for parameter in function.function_type().parameters().unwrap() { - let param_die_uid = dwarf - .unit - .add(function_die_uid, constants::DW_TAG_formal_parameter); - - dwarf.unit.get_mut(param_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(parameter.name.as_bytes().to_vec()), - ); - - let target_die_uid = AttributeValue::UnitRef(export_type( - ¶meter.t.contents, - bv, - defined_types, - dwarf, - )); - dwarf - .unit - .get_mut(param_die_uid) - .set(gimli::DW_AT_type, target_die_uid); - } - - if function.function_type().has_variable_arguments().contents { - dwarf - .unit - .add(function_die_uid, constants::DW_TAG_unspecified_parameters); - } - - if function.symbol().external() { - dwarf - .unit - .get_mut(function_die_uid) - .set(gimli::DW_AT_external, AttributeValue::Flag(true)); - } - - // TODO : calling convention attr - // TODO : local vars - } -} - -fn export_data_vars( - bv: &BinaryView, - dwarf: &mut DwarfUnit, - defined_types: &mut Vec<(Ref, UnitEntryId)>, -) { - let root = dwarf.unit.root(); - - for data_variable in &bv.data_variables() { - if let Some(symbol) = data_variable.symbol(bv) { - if symbol.sym_type() == SymbolType::External { - continue; - } - } - - let var_die_uid = dwarf.unit.add(root, constants::DW_TAG_variable); - - if let Some(symbol) = data_variable.symbol(bv) { - dwarf.unit.get_mut(var_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String(symbol.full_name().as_bytes().to_vec()), - ); - - if symbol.external() { - dwarf - .unit - .get_mut(var_die_uid) - .set(gimli::DW_AT_external, AttributeValue::Flag(true)); - } - } else { - dwarf.unit.get_mut(var_die_uid).set( - gimli::DW_AT_name, - AttributeValue::String( - format!("data_{:x}", data_variable.address) - .as_bytes() - .to_vec(), - ), - ); - } - - let mut variable_location = Expression::new(); - variable_location.op_addr(Address::Constant(data_variable.address)); - dwarf.unit.get_mut(var_die_uid).set( - gimli::DW_AT_location, - AttributeValue::Exprloc(variable_location), - ); - - let target_die_uid = AttributeValue::UnitRef(export_type( - data_variable.t.contents.as_ref(), - bv, - defined_types, - dwarf, - )); - dwarf - .unit - .get_mut(var_die_uid) - .set(gimli::DW_AT_type, target_die_uid); - } -} - -fn present_form() -> Vec { - // TODO : Verify inputs (like save location) so that we can fail early - // TODO : Add Language field - // TODO : Choose to export types/functions/etc - interaction::FormInputBuilder::new() - .save_file_field("Save Location", None, None, None) - .choice_field( - "Architecture", - &[ - "Unknown", - "Aarch64", - "Aarch64_Ilp32", - "Arm", - "Avr", - "Bpf", - "I386", - "X86_64", - "X86_64_X32", - "Hexagon", - "LoongArch64", - "Mips", - "Mips64", - "Msp430", - "PowerPc", - "PowerPc64", - "Riscv32", - "Riscv64", - "S390x", - "Sbf", - "Sparc64", - "Wasm32", - "Xtensa", - ], - None, - ) - .choice_field( - "Container Format", - &["Coff", "Elf", "MachO", "Pe", "Wasm", "Xcoff"], - None, - ) - .get_form_input("Export as DWARF") -} - -fn write_dwarf( - responses: Vec, - endian: T, - dwarf: &mut DwarfUnit, -) { - if responses.len() < 3 { - return; - } - - let arch = match responses[1] { - Index(0) => Architecture::Unknown, - Index(1) => Architecture::Aarch64, - Index(2) => Architecture::Aarch64_Ilp32, - Index(3) => Architecture::Arm, - Index(4) => Architecture::Avr, - Index(5) => Architecture::Bpf, - Index(6) => Architecture::I386, - Index(7) => Architecture::X86_64, - Index(8) => Architecture::X86_64_X32, - Index(9) => Architecture::Hexagon, - Index(10) => Architecture::LoongArch64, - Index(11) => Architecture::Mips, - Index(12) => Architecture::Mips64, - Index(13) => Architecture::Msp430, - Index(14) => Architecture::PowerPc, - Index(15) => Architecture::PowerPc64, - Index(16) => Architecture::Riscv32, - Index(17) => Architecture::Riscv64, - Index(18) => Architecture::S390x, - Index(19) => Architecture::Sbf, - Index(20) => Architecture::Sparc64, - Index(21) => Architecture::Wasm32, - Index(22) => Architecture::Xtensa, - _ => Architecture::Unknown, - }; - - let format = match responses[2] { - Index(0) => BinaryFormat::Coff, - Index(1) => BinaryFormat::Elf, - Index(2) => BinaryFormat::MachO, - Index(3) => BinaryFormat::Pe, - Index(4) => BinaryFormat::Wasm, - Index(5) => BinaryFormat::Xcoff, - _ => BinaryFormat::Elf, - }; - - // TODO : Properly determine output format (without user input) - // TODO : Properly determine architecture (without user input) - // TODO : Look in to other options (mangling, flags, etc (see Object::new)) - let mut out_object = write::Object::new( - format, - arch, - if endian.is_little_endian() { - object::Endianness::Little - } else { - object::Endianness::Big - }, - ); - - // Finally, write the DWARF data to the sections. - let mut sections = Sections::new(EndianVec::new(endian)); - dwarf.write(&mut sections).unwrap(); - - sections - .for_each(|input_id, input_data| { - // Create section in output object - let output_id = out_object.add_section( - vec![], // Only machos have segment names? see object::write::Object::segment_name - input_id - .dwo_name() - .unwrap_or_else(|| input_id.name()) - .as_bytes() - .to_vec(), - SectionKind::Debug, // TODO: Might be wrong - ); - - // Write data to section in output object - let out_section = out_object.section_mut(output_id); - if out_section.is_bss() { - panic!("Please report this as a bug: output section is bss"); - } else { - out_section.set_data(input_data.clone().into_vec(), 1); - } - // out_section.flags = in_section.flags(); // TODO - - Ok::<(), ()>(()) - }) - .unwrap(); - - if let interaction::FormResponses::String(filename) = &responses[0] { - if let Ok(out_data) = out_object.write() { - if let Err(err) = fs::write(filename, out_data) { - error!("Failed to write DWARF file: {}", err); - } - } else { - error!("Failed to write DWARF with requested settings"); - } - } -} - -fn export_dwarf(bv: &BinaryView) { - let responses = present_form(); - - let encoding = gimli::Encoding { - format: gimli::Format::Dwarf32, - version: 4, - address_size: bv.address_size() as u8, - }; - - // Create a container for a single compilation unit. - // TODO : Add attributes to the compilation unit DIE? - let mut dwarf = DwarfUnit::new(encoding); - dwarf.unit.get_mut(dwarf.unit.root()).set( - gimli::DW_AT_producer, - AttributeValue::String("Binary Ninja DWARF Export Plugin".as_bytes().to_vec()), - ); - - // Everything has types, so we need to track what is already defined globally as to not duplicate type entries - let mut defined_types: Vec<(Ref, UnitEntryId)> = vec![]; - export_types(bv, &mut dwarf, &mut defined_types); - export_functions(bv, &mut dwarf, &mut defined_types); - export_data_vars(bv, &mut dwarf, &mut defined_types); - // TODO: Export all symbols instead of just data vars? - // TODO: Sections? Segments? - - if bv.default_endianness() == binaryninja::Endianness::LittleEndian { - write_dwarf(responses, gimli::LittleEndian, &mut dwarf); - } else { - write_dwarf(responses, gimli::BigEndian, &mut dwarf); - }; -} - -struct MyCommand; -impl Command for MyCommand { - fn action(&self, view: &BinaryView) { - export_dwarf(view) - } - - fn valid(&self, _view: &BinaryView) -> bool { - true - } -} - -#[no_mangle] -pub extern "C" fn CorePluginInit() -> bool { - init(LevelFilter::Info).expect("Unable to initialize logger"); - - let settings = Settings::new(""); - settings.register_setting_json( - "analysis.experimental.dwarfExport", - r#"{ - "title" : "Enable the DWARF Export Plugin", - "type" : "boolean", - "default" : false, - "description" : "Export current analysis state and annotations as DWARF for import into other tools. This is currently an experimental feature as integrations with tools that import DWARF information are limited." - }"#, - ); - - if settings.get_bool("analysis.experimental.dwarfExport", None, None) { - register( - "Export as DWARF", - "Export current analysis state and annotations as DWARF for import into other tools", - MyCommand {}, - ); - } - true -} -- cgit v1.3.1