diff options
| author | KyleMiles <krm504@nyu.edu> | 2023-02-10 13:19:07 -0500 |
|---|---|---|
| committer | KyleMiles <krm504@nyu.edu> | 2023-02-14 00:15:59 -0500 |
| commit | da48fde5958ad3eedcd3c4b82d7dc5b9428c9e93 (patch) | |
| tree | 44678aef5f86e86a8841dc908a58f5cb00764767 | |
| parent | 9e91eaf63aed340a2c0a61ad8f70e9a7883e3aba (diff) | |
Add DWARF Export
This plugin exports:
- Functions
- Addresses
- Name
- Return type
- Argument names and types
- Data Variables
- Name
- Tyoe
- Address
- Types
- All types in a BNDB except for:
- TypeClass::FunctionTypeClass
- TypeClass::VarArgsTypeClass
- TypeClass::ValueTypeClass
Current MAJOR TODOs include:
- Finish exporting all the types
- Verify what we export can be imported by other toosl
- Auto-detect settings for the initial interaction dialouge
- Allow the functions to be more easily called in a headless mode
- Local function variable names and types
- All my todo comments in the code
| -rw-r--r-- | rust/Cargo.toml | 1 | ||||
| -rw-r--r-- | rust/examples/dwarfexport/Cargo.toml | 13 | ||||
| -rw-r--r-- | rust/examples/dwarfexport/README.md | 1 | ||||
| -rw-r--r-- | rust/examples/dwarfexport/build.rs | 68 | ||||
| -rw-r--r-- | rust/examples/dwarfexport/src/lib.rs | 667 |
5 files changed, 750 insertions, 0 deletions
diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 1a3cf1bb..903227ae 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -16,6 +16,7 @@ members = [ "examples/basic_script", "examples/decompile", "examples/dwarfdump", + "examples/dwarfexport", "examples/flowgraph", "examples/template" ] diff --git a/rust/examples/dwarfexport/Cargo.toml b/rust/examples/dwarfexport/Cargo.toml new file mode 100644 index 00000000..a95bc4e6 --- /dev/null +++ b/rust/examples/dwarfexport/Cargo.toml @@ -0,0 +1,13 @@ +[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 new file mode 100644 index 00000000..cea8e3b4 --- /dev/null +++ b/rust/examples/dwarfexport/README.md @@ -0,0 +1 @@ +# DWARF Export diff --git a/rust/examples/dwarfexport/build.rs b/rust/examples/dwarfexport/build.rs new file mode 100644 index 00000000..5ba9bcde --- /dev/null +++ b/rust/examples/dwarfexport/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/dwarfexport/src/lib.rs b/rust/examples/dwarfexport/src/lib.rs new file mode 100644 index 00000000..c6549ea4 --- /dev/null +++ b/rust/examples/dwarfexport/src/lib.rs @@ -0,0 +1,667 @@ +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<Type>, 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(); + let new_die_id = match t.type_class() { + TypeClass::VoidTypeClass => { + let void_die_uid = dwarf.unit.add(root, constants::DW_TAG_unspecified_type); + 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); + 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); + 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); + 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) + } + }; + 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); + 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); + 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); + 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); + 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); + 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 + } + }; + + defined_types.push((t.to_owned(), new_die_id)); + new_die_id +} + +fn export_types( + bv: &BinaryView, + dwarf: &mut DwarfUnit, + defined_types: &mut Vec<(Ref<Type>, 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<Type>, 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<Type>, 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<FormResponses> { + // 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", Some(".dwo"), 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<T: gimli::Endianity>( + responses: Vec<FormResponses>, + 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); + + // 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<Type>, 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 +} |
