summaryrefslogtreecommitdiff
path: root/rust/examples
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2024-12-22 01:21:02 -0500
committerMason Reed <mason@vector35.com>2025-01-25 00:52:29 -0500
commit6a63c17853009ce7a0071458e39c140a09440230 (patch)
treead59ab3b356b0a2c60423e55f62c996891788788 /rust/examples
parentbfa1d409b2d5e734804a5fdb4c68c90a93712f1f (diff)
Rust refactor
Moves a bunch of stuff out of src/types.rs that did not belong: - Confidence - Variable - Function specific stuff - Refactored InstructionInfo, see the msp430 and riscv examples. - Renamed Function::from_raw to Function::ref_from_raw and fixed places where the ref was incremented twice - Fixed FunctionRecognizer leaking functions (see above) - Fixed some apis incorrectly returning Result where Option is clearer - Started to move destructured types to the From trait for converting to an from ffi types, see Location for an example - Started to remove bad module level imports (importing std modules like mem all over the place) - Moved some wrapper handle types to named handle field (this improves readability), see CoreArchitecture for an example - Removed some unintuitive getters, this is bad practice for Rust code, just access the field directly, see DataVariable for an example - General code cleanup, purposely did not run rustfmt, that will be a single seperate commit More rust cleanup - Fixed invalid views being able to invoke UB when dealing with databases - Cleaned up some helper code in dwarf_import - Fixed inverted is_null checks causing crashes! Oops! More rust cleanup Still a WIP, I think branch info is still invalid, need to figure out the issue there. - Fixed some invalid Ref lifetimes when constructing indirectly, see Array<DataVariable> for example - Added some more comments - Removed some "magic" functions like MLIL Function::ssa_variables There are still a bunch of invalid lifetimes that aren't crashing us due to the usage of those API's not living long enough. But they ARE an issue. More rust cleanup Trying to comment more TODO's as I go along. - Renamed llil::Function to llil::LowLevelILFunction for clarity and consistency - Take base structures by ref in StructureBuilder::set_base_structures to prevent premature drops - Added more raw to wrapper conversions - Removed UB prone apis - Getting rid of more core module references, use std! - Removed improper Guard usage in array impls for wrapper types with no context - Untangling the UB of the Label api, still only half done :/ More rust cleanup - Misc formatting - Made Logger ref counted - Fixed leaking name of logger every time something was logged - Fixed the last (hopefully) of the unresolved labels - Simplified some code Fix leak in DebugInfo::AddType componentArray was never freed Add more HLIL related functions to rust More rust cleanup improve the CustomBinaryView init process Canonicalize path in `create_delete_empty` test Link core in rust When linking you must depend on the -sys crate. This is because linker arguments (what says where to find binaryninjacore) are NOT transitive. The top level application crate MUST provide it. For more information see: - https://github.com/rust-lang/cargo/issues/9554#issuecomment-857882964 - https://github.com/oxidecomputer/omicron/pull/225 Remove vendored pdb crate Use cargo to manage the git repo ref instead Fix misc rustdoc warnings Move actual plugins out of `rust/examples` and into `plugins` This is where all shipped public plugins that are not arch/view/platform/lang will be at from now on Originally they were in the rust workspace, meaning they all shared a Cargo.lock which is ill-advised. More rust cleanup - More clarification on plugin/executable requirements - Made examples actually rust examples - Add Display impl for InstructionTextToken - Renamed feature "noexports" to "no_exports" Move under_construction.png to assets directory This really did bother me Remove unneeded `extern crate bindgen` Replace nop'd log::info with println We don't register a compatible log sink so they will just get sent into the void Move inline tests into tests directory This is being done in the hopes of curbing the multi-thousand line files that will occur once we flesh out the tests Format rust code Update rust ci Still need to add support for running tests in ci More rust cleanup - Architecture id's are now typed accordingly - Fix some clippy lints - Make instruction index public in LLIL - Removed useless helper functions - LLIL expressions and instruction indexes are now typed accordingly Generate binaryninjacore-sys documentation This should show binaryninjacore-sys alongside binaryninja crate More rust cleanup - Remove lazy_static dependency - Remove hacky impl Debug for Type and just use the display impl - Add more debug impls - Reorder some top level namespace items - Add first type test Remove unneeded script helper in rust api More rust cleanup - Added main thread handler api - Register a headless main thread handler by default in initialization - Refactor QualifiedName to be properly owned - Loosened some type constraints on some apis involving QualifiedName - Fixed some apis that were crashing due to incorrect param types - Removed extern crate cruft for log crate - Simplified headless initialization using more wrapper apis - Fixed segments leaking because of no ref wrapper, see BinaryViewExt::segment_at - Added rstest to manage headless init in unit tests - Added some more unit tests - Refactored demangler api to be more ergonomic - Fixed minidump plugin not building More rust cleanup - Fixup usage of QualifiedName in plugins - Make QualifiedName more usable Implement rust TypeParser fix Platform TypeParser related functions separate User and System implementations of TypeParserResult Implement rust TypeContainer More rust cleanup - Hopefully fixed the rust.yml CI - Added TypePrinter API (this is still WIP and will crash) - Added TypeParser API - Added TypeContainer API - More work around QualifiedName apis Oh your suppose to do this Add workflow_dispatch trigger to rust.yml More rust fixes - Swapped some usage of raw 255 to MAX_CONFIDENCE, no one likes magic numbers - New InstructionTextToken API, complete with owned data, this still needs a lot of testing. - InstructionTextTokenKind describes a destructured InstructionTextToken, this should make token usage much clearer, some docs pending - Added some misc Default and Debug impls - Updated TypePrinter and architectures to use new InstructionTextToken API Misc formatting changes More rust cleanup - Fixed MANY memory leaks (most due to QualifiedName) - Made StructureBuilder more builder and less structure - Fixed CMakeLists.txt that were globbing entire api, resulting in 100 second slowdown on cmake generation - Added more Debug impl's - Added some more tests - Fixed TypeParserResult UB - Moved the From impls to blank impl for clarity, we have multiple different variants of core to rust for some structures, it should be explicit which one you are choosing. - PossibleValueSet should now be able to allocate so we can go from rust to core with those variants that require allocation - Misc doc code formatting Misc clippy lints and clippy CI Co-authored-by: Michael Krasnitski <michael.krasnitski@gmail.com> Fix typo in rust CI Misc rust formatting Fix misc typos and add typos to rust CI Add cargo workspace This will help tooling and external contributors get a map of the rust crates within binaryninja-api More rust cleanup - Format all rust plugins - Fix some tests that were out of date - Simplify WARP tests to only binaries, building object files from source is a pain - Link to core in all rust plugins - Fix some memory leaks - Update warp insta snapshots - Fix some misc clippy lints Run rust tests in CI This commit also coincides with the creation of the "testing" environment which exposes a BN_SERIAL secret for pulling a headless Binary Ninja Install missing wayland dependency in github CI Apparently its needed for linux file picker for the WARP integration Set the BINARYNINJADIR so rust can find binaryninjacore in CI The chances of this working are low Misc remove unused dependency Rust misc formatting fixes Improve initialization in rust headless scripts Provide sensible errors and validation to rust headless scripts, solves https://github.com/Vector35/binaryninja-api/issues/5796 Add BN_LICENSE environment variable to rust CI We pass the serial to download binary ninja, but we never provided the license for core initialization Fix typo More rust cleanup - Improved binary view initialization (see init_with_opts) - Allow floating license to free itself before initialization - Add initialization unit test - Add better Debug impls for some common types - Use Path api for opening binary views, this is not breaking as it uses the AsRef impl - Bump rayon dependency and constrain dependencies to x.x Update readme and include that directly in the rustdocs More rust documentation changes Add format comment to InitializationOptions::with_license Misc formatting and clippy lint allow More rust cleanup - Remove under_construction.png from the build.rs it has been removed - Use InstructionIndex in more places - Add missing PartialOrd and Ord impls for id types More rust cleanup - Make workflow cloning explicit - Add workflow tests - Add missing property string list getting for settings - Remove IntoActivityName (see https://github.com/Vector35/binaryninja-api/pull/6257) More rust cleanup This commit is half done Misc rust formatting More rust cleanup - Renamed common name conflictions (I will put my justification in the PR) - Fixed invalid instruction retrieval for LLIL - Added common aliases for llil function, instruction and expression types (see my comment in the PR) - Refactored the instruction retrieval for LLIL, MLIL and HLIL - Added instruction index types to MLIL and HLIL - Moved llil module to lowlevelil module (mlil and hlil will be moved as well) - Added preliminary LLIL unit testing Fix typos Misc clippy fixes More rust cleanup - Normalized modules - Split some code out into own files - Fixed some UB in type archive and projects - Improved API around type archives and projects substantially - Added ProgressExecutor abstraction for functions which have a progress callback - Improved background task documentation and added unit tests - Added worker thread api and unit tests - Moved some owned types to ref types, this is still not complete, but this is the path forward. - Add external location/library accessors to the binary view - Added some misc documentation - Replaced mod.rs with the module name source file Still need to normalize some paths and also update some documentation surrounding that change. Update some tests and examples Fix background task tests colliding We were creating multiple background tasks with the same progress text on multiple threads More rust cleanup - Fixed progress executor freeing itself after one iteration - Updated the last of the doc imports - Moved mainthread to main_thread - Made project creation and opening failable We could probably AsRef<ProgressExecutor> to get around the allocation and free inside the function bodies, not high priority as those functions are long running anyways. Move binary view initialization into function body for LLIL test Normalize test file names More rust cleanup - Updated README to clarify offline documentation - Refactored settings api - Added settings example to show dumping settings value and specific properties - Use the workspace to depend on binaryninja and binaryninjacore-sys - Remove binaryninjacore-sys as a workspace member (its not really required) Update workflow test to new settings api More rust cleanup - Rename Label to LowLevelILLabel - Update the functions label map automatically This fixed a major api blunder where the label map is returned as a reference and originally resulted in UB prone lifetime semantics. It was temporarily "fixed" with explicit label updates in the architecture implementation code. But that was less than ideal and was easy to mess up. Now the label map will be updated automatically as the location of labels is now tracked. Misc clippy lints More rust cleanup - Get rid of RawFunctionViewType - Add better Debug impl for Function More rust cleanup - Fixed the documentation icon using local files (thank you @mkrasnitski) - Fixed labels being updated and overwriting the label location used to update the label map More rust cleanup - Added unit tests for MLIL and HLIL - "Fixed" MLIL, LLIL, and HLIL having issues regarding Instruction vs Expression indexes - Renamed CallingConvention to CoreCallingConvention and removed architecture generic - Renamed CallingConventionBase to CallingConvention - Simplified calling convention code and fixed some bugs with implicit registers - Added impl Debug to MLIL and HLIL instructions Still need to at some point add an Expression to MLIL and HLIL. We also might want to look into having the Instruction kind just return the expression kind. Misc clippy lint More rust cleanup - Allow calling conventions to be registered for multiple architectures - Swapped a unreachable statement to an unimplemented statement More rust cleanup - Fixed the issue with PDB types, this has caused me an insane amount of grief - Fixed LLIL visit_tree missing LLIL_LOAD expressions - Added LLIL visitor test - Made all WARP file pickers use the rfd crate Use the dev branch of Binary Ninja in rust CI Misc rust fmt More rust cleanup - Refactored BinaryReader and BinaryWriter - Added some warnings to high_level_il and medium_level_il modules that they are unstable - Add BinaryReader and BinaryWriter tests - Changed BinaryView len to return u64 (that is what the core returns) - Misc formatting changes - Remove extern uses in lib.rs Add impl Debug for BinaryReader and BinaryWriter Turn off broken tests Add more info to the rust README.md More rust cleanup - Make EdgeStyle type not wrap raw - Regression tests for WARP will run on all bins in the out dir now impl rust Collaboration and Remote API Fix typo Update collaboration API Makes collaboration more in line with the refactor. Still a lot of work to do. Namely still need: - Proper errors - _with_opts functions - More ergonomic api - Better connection procedure - Updated documentation - A LOT of unit tests - An example - Typed id's for everything (i dont want BnString as the id!!!) - NEED to refactor the progress callbacks into the new progress api, but we should pull in some of the stuff the collab progress has - Elimination of apis that are dumb helpers Separate out the rust testing and use pull_request_target pull_request_target allows PR's to access the headless license, for this to be safe we need to prevent people from running the job. To prevent the job from being ran we add an environment requirement on testing that a reviewer must review the code and then manually approve it to run. More rust cleanup - Use GroupId instead of u64 - Use ProgressCallback in place of ProgressExecutor - Misc cleanup of FileMetadata - Add `save_to_path` and `save_to_accessor` to save modified binaries - Added binary_view unit tests - Added collaboration unit tests - Fixed a few issues with the collaboration apis - Renamed Command registration functions so that there is no import ambiguity - Split out RemoteUndoEntry - Collaboration apis now have a explicit `_with_progress` set of apis - Misc clippy lint fixes Fix some typos More rust cleanup - Add extra info to README.md - Refactor components api - Add components unit test Add testing and documentation to contributing section in README.md Fix misc doc comments
Diffstat (limited to 'rust/examples')
-rw-r--r--rust/examples/basic_script/CMakeLists.txt105
-rw-r--r--rust/examples/basic_script/Cargo.toml7
-rw-r--r--rust/examples/basic_script/build.rs68
-rw-r--r--rust/examples/decompile.rs49
-rw-r--r--rust/examples/decompile/Cargo.toml10
-rw-r--r--rust/examples/decompile/build.rs68
-rw-r--r--rust/examples/decompile/src/main.rs54
-rw-r--r--rust/examples/demangler.rs59
-rw-r--r--rust/examples/dump_settings.rs21
-rw-r--r--rust/examples/dwarf/dwarf_export/CMakeLists.txt93
-rw-r--r--rust/examples/dwarf/dwarf_export/Cargo.toml13
-rw-r--r--rust/examples/dwarf/dwarf_export/README.md1
-rw-r--r--rust/examples/dwarf/dwarf_export/build.rs68
-rw-r--r--rust/examples/dwarf/dwarf_export/src/edit_distance.rs44
-rw-r--r--rust/examples/dwarf/dwarf_export/src/lib.rs793
-rw-r--r--rust/examples/dwarf/dwarf_import/CMakeLists.txt93
-rw-r--r--rust/examples/dwarf/dwarf_import/Cargo.toml17
-rw-r--r--rust/examples/dwarf/dwarf_import/src/die_handlers.rs394
-rw-r--r--rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs643
-rw-r--r--rust/examples/dwarf/dwarf_import/src/functions.rs204
-rw-r--r--rust/examples/dwarf/dwarf_import/src/helpers.rs600
-rw-r--r--rust/examples/dwarf/dwarf_import/src/lib.rs723
-rw-r--r--rust/examples/dwarf/dwarf_import/src/types.rs462
-rw-r--r--rust/examples/dwarf/dwarfdump/Cargo.toml13
-rw-r--r--rust/examples/dwarf/dwarfdump/readme.md17
-rw-r--r--rust/examples/dwarf/dwarfdump/src/lib.rs307
-rw-r--r--rust/examples/dwarf/shared/Cargo.toml11
-rw-r--r--rust/examples/dwarf/shared/src/lib.rs190
-rw-r--r--rust/examples/flowgraph.rs56
-rw-r--r--rust/examples/flowgraph/Cargo.toml11
-rw-r--r--rust/examples/flowgraph/src/lib.rs50
-rw-r--r--rust/examples/high_level_il.rs124
-rw-r--r--rust/examples/hlil_lifter/Cargo.toml16
-rw-r--r--rust/examples/hlil_lifter/build.rs68
-rw-r--r--rust/examples/hlil_lifter/src/main.rs52
-rw-r--r--rust/examples/hlil_visitor/Cargo.toml16
-rw-r--r--rust/examples/hlil_visitor/build.rs68
-rw-r--r--rust/examples/hlil_visitor/src/main.rs124
-rw-r--r--rust/examples/idb_import/CMakeLists.txt91
-rw-r--r--rust/examples/idb_import/Cargo.toml14
-rw-r--r--rust/examples/idb_import/src/addr_info.rs57
-rw-r--r--rust/examples/idb_import/src/lib.rs344
-rw-r--r--rust/examples/idb_import/src/types.rs699
-rw-r--r--rust/examples/medium_level_il.rs145
-rw-r--r--rust/examples/minidump/.gitignore2
-rw-r--r--rust/examples/minidump/Cargo.toml12
-rw-r--r--rust/examples/minidump/README.md65
-rw-r--r--rust/examples/minidump/build.rs68
-rw-r--r--rust/examples/minidump/images/loaded-minidump-screenshot-border.pngbin266633 -> 0 bytes
-rw-r--r--rust/examples/minidump/images/minidump-binary-view-type-screenshot-border.pngbin31215 -> 0 bytes
-rw-r--r--rust/examples/minidump/images/minidump-segments-sections-screenshot-border.pngbin122117 -> 0 bytes
-rw-r--r--rust/examples/minidump/src/command.rs41
-rw-r--r--rust/examples/minidump/src/lib.rs38
-rw-r--r--rust/examples/minidump/src/view.rs402
-rw-r--r--rust/examples/mlil_lifter/Cargo.toml16
-rw-r--r--rust/examples/mlil_lifter/build.rs68
-rw-r--r--rust/examples/mlil_lifter/src/main.rs49
-rw-r--r--rust/examples/mlil_visitor/Cargo.toml16
-rw-r--r--rust/examples/mlil_visitor/build.rs68
-rw-r--r--rust/examples/mlil_visitor/src/main.rs137
-rw-r--r--rust/examples/pdb-ng/.gitignore1
-rw-r--r--rust/examples/pdb-ng/CMakeLists.txt138
-rw-r--r--rust/examples/pdb-ng/Cargo.toml18
-rw-r--r--rust/examples/pdb-ng/demo/Cargo.toml21
m---------rust/examples/pdb-ng/pdb-0.8.0-patched0
-rw-r--r--rust/examples/pdb-ng/src/lib.rs937
-rw-r--r--rust/examples/pdb-ng/src/parser.rs508
-rw-r--r--rust/examples/pdb-ng/src/struct_grouper.rs1164
-rw-r--r--rust/examples/pdb-ng/src/symbol_parser.rs2061
-rw-r--r--rust/examples/pdb-ng/src/type_parser.rs2477
-rw-r--r--rust/examples/simple.rs (renamed from rust/examples/basic_script/src/main.rs)18
-rw-r--r--rust/examples/template/Cargo.toml16
-rw-r--r--rust/examples/template/README.md19
-rw-r--r--rust/examples/template/build.rs68
-rw-r--r--rust/examples/template/src/main.rs40
-rw-r--r--rust/examples/test_demangler/Cargo.toml17
-rw-r--r--rust/examples/test_demangler/src/lib.rs65
-rw-r--r--rust/examples/type_printer.rs39
-rw-r--r--rust/examples/workflow.rs89
-rw-r--r--rust/examples/workflow/Cargo.toml11
-rw-r--r--rust/examples/workflow/build.rs68
-rw-r--r--rust/examples/workflow/src/lib.rs70
82 files changed, 593 insertions, 15296 deletions
diff --git a/rust/examples/basic_script/CMakeLists.txt b/rust/examples/basic_script/CMakeLists.txt
deleted file mode 100644
index 706b08cf..00000000
--- a/rust/examples/basic_script/CMakeLists.txt
+++ /dev/null
@@ -1,105 +0,0 @@
-cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
-
-project(test_headless)
-
-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 basic_script${CMAKE_EXECUTABLE_SUFFIX})
-set(OUTPUT_PATH ${CMAKE_BINARY_DIR}/out/bin/${OUTPUT_FILE})
-
-
-if(NOT BN_API_BUILD_EXAMPLES)
- # Out-of-tree build
- find_path(
- BN_API_PATH
- NAMES binaryninjaapi.h
- HINTS ../../.. binaryninjaapi
- REQUIRED
- )
- add_subdirectory(${BN_API_PATH} api)
-endif()
-
-add_custom_target(test_headless ALL DEPENDS ${OUTPUT_PATH})
-add_dependencies(test_headless binaryninjaapi)
-
-# Get API source directory so we can find BinaryNinjaCore
-get_target_property(BN_API_SOURCE_DIR binaryninjaapi SOURCE_DIR)
-message(STATUS "${BN_API_SOURCE_DIR}")
-list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake")
-
-# BinaryNinjaCore has the user plugins dir define that we want
-find_package(BinaryNinjaCore REQUIRED)
-
-find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
-set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_API_VERSION} cargo build)
-
-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 ${OUTPUT_PATH}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_INSTALL_BIN_DIR}
- ${RUSTUP_COMMAND} --target=aarch64-apple-darwin ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_INSTALL_BIN_DIR}
- ${RUSTUP_COMMAND} --target=x86_64-apple-darwin ${CARGO_OPTS}
- COMMAND lipo -create ${AARCH64_LIB_PATH} ${X86_64_LIB_PATH} -output ${OUTPUT_PATH}
- 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 ${OUTPUT_PATH}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_INSTALL_BIN_DIR}
- ${RUSTUP_COMMAND} ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E copy ${LIB_PATH} ${OUTPUT_PATH}
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
- endif()
-elseif(WIN32)
- add_custom_command(
- OUTPUT ${OUTPUT_PATH}
- COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_INSTALL_BIN_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH}
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
-else()
- add_custom_command(
- OUTPUT ${OUTPUT_PATH}
- COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_INSTALL_BIN_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH}
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
-endif()
diff --git a/rust/examples/basic_script/Cargo.toml b/rust/examples/basic_script/Cargo.toml
deleted file mode 100644
index c8ac699f..00000000
--- a/rust/examples/basic_script/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "basic_script"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-binaryninja = {path="../../"}
diff --git a/rust/examples/basic_script/build.rs b/rust/examples/basic_script/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/basic_script/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/decompile.rs b/rust/examples/decompile.rs
new file mode 100644
index 00000000..a55e638e
--- /dev/null
+++ b/rust/examples/decompile.rs
@@ -0,0 +1,49 @@
+use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt};
+use binaryninja::disassembly::{DisassemblyOption, DisassemblySettings};
+use binaryninja::function::Function;
+use binaryninja::linear_view::LinearViewObject;
+
+fn decompile_to_c(view: &BinaryView, func: &Function) {
+ let settings = DisassemblySettings::new();
+ settings.set_option(DisassemblyOption::ShowAddress, false);
+ settings.set_option(DisassemblyOption::WaitForIL, true);
+ settings.set_option(DisassemblyOption::IndentHLILBody, false);
+ settings.set_option(DisassemblyOption::ShowCollapseIndicators, false);
+ settings.set_option(DisassemblyOption::ShowFunctionHeader, false);
+
+ let linear_view = LinearViewObject::language_representation(view, &settings, "Pseudo C");
+
+ let mut cursor = linear_view.create_cursor();
+ cursor.seek_to_address(func.highest_address());
+
+ let last = view.get_next_linear_disassembly_lines(&mut cursor.duplicate());
+ let first = view.get_previous_linear_disassembly_lines(&mut cursor);
+
+ let lines = first.into_iter().chain(&last);
+
+ for line in lines {
+ println!("{}", line.as_ref());
+ }
+}
+
+pub fn main() {
+ let filename = std::env::args().nth(1).expect("No filename provided");
+
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
+
+ println!("Loading binary...");
+ let bv = headless_session
+ .load(&filename)
+ .expect("Couldn't open file!");
+
+ println!("Filename: `{}`", bv.file().filename());
+ println!("File size: `{:#x}`", bv.len());
+ println!("Function count: {}", bv.functions().len());
+
+ for func in &bv.functions() {
+ decompile_to_c(bv.as_ref(), func.as_ref());
+ }
+}
diff --git a/rust/examples/decompile/Cargo.toml b/rust/examples/decompile/Cargo.toml
deleted file mode 100644
index 63d61c26..00000000
--- a/rust/examples/decompile/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-name = "decompile"
-version = "0.1.0"
-authors = ["Fabian Freyer <mail@fabianfreyer.de>"]
-edition = "2021"
-
-[dependencies]
-binaryninja = {path="../../"}
-clap = { version = "4.5", features = ["derive"] }
-
diff --git a/rust/examples/decompile/build.rs b/rust/examples/decompile/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/decompile/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/decompile/src/main.rs b/rust/examples/decompile/src/main.rs
deleted file mode 100644
index f4f2a0ff..00000000
--- a/rust/examples/decompile/src/main.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
-use binaryninja::disassembly::{DisassemblyOption, DisassemblySettings};
-use binaryninja::function::Function;
-use binaryninja::linearview::{LinearViewCursor, LinearViewObject};
-
-use clap::Parser;
-
-/// Use binaryninja to decompile to C.
-#[derive(Parser, Debug)]
-#[clap(version, long_about = None)]
-struct Args {
- /// Path to the file to decompile
- filename: String,
-}
-
-fn decompile_to_c(view: &BinaryView, func: &Function) {
- let settings = DisassemblySettings::new();
- settings.set_option(DisassemblyOption::ShowAddress, false);
- settings.set_option(DisassemblyOption::WaitForIL, true);
-
- let linearview = LinearViewObject::language_representation(view, &settings, "Pseudo C");
-
- let mut cursor = LinearViewCursor::new(&linearview);
- cursor.seek_to_address(func.highest_address());
-
- let last = view.get_next_linear_disassembly_lines(&mut cursor.duplicate());
- let first = view.get_previous_linear_disassembly_lines(&mut cursor);
-
- let lines = first.into_iter().chain(&last);
-
- for line in lines {
- println!("{}", line.as_ref());
- }
-}
-
-fn main() {
- let args = Args::parse();
-
- eprintln!("Loading plugins...");
- binaryninja::headless::init();
-
- eprintln!("Loading binary...");
- let bv = binaryninja::load(args.filename).expect("Couldn't open file");
-
- eprintln!("Filename: `{}`", bv.file().filename());
- eprintln!("File size: `{:#x}`", bv.len());
- eprintln!("Function count: {}", bv.functions().len());
-
- for func in &bv.functions() {
- decompile_to_c(bv.as_ref(), func.as_ref());
- }
-
- binaryninja::headless::shutdown();
-}
diff --git a/rust/examples/demangler.rs b/rust/examples/demangler.rs
new file mode 100644
index 00000000..b2e5eb66
--- /dev/null
+++ b/rust/examples/demangler.rs
@@ -0,0 +1,59 @@
+use binaryninja::architecture::CoreArchitecture;
+use binaryninja::binary_view::BinaryView;
+use binaryninja::demangle::{CustomDemangler, Demangler};
+use binaryninja::rc::Ref;
+use binaryninja::types::{QualifiedName, Type};
+
+struct TestDemangler;
+
+impl CustomDemangler for TestDemangler {
+ fn is_mangled_string(&self, name: &str) -> bool {
+ name == "test_name" || name == "test_name2"
+ }
+
+ fn demangle(
+ &self,
+ _arch: &CoreArchitecture,
+ name: &str,
+ _view: Option<Ref<BinaryView>>,
+ ) -> Option<(QualifiedName, Option<Ref<Type>>)> {
+ match name {
+ "test_name" => Some((QualifiedName::from(vec!["test_name"]), Some(Type::bool()))),
+ "test_name2" => Some((QualifiedName::from(vec!["test_name2", "aaa"]), None)),
+ _ => None,
+ }
+ }
+}
+
+fn main() {
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let _headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
+
+ println!("Registering demangler...");
+ Demangler::register("Test", TestDemangler);
+
+ let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86 exists");
+
+ for d in Demangler::list().iter() {
+ println!("{}", d.name());
+
+ println!(
+ " \"__ZN1AC2Ei\" is mangled? {}",
+ d.is_mangled_string("__ZN1AC2Ei")
+ );
+ println!(
+ " \"__ZN1AC2Ei\" : {:?}",
+ d.demangle(&placeholder_arch, "__ZN1AC2Ei", None)
+ );
+ println!(
+ " \"test_name\" : {:?}",
+ d.demangle(&placeholder_arch, "test_name", None)
+ );
+ println!(
+ " \"test_name2\" : {:?}",
+ d.demangle(&placeholder_arch, "test_name2", None)
+ );
+ }
+}
diff --git a/rust/examples/dump_settings.rs b/rust/examples/dump_settings.rs
new file mode 100644
index 00000000..51452ca7
--- /dev/null
+++ b/rust/examples/dump_settings.rs
@@ -0,0 +1,21 @@
+use binaryninja::settings::Settings;
+
+fn main() {
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let _headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
+
+ let settings = Settings::new();
+ for key in &settings.keys() {
+ let value = settings.get_string(key);
+ let default_value = settings.get_property_string(key, "default");
+ let title = settings.get_property_string(key, "title");
+ let description = settings.get_property_string(key, "description");
+ println!("{}:", key);
+ println!(" value: {}", value);
+ println!(" default_value: {}", default_value);
+ println!(" title: {}", title);
+ println!(" description: {}", description);
+ }
+}
diff --git a/rust/examples/dwarf/dwarf_export/CMakeLists.txt b/rust/examples/dwarf/dwarf_export/CMakeLists.txt
deleted file mode 100644
index 5827c1d2..00000000
--- a/rust/examples/dwarf/dwarf_export/CMakeLists.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
-
-project(dwarf_export)
-
-file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS
- ${PROJECT_SOURCE_DIR}/Cargo.toml
- ${PROJECT_SOURCE_DIR}/src/*.rs
- ${PROJECT_SOURCE_DIR}/../shared/Cargo.toml
- ${PROJECT_SOURCE_DIR}/../shared/src/*.rs)
-
-file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
- ${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)
- set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}dwarf_export.pdb)
-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}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${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
deleted file mode 100644
index 715686f2..00000000
--- a/rust/examples/dwarf/dwarf_export/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "dwarf_export"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-binaryninja = {path="../../../"}
-gimli = "^0.31"
-log = "^0.4"
-object = { version = "0.32.1", features = ["write"] }
diff --git a/rust/examples/dwarf/dwarf_export/README.md b/rust/examples/dwarf/dwarf_export/README.md
deleted file mode 100644
index cea8e3b4..00000000
--- a/rust/examples/dwarf/dwarf_export/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# DWARF Export
diff --git a/rust/examples/dwarf/dwarf_export/build.rs b/rust/examples/dwarf/dwarf_export/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/dwarf/dwarf_export/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/dwarf/dwarf_export/src/edit_distance.rs b/rust/examples/dwarf/dwarf_export/src/edit_distance.rs
deleted file mode 100644
index 9f135451..00000000
--- a/rust/examples/dwarf/dwarf_export/src/edit_distance.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-pub(crate) fn distance(a: &str, b: &str) -> usize {
- if a == b {
- return 0;
- }
- match (a.chars().count(), b.chars().count()) {
- (0, b) => return b,
- (a, 0) => return a,
- // (a_len, b_len) if a_len < b_len => return distance(b, a),
- _ => (),
- }
-
- let mut result = 0;
- let mut cache: Vec<usize> = (1..a.chars().count() + 1).collect();
-
- for (index_b, char_b) in b.chars().enumerate() {
- result = index_b;
- let mut distance_a = index_b;
-
- for (index_a, char_a) in a.chars().enumerate() {
- let distance_b = if char_a == char_b {
- distance_a
- } else {
- distance_a + 1
- };
-
- distance_a = cache[index_a];
-
- result = if distance_a > result {
- if distance_b > result {
- result + 1
- } else {
- distance_b
- }
- } else if distance_b > distance_a {
- distance_a + 1
- } else {
- distance_b
- };
-
- cache[index_a] = result;
- }
- }
- result
-}
diff --git a/rust/examples/dwarf/dwarf_export/src/lib.rs b/rust/examples/dwarf/dwarf_export/src/lib.rs
deleted file mode 100644
index 9ebf3bc7..00000000
--- a/rust/examples/dwarf/dwarf_export/src/lib.rs
+++ /dev/null
@@ -1,793 +0,0 @@
-mod edit_distance;
-
-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},
- rc::Ref,
- string::BnString,
- symbol::SymbolType,
- types::{Conf, MemberAccess, StructureType, Type, TypeClass},
-};
-use log::{error, info, LevelFilter};
-use binaryninja::logger::Logger;
-
-fn export_type(
- name: String,
- t: &Type,
- bv: &BinaryView,
- defined_types: &mut Vec<(Ref<Type>, UnitEntryId)>,
- dwarf: &mut DwarfUnit,
-) -> Option<UnitEntryId> {
- if let Some((_, die)) = defined_types
- .iter()
- .find(|(defined_type, _)| defined_type.as_ref() == t)
- {
- return Some(*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()),
- );
- Some(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(name.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),
- );
- Some(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(name.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)
- },
- );
- Some(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(name.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),
- );
- Some(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));
-
- dwarf.unit.get_mut(structure_die_uid).set(
- gimli::DW_AT_name,
- AttributeValue::String(name.as_bytes().to_vec()),
- );
- dwarf.unit.get_mut(structure_die_uid).set(
- gimli::DW_AT_byte_size,
- AttributeValue::Data2(t.width() as u16),
- );
-
- 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_member_location,
- AttributeValue::Data8(struct_member.offset),
- );
-
- if let Some(target_die_uid) = export_type(
- format!("{}", struct_member.ty.contents),
- struct_member.ty.contents.as_ref(),
- bv,
- defined_types,
- dwarf,
- ) {
- dwarf
- .unit
- .get_mut(struct_member_die_uid)
- .set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
- }
- }
-
- Some(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(name.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),
- );
- }
-
- Some(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()
- {
- // TODO : Passing through the name here might be wrong
- if let Some(target_die_uid) =
- export_type(name, &target_type, bv, defined_types, dwarf)
- {
- dwarf
- .unit
- .get_mut(pointer_die_uid)
- .set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
- }
- }
- Some(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));
-
- // Name
- dwarf.unit.get_mut(array_die_uid).set(
- gimli::DW_AT_name,
- AttributeValue::String(name.as_bytes().to_vec()),
- );
-
- // Element type
- if let Ok(Conf {
- contents: element_type,
- ..
- }) = t.element_type()
- {
- // TODO : Passing through the name here might be wrong
- if let Some(target_die_uid) =
- export_type(name, &element_type, bv, defined_types, dwarf)
- {
- dwarf
- .unit
- .get_mut(array_die_uid)
- .set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
- }
- }
-
- // For some reason subrange types have a 'type' field that is just "some type" that'll work to index this array
- // We're hardcoding this to a uint64_t. This could be unsound.
- let array_accessor_type = export_type(
- "uint64_t".to_string(),
- &Type::named_int(8, false, "uint64_t"),
- bv,
- defined_types,
- dwarf,
- )
- .unwrap();
-
- // Array length and multidimensional arrays
- let mut current_t = t.to_owned();
- while let Ok(Conf {
- contents: element_type,
- ..
- }) = current_t.element_type()
- {
- let array_dimension_die_uid = dwarf
- .unit
- .add(array_die_uid, constants::DW_TAG_subrange_type);
-
- dwarf.unit.get_mut(array_dimension_die_uid).set(
- gimli::DW_AT_type,
- AttributeValue::UnitRef(array_accessor_type),
- );
-
- dwarf.unit.get_mut(array_dimension_die_uid).set(
- gimli::DW_AT_upper_bound,
- AttributeValue::Data8(current_t.count() - 1),
- );
-
- if element_type.type_class() != TypeClass::ArrayTypeClass {
- break;
- } else {
- current_t = element_type;
- }
- }
-
- Some(array_die_uid)
- }
- TypeClass::FunctionTypeClass => {
- Some(dwarf.unit.add(root, constants::DW_TAG_unspecified_type))
- }
- TypeClass::VarArgsTypeClass => {
- Some(dwarf.unit.add(root, constants::DW_TAG_unspecified_type))
- }
- TypeClass::ValueTypeClass => Some(dwarf.unit.add(root, constants::DW_TAG_unspecified_type)),
- TypeClass::NamedTypeReferenceClass => {
- let ntr = t.get_named_type_reference().unwrap();
- if let Some(target_type) = ntr.target(bv) {
- if target_type.type_class() == TypeClass::StructureTypeClass {
- export_type(
- ntr.name().to_string(),
- &target_type,
- bv,
- defined_types,
- dwarf,
- )
- } else {
- 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(ntr.name().to_string().as_bytes().to_vec()),
- );
-
- if let Some(target_die_uid) = export_type(
- ntr.name().to_string(),
- &target_type,
- bv,
- defined_types,
- dwarf,
- ) {
- dwarf
- .unit
- .get_mut(typedef_die_uid)
- .set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
- }
- Some(typedef_die_uid)
- }
- } else {
- error!("Could not get target of typedef `{}`", ntr.name());
- None
- }
- }
- 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(name.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)
- },
- );
- Some(wide_char_die_uid)
- }
- }
-}
-
-fn export_types(
- bv: &BinaryView,
- dwarf: &mut DwarfUnit,
- defined_types: &mut Vec<(Ref<Type>, UnitEntryId)>,
-) {
- for t in &bv.types() {
- export_type(
- t.name().to_string(),
- &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().short_name().as_bytes().to_vec()),
- );
-
- // 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())), // TODO: Relocations
- );
- dwarf.unit.get_mut(function_die_uid).set(
- gimli::DW_AT_high_pc,
- AttributeValue::Address(Address::Constant(address_range.end())),
- );
- } else {
- let range_list = RangeList(
- address_ranges
- .into_iter()
- .map(|range| Range::StartLength {
- begin: Address::Constant(range.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())),
- );
- }
-
- if function.return_type().contents.type_class() != TypeClass::VoidTypeClass {
- if let Some(return_type_die_uid) = export_type(
- format!("{}", function.return_type().contents),
- function.return_type().contents.as_ref(),
- bv,
- defined_types,
- dwarf,
- ) {
- dwarf.unit.get_mut(function_die_uid).set(
- gimli::DW_AT_type,
- AttributeValue::UnitRef(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()),
- );
-
- if let Some(target_die_uid) = export_type(
- format!("{}", parameter.t.contents),
- &parameter.t.contents,
- bv,
- defined_types,
- dwarf,
- ) {
- dwarf
- .unit
- .get_mut(param_die_uid)
- .set(gimli::DW_AT_type, AttributeValue::UnitRef(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 let SymbolType::External
- | SymbolType::Function
- | SymbolType::ImportedFunction
- | SymbolType::LibraryFunction = symbol.sym_type()
- {
- 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),
- );
-
- if let Some(target_die_uid) = export_type(
- format!("{}", data_variable.t()),
- data_variable.t(),
- bv,
- defined_types,
- dwarf,
- ) {
- dwarf
- .unit
- .get_mut(var_die_uid)
- .set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
- }
- }
-}
-
-fn present_form(bv_arch: &str) -> 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
- let archs = [
- "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",
- ];
- interaction::FormInputBuilder::new()
- .save_file_field(
- "Save Location",
- Some("Debug Files (*.dwo *.debug);;All Files (*)"),
- None,
- None,
- )
- .choice_field(
- "Architecture",
- &archs,
- archs
- .iter()
- .enumerate()
- .min_by(|&(_, arch_name_1), &(_, arch_name_2)| {
- edit_distance::distance(bv_arch, arch_name_1)
- .cmp(&edit_distance::distance(bv_arch, arch_name_2))
- })
- .map(|(index, _)| index),
- )
- // Add actual / better support for formats other than elf?
- // .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() < 2 {
- 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 : Look in to other options (mangling, flags, etc (see Object::new))
- let mut out_object = write::Object::new(
- BinaryFormat::Elf,
- 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.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 {
- info!("Successfully saved as DWARF to `{}`", filename);
- }
- } else {
- error!("Failed to write DWARF with requested settings");
- }
- }
-}
-
-fn export_dwarf(bv: &BinaryView) {
- let arch_name = if let Some(arch) = bv.default_arch() {
- arch.name()
- } else {
- BnString::new("Unknown")
- };
- let responses = present_form(arch_name.as_str());
-
- 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<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 {
- Logger::new("DWARF Export").with_level(LevelFilter::Debug).init();
-
- register(
- "Export as DWARF",
- "Export current analysis state and annotations as DWARF for import into other tools",
- MyCommand {},
- );
-
- true
-}
diff --git a/rust/examples/dwarf/dwarf_import/CMakeLists.txt b/rust/examples/dwarf/dwarf_import/CMakeLists.txt
deleted file mode 100644
index 9115b7a3..00000000
--- a/rust/examples/dwarf/dwarf_import/CMakeLists.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
-
-project(dwarf_import)
-
-file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS
- ${PROJECT_SOURCE_DIR}/Cargo.toml
- ${PROJECT_SOURCE_DIR}/src/*.rs
- ${PROJECT_SOURCE_DIR}/../shared/Cargo.toml
- ${PROJECT_SOURCE_DIR}/../shared/src/*.rs)
-
-file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
- ${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)
- set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}dwarf_import.pdb)
-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}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${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
deleted file mode 100644
index 0ea66894..00000000
--- a/rust/examples/dwarf/dwarf_import/Cargo.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[package]
-name = "dwarf_import"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-dwarfreader = { path = "../shared/" }
-binaryninja = { path = "../../../" }
-gimli = "0.31"
-log = "0.4.20"
-iset = "0.2.2"
-cpp_demangle = "0.4.3"
-regex = "1"
-indexmap = "2.5.0"
diff --git a/rust/examples/dwarf/dwarf_import/src/die_handlers.rs b/rust/examples/dwarf/dwarf_import/src/die_handlers.rs
deleted file mode 100644
index b2d61842..00000000
--- a/rust/examples/dwarf/dwarf_import/src/die_handlers.rs
+++ /dev/null
@@ -1,394 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
-use crate::{helpers::*, ReaderType};
-use crate::types::get_type;
-
-use binaryninja::{
- rc::*,
- types::{EnumerationBuilder, FunctionParameter, ReferenceType, Type, TypeBuilder},
-};
-
-use gimli::Dwarf;
-use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Unit};
-
-pub(crate) fn handle_base_type<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<R>,
-) -> Option<Ref<Type>> {
- // All base types have:
- // DW_AT_encoding (our concept of type_class)
- // DW_AT_byte_size and/or DW_AT_bit_size
- // *DW_AT_name
- // *DW_AT_endianity (assumed default for arch)
- // *DW_AT_data_bit_offset (assumed 0)
- // *Some indication of signedness?
- // * = Optional
-
- let name = debug_info_builder_context.get_name(dwarf, unit, entry)?;
- let size = get_size_as_usize(entry)?;
- match entry.attr_value(constants::DW_AT_encoding) {
- 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)),
- 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(crate) fn handle_enum<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<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 = debug_info_builder_context.get_name(dwarf, unit, child.entry())?;
- let attr = &child
- .entry()
- .attr(constants::DW_AT_const_value)
- .unwrap()
- .unwrap();
- if let Some(value) = get_attr_as_u64(attr) {
- enumeration_builder.insert(name, value);
- } else {
- log::error!("Unhandled enum member value type - please report this");
- return None;
- }
- }
- }
-
- let width = match get_size_as_usize(entry).unwrap_or(8)
- {
- 0 => debug_info_builder_context.default_address_size(),
- x => x
- };
-
- Some(Type::enumeration(
- &enumeration_builder.finalize(),
- width,
- false,
- ))
-}
-
-pub(crate) fn handle_typedef(
- debug_info_builder: &mut DebugInfoBuilder,
- entry_type: Option<TypeUID>,
- typedef_name: &String,
-) -> (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(t) = debug_info_builder.get_type(entry_type_offset) {
- return (Some(t.get_type()), typedef_name != t.get_name());
- }
- }
-
- // 5.3: "typedef represents a declaration of the type that is not also a definition"
- (None, false)
-}
-
-pub(crate) fn handle_pointer<R: ReaderType>(
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<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().get_type();
- 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 if let Some(entry_type_offset) = entry_type {
- let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type();
- Some(Type::pointer_of_width(
- parent_type.as_ref(),
- debug_info_builder_context.default_address_size(),
- false,
- false,
- Some(reference_type),
- ))
- } else {
- Some(Type::pointer_of_width(
- Type::void().as_ref(),
- debug_info_builder_context.default_address_size(),
- false,
- false,
- Some(reference_type),
- ))
- }
-}
-
-pub(crate) fn handle_array<R: ReaderType>(
- 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().get_type();
-
- 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(crate) fn handle_function<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<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")
- .get_type()
- }
- None => Type::void(),
- };
-
- // Alias function type in the case that it contains itself
- if let Some(name) = debug_info_builder_context.get_name(dwarf, unit, entry) {
- debug_info_builder.add_type(
- get_uid(dwarf, unit, entry),
- &name,
- Type::named_type_from_type(
- &name,
- &Type::function::<&binaryninja::types::Type>(return_type.as_ref(), &[], false),
- ),
- false,
- );
- }
-
- let mut parameters: Vec<FunctionParameter> = vec![];
- let mut variable_arguments = 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_formal_parameter {
- if let (Some(child_uid), Some(name)) = {
- (
- get_type(
- dwarf,
- unit,
- child.entry(),
- debug_info_builder_context,
- debug_info_builder,
- ),
- debug_info_builder_context.get_name(dwarf, unit, child.entry()),
- )
- } {
- let child_type = debug_info_builder.get_type(child_uid).unwrap().get_type();
- parameters.push(FunctionParameter::new(child_type, name, None));
- }
- } else if child.entry().tag() == constants::DW_TAG_unspecified_parameters {
- variable_arguments = true;
- }
- }
-
- if debug_info_builder_context.get_name(dwarf, unit, entry).is_some() {
- debug_info_builder.remove_type(get_uid(dwarf, unit, entry));
- }
-
- Some(Type::function(
- return_type.as_ref(),
- &parameters,
- variable_arguments,
- ))
-}
-
-pub(crate) 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().get_type();
- Some((*parent_type).to_builder().set_const(true).finalize())
- } else {
- Some(TypeBuilder::void().set_const(true).finalize())
- }
-}
-
-pub(crate) 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().get_type();
- Some((*parent_type).to_builder().set_volatile(true).finalize())
- } else {
- Some(TypeBuilder::void().set_volatile(true).finalize())
- }
-}
diff --git a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
deleted file mode 100644
index 168ddaaa..00000000
--- a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
+++ /dev/null
@@ -1,643 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use crate::{helpers::{get_uid, resolve_specification, DieReference}, ReaderType};
-
-use binaryninja::{
- binaryview::{BinaryView, BinaryViewBase, BinaryViewExt},
- debuginfo::{DebugFunctionInfo, DebugInfo},
- platform::Platform,
- rc::*,
- symbol::SymbolType,
- templatesimplifier::simplify_str_to_fqn,
- types::{Conf, FunctionParameter, NamedTypedVariable, Type, Variable, VariableSourceType},
-};
-
-use gimli::{DebuggingInformationEntry, Dwarf, Unit};
-
-use indexmap::{map::Values, IndexMap};
-use log::{debug, error, warn};
-use std::{
- cmp::Ordering,
- collections::HashMap,
- hash::Hash,
-};
-
-pub(crate) type TypeUID = usize;
-
-/////////////////////////
-// FunctionInfoBuilder
-
-// TODO : Function local variables
-#[derive(PartialEq, Eq, Hash)]
-pub(crate) struct FunctionInfoBuilder {
- pub(crate) full_name: Option<String>,
- pub(crate) raw_name: Option<String>,
- pub(crate) return_type: Option<TypeUID>,
- pub(crate) address: Option<u64>,
- pub(crate) parameters: Vec<Option<(String, TypeUID)>>,
- pub(crate) platform: Option<Ref<Platform>>,
- pub(crate) variable_arguments: bool,
- pub(crate) stack_variables: Vec<NamedTypedVariable>,
- pub(crate) use_cfa: bool, //TODO actually store more info about the frame base
-}
-
-impl FunctionInfoBuilder {
- pub(crate) fn update(
- &mut self,
- full_name: Option<String>,
- raw_name: Option<String>,
- return_type: Option<TypeUID>,
- address: Option<u64>,
- parameters: &Vec<Option<(String, 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() {
- match self.parameters.get(i) {
- Some(None) => self.parameters[i] = new_parameter.clone(),
- Some(Some(_)) => (),
- // Some(Some((name, _))) if name.as_bytes().is_empty() => {
- // self.parameters[i] = new_parameter
- // }
- // Some(Some((_, uid))) if *uid == 0 => self.parameters[i] = new_parameter, // TODO : This is a placebo....void types aren't actually UID 0
- _ => self.parameters.push(new_parameter.clone()),
- }
- }
- }
-}
-
-//////////////////////
-// DebugInfoBuilder
-
-// TODO : Don't make this pub...fix the value thing
-pub(crate) struct DebugType {
- name: String,
- t: Ref<Type>,
- commit: bool,
-}
-
-impl DebugType {
- pub fn get_name(&self) -> &String {
- &self.name
- }
-
- pub fn get_type(&self) -> Ref<Type> {
- self.t.clone()
- }
-}
-
-pub(crate) struct DebugInfoBuilderContext<R: ReaderType> {
- units: Vec<Unit<R>>,
- sup_units: Vec<Unit<R>>,
- names: HashMap<TypeUID, String>,
- default_address_size: usize,
- pub(crate) total_die_count: usize,
- pub(crate) total_unit_size_bytes: usize,
-}
-
-impl<R: ReaderType> DebugInfoBuilderContext<R> {
- pub(crate) fn new(view: &BinaryView, dwarf: &Dwarf<R>) -> Option<Self> {
-
- let mut units = vec![];
- let mut iter = dwarf.units();
- while let Ok(Some(header)) = iter.next() {
- if let Ok(unit) = dwarf.unit(header) {
- units.push(unit);
- } else {
- error!("Unable to read DWARF information. File may be malformed or corrupted. Not applying debug info.");
- return None;
- }
- }
-
- let mut sup_units = vec![];
- if let Some(sup_dwarf) = dwarf.sup() {
- let mut sup_iter = sup_dwarf.units();
- while let Ok(Some(header)) = sup_iter.next() {
- if let Ok(unit) = sup_dwarf.unit(header) {
- sup_units.push(unit);
- } else {
- error!("Unable to read supplementary DWARF information. File may be malformed or corrupted. Not applying debug info.");
- return None;
- }
- }
- }
-
- Some(Self {
- units,
- sup_units,
- names: HashMap::new(),
- default_address_size: view.address_size(),
- total_die_count: 0,
- total_unit_size_bytes: 0,
- })
- }
-
- pub(crate) fn units(&self) -> &[Unit<R>] {
- &self.units
- }
-
- pub(crate) fn sup_units(&self) -> &[Unit<R>] {
- &self.sup_units
- }
-
- pub(crate) fn default_address_size(&self) -> usize {
- self.default_address_size
- }
-
- pub(crate) fn set_name(&mut self, die_uid: TypeUID, name: String) {
- // die_uids need to be unique here
- assert!(self.names.insert(die_uid, name).is_none());
- }
-
- pub(crate) fn get_name(
- &self,
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- ) -> Option<String> {
- match resolve_specification(dwarf, unit, entry, self) {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => self
- .names
- .get(&get_uid(
- dwarf,
- entry_unit,
- &entry_unit.entry(entry_offset).unwrap(),
- ))
- .cloned(),
- DieReference::Err => None,
- }
- }
-}
-
-// 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(crate) struct DebugInfoBuilder {
- functions: Vec<FunctionInfoBuilder>,
- raw_function_name_indices: HashMap<String, usize>,
- full_function_name_indices: HashMap<String, usize>,
- types: IndexMap<TypeUID, DebugType>,
- data_variables: HashMap<u64, (Option<String>, TypeUID)>,
- range_data_offsets: iset::IntervalMap<u64, i64>
-}
-
-impl DebugInfoBuilder {
- pub(crate) fn new() -> Self {
- Self {
- functions: vec![],
- raw_function_name_indices: HashMap::new(),
- full_function_name_indices: HashMap::new(),
- types: IndexMap::new(),
- data_variables: HashMap::new(),
- range_data_offsets: iset::IntervalMap::new(),
- }
- }
-
- pub(crate) fn set_range_data_offsets(&mut self, offsets: iset::IntervalMap<u64, i64>) {
- self.range_data_offsets = offsets
- }
-
- #[allow(clippy::too_many_arguments)]
- pub(crate) fn insert_function(
- &mut self,
- full_name: Option<String>,
- raw_name: Option<String>,
- return_type: Option<TypeUID>,
- address: Option<u64>,
- parameters: &Vec<Option<(String, TypeUID)>>,
- variable_arguments: bool,
- use_cfa: bool,
- ) -> Option<usize> {
- // Returns the index of the function
- // Raw names should be the primary key, but if they don't exist, use the full name
- // TODO : Consider further falling back on address/architecture
-
- /*
- If it has a raw_name and we know it, update it and return
- Else if it has a full_name and we know it, update it and return
- Else Add a new entry if we don't know the full_name or raw_name
- */
-
- if let Some(ident) = &raw_name {
- // check if we already know about this raw name's index
- // if we do, and the full name will change, remove the known full index if it exists
- // update the function
- // if the full name exists, update the stored index for the full name
- if let Some(idx) = self.raw_function_name_indices.get(ident) {
- let function = self.functions.get_mut(*idx).unwrap();
-
- if function.full_name.is_some() && function.full_name != full_name {
- self.full_function_name_indices.remove(function.full_name.as_ref().unwrap());
- }
-
- function.update(full_name, raw_name, return_type, address, parameters);
-
- if function.full_name.is_some() {
- self.full_function_name_indices.insert(function.full_name.clone().unwrap(), *idx);
- }
-
- return Some(*idx);
- }
- }
- else if let Some(ident) = &full_name {
- // check if we already know about this full name's index
- // if we do, and the raw name will change, remove the known raw index if it exists
- // update the function
- // if the raw name exists, update the stored index for the raw name
- if let Some(idx) = self.full_function_name_indices.get(ident) {
- let function = self.functions.get_mut(*idx).unwrap();
-
- if function.raw_name.is_some() && function.raw_name != raw_name {
- self.raw_function_name_indices.remove(function.raw_name.as_ref().unwrap());
- }
-
- function.update(full_name, raw_name, return_type, address, parameters);
-
- if function.raw_name.is_some() {
- self.raw_function_name_indices.insert(function.raw_name.clone().unwrap(), *idx);
- }
-
- return Some(*idx);
- }
- }
- else {
- debug!("Function entry in DWARF without full or raw name.");
- return None;
- }
-
- let function = FunctionInfoBuilder {
- full_name,
- raw_name,
- return_type,
- address,
- parameters: parameters.clone(),
- platform: None,
- variable_arguments,
- stack_variables: vec![],
- use_cfa,
- };
-
- if let Some(n) = &function.full_name {
- self.full_function_name_indices.insert(n.clone(), self.functions.len());
- }
-
- if let Some(n) = &function.raw_name {
- self.raw_function_name_indices.insert(n.clone(), self.functions.len());
- }
-
- self.functions.push(function);
- Some(self.functions.len()-1)
- }
-
- pub(crate) fn functions(&self) -> &[FunctionInfoBuilder] {
- &self.functions
- }
-
- #[allow(dead_code)]
- pub(crate) fn types(&self) -> Values<'_, TypeUID, DebugType> {
- self.types.values()
- }
-
- pub(crate) fn add_type(&mut self, type_uid: TypeUID, name: &String, t: Ref<Type>, commit: bool) {
- if let Some(DebugType {
- name: existing_name,
- t: existing_type,
- commit: _,
- }) = self.types.insert(
- type_uid,
- DebugType {
- name: name.clone(),
- t: t.clone(),
- commit,
- },
- ) {
- if existing_type != t && commit {
- warn!("DWARF info contains duplicate type definition. Overwriting type `{}` (named `{:?}`) with `{}` (named `{:?}`)",
- existing_type,
- existing_name,
- t,
- name
- );
- }
- }
- }
-
- pub(crate) fn remove_type(&mut self, type_uid: TypeUID) {
- self.types.swap_remove(&type_uid);
- }
-
- pub(crate) fn get_type(&self, type_uid: TypeUID) -> Option<&DebugType> {
- self.types.get(&type_uid)
- }
-
- pub(crate) fn contains_type(&self, type_uid: TypeUID) -> bool {
- self.types.contains_key(&type_uid)
- }
-
-
- pub(crate) fn add_stack_variable(
- &mut self,
- fn_idx: Option<usize>,
- offset: i64,
- name: Option<String>,
- type_uid: Option<TypeUID>,
- lexical_block: Option<&iset::IntervalSet<u64>>,
- ) {
- let name = match name {
- Some(x) => {
- if x.len() == 1 && x.chars().next() == Some('\x00') {
- // Anonymous variable, generate name
- format!("debug_var_{}", offset)
- }
- else {
- x
- }
- },
- None => {
- // Anonymous variable, generate name
- format!("debug_var_{}", offset)
- }
- };
-
- let Some(function_index) = fn_idx else {
- // If we somehow lost track of what subprogram we're in or we're not actually in a subprogram
- error!("Trying to add a local variable outside of a subprogram. Please report this issue.");
- return;
- };
-
- // Either get the known type or use a 0 confidence void type so we at least get the name applied
- let t = match type_uid {
- Some(uid) => Conf::new(self.get_type(uid).unwrap().get_type(), 128),
- None => Conf::new(Type::void(), 0)
- };
- let function = &mut self.functions[function_index];
-
- // TODO: If we can't find a known offset can we try to guess somehow?
-
- let Some(func_addr) = function.address else {
- // If we somehow are processing a function's variables before the function is created
- error!("Trying to add a local variable without a known function start. Please report this issue.");
- return;
- };
-
- let adjusted_offset;
-
- let Some(adjustment_at_variable_lifetime_start) = lexical_block.and_then(|block_ranges| {
- block_ranges
- .unsorted_iter()
- .find_map(|x| self.range_data_offsets.values_overlap(x.start).next())
- }).or_else(|| {
- // Try using the offset at the adjustment 4 bytes after the function start, in case the function starts with a stack adjustment
- // TODO: This is a decent heuristic but not perfect, since further adjustments could still be made
- self.range_data_offsets.values_overlap(func_addr+4).next()
- }).or_else(|| {
- // If all else fails, use the function start address
- self.range_data_offsets.values_overlap(func_addr).next()
- }) else {
- // Unknown why, but this is happening with MachO + external dSYM
- debug!("Refusing to add a local variable ({}@{}) to function at {} without a known CIE offset.", name, offset, func_addr);
- return;
- };
-
- // TODO: handle non-sp frame bases
- // TODO: if not in a lexical block these can be wrong, see https://github.com/Vector35/binaryninja-api/issues/5882#issuecomment-2406065057
- if function.use_cfa {
- // Apply CFA offset to variable storage offset if DW_AT_frame_base is frame base is CFA
- adjusted_offset = offset + adjustment_at_variable_lifetime_start;
- }
- else {
- // If it's using SP, we know the SP offset is <SP offset> + (<entry SP CFA offset> - <SP CFA offset>)
- let Some(adjustment_at_entry) = self.range_data_offsets.values_overlap(func_addr).next() else {
- // Unknown why, but this is happening with MachO + external dSYM
- debug!("Refusing to add a local variable ({}@{}) to function at {} without a known CIE offset for function start.", name, offset, func_addr);
- return;
- };
-
- adjusted_offset = offset + (adjustment_at_entry - adjustment_at_variable_lifetime_start);
- }
-
- if adjusted_offset > 0 {
- // If we somehow end up with a positive sp offset
- error!("Trying to add a local variable \"{}\" in function at {:#x} at positive storage offset {}. Please report this issue.", name, func_addr, adjusted_offset);
- return;
- }
-
- let var = Variable::new(VariableSourceType::StackVariableSourceType, 0, adjusted_offset);
- function.stack_variables.push(NamedTypedVariable::new(var, name, t, false));
-
- }
-
- pub(crate) fn add_data_variable(
- &mut self,
- address: u64,
- name: Option<String>,
- type_uid: TypeUID,
- ) {
- if let Some((_existing_name, existing_type_uid)) =
- self.data_variables.insert(address, (name, type_uid))
- {
- let existing_type = self.get_type(existing_type_uid).unwrap().get_type();
- let new_type = self.get_type(type_uid).unwrap().get_type();
-
- if existing_type_uid != type_uid || existing_type != new_type {
- warn!("DWARF info contains duplicate data variable definition. Overwriting data variable at 0x{:08x} (`{}`) with `{}`",
- address,
- existing_type,
- new_type
- );
- }
- }
- }
-
- fn commit_types(&self, debug_info: &mut DebugInfo) {
- let mut type_uids_by_name: HashMap<String, TypeUID> = HashMap::new();
-
- for (debug_type_uid, debug_type) in self.types.iter() {
- if !debug_type.commit {
- continue;
- }
-
- let mut debug_type_name = debug_type.name.clone();
-
- // Prevent storing two types with the same name and differing definitions
- if let Some(stored_uid) = type_uids_by_name.get(&debug_type_name) {
- let Some(stored_debug_type) = self.types.get(stored_uid) else {
- error!("Stored type name without storing a type! Please report this error. UID: {}, name: {}", stored_uid, debug_type_name);
- continue;
- };
-
- let mut skip_adding_type = false;
- if stored_debug_type.t != debug_type.t {
- // We already stored a type with this name and it's a different type, deconflict the name and try again
- let mut i = 1;
- loop {
- if let Some(stored_uid) = type_uids_by_name.get(&debug_type_name) {
- if debug_type_uid == stored_uid {
- // We already have a type with this name but it's the same type so we're ok
- skip_adding_type = true;
- break;
- }
- if let Some(stored_debug_type) = self.types.get(stored_uid) {
- if stored_debug_type.t == debug_type.t {
- // We already have a type with this name but it's the same type so we're ok
- skip_adding_type = true;
- break;
- }
- }
-
- debug_type_name = format!("{}_{}", debug_type.name, i);
- i += 1;
- }
- else {
- // We found a unique name
- break;
- }
- }
- }
-
- if skip_adding_type {
- continue;
- }
- };
-
- type_uids_by_name.insert(debug_type_name.clone(), *debug_type_uid);
- debug_info.add_type(debug_type_name, debug_type.t.as_ref(), &[]);
- // TODO : Components
- }
- }
-
- // 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().t,
- name.clone(),
- &[] // TODO : Components
- ));
- }
- }
-
- fn get_function_type(&self, function: &FunctionInfoBuilder) -> Ref<Type> {
- let return_type = match function.return_type {
- Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().get_type(), 128),
- _ => Conf::new(binaryninja::types::Type::void(), 0),
- };
-
- let parameters: Vec<FunctionParameter> = function
- .parameters
- .iter()
- .filter_map(|parameter| match parameter {
- Some((name, 0)) => Some(FunctionParameter::new(Type::void(), name.clone(), None)),
- Some((name, uid)) => Some(FunctionParameter::new(
- self.get_type(*uid).unwrap().get_type(),
- name.clone(),
- None,
- )),
- _ => None,
- })
- .collect();
-
- binaryninja::types::Type::function(&return_type, &parameters, function.variable_arguments)
- }
-
- fn commit_functions(&self, debug_info: &mut DebugInfo) {
- for function in self.functions() {
- // let calling_convention: Option<Ref<CallingConvention<CoreArchitecture>>> = None;
-
- debug_info.add_function(DebugFunctionInfo::new(
- function.full_name.clone(),
- function.full_name.clone(), // 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(self.get_function_type(function)),
- function.address,
- function.platform.clone(),
- vec![], // TODO : Components
- function.stack_variables.clone(), // TODO: local non-stack variables
- ));
- }
- }
-
- pub(crate) fn post_process(&mut self, bv: &BinaryView, _debug_info: &mut DebugInfo) -> &Self {
- // When originally resolving names, we need to check:
- // If there's already a name from binja that's "more correct" than what we found (has more namespaces)
- // If there's no name for the DIE, but there's a linkage name that's resolved in binja to a usable name
- // This is no longer true, because DWARF doesn't provide platform information for functions, so we at least need to post-process thumb functions
-
- for func in &mut self.functions {
- // If the function's raw name already exists in the binary...
- if let Some(raw_name) = &func.raw_name {
- if let Ok(symbol) = bv.symbol_by_raw_name(raw_name) {
- // Link mangled names without addresses to existing symbols in the binary
- if func.address.is_none() && func.raw_name.is_some() {
- // DWARF doesn't contain GOT info, so remove any entries there...they will be wrong (relying on Binja's mechanisms for the GOT is good )
- if symbol.sym_type() != SymbolType::ImportAddress {
- func.address = Some(symbol.address());
- }
- }
-
- if let Some(full_name) = &func.full_name {
- let func_full_name = full_name;
- let symbol_full_name = symbol.full_name();
-
- // If our name has fewer namespaces than the existing name, assume we lost the namespace info
- if simplify_str_to_fqn(func_full_name, true).len()
- < simplify_str_to_fqn(symbol_full_name.clone(), true).len()
- {
- func.full_name = Some(symbol_full_name.to_string());
- }
- }
- }
- }
-
- if let Some(address) = func.address.as_mut() {
- let (diff, overflowed) = bv.start().overflowing_sub(bv.original_image_base());
- if !overflowed {
- *address = (*address).overflowing_add(diff).0; // rebase the address
- let existing_functions = bv.functions_at(*address);
- match existing_functions.len().cmp(&1) {
- Ordering::Greater => {
- warn!("Multiple existing functions at address {address:08x}. One or more functions at this address may have the wrong platform information. Please report this binary.");
- }
- Ordering::Equal => func.platform = Some(existing_functions.get(0).platform()),
- Ordering::Less => {}
- }
- }
- }
- }
-
- self
- }
-
- pub(crate) 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
deleted file mode 100644
index 43b69ca3..00000000
--- a/rust/examples/dwarf/dwarf_import/src/functions.rs
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::sync::OnceLock;
-
-use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
-use crate::{helpers::*, ReaderType};
-use crate::types::get_type;
-
-use binaryninja::templatesimplifier::simplify_str_to_str;
-use cpp_demangle::DemangleOptions;
-use gimli::{constants, AttributeValue, DebuggingInformationEntry, Dwarf, Operation, Unit};
-use log::{debug, error};
-use regex::Regex;
-
-fn get_parameters<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<R>,
- debug_info_builder: &mut DebugInfoBuilder,
-) -> (Vec<Option<(String, TypeUID)>>, bool) {
- if !entry.has_children() {
- return (vec![], false);
- }
-
- // We make a new tree from the current entry to iterate over its children
- let mut sub_die_tree = unit.entries_tree(Some(entry.offset())).unwrap();
- let root = sub_die_tree.root().unwrap();
-
- let mut variable_arguments = false;
- 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 => {
- //TODO: if the param type is a typedef to an anonymous struct (typedef struct {...} foo) then this is reoslved to an anonymous struct instead of foo
- // We should still recurse to make sure we load all types this param type depends on, but
- let name = debug_info_builder_context.get_name(dwarf, unit, child.entry());
-
- let type_ = get_type(
- dwarf,
- unit,
- child.entry(),
- debug_info_builder_context,
- debug_info_builder,
- );
- if let Some(parameter_name) = name {
- if let Some(parameter_type) = type_ {
- result.push(Some((parameter_name, parameter_type)));
- } else {
- result.push(Some((parameter_name, 0)))
- }
- } else {
- result.push(None)
- }
- }
- constants::DW_TAG_unspecified_parameters => variable_arguments = true,
- _ => (),
- }
- }
- (result, variable_arguments)
-}
-
-pub(crate) fn parse_function_entry<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<R>,
- debug_info_builder: &mut DebugInfoBuilder,
-) -> Option<usize> {
- // Collect function properties (if they exist in this DIE)
- let raw_name = get_raw_name(dwarf, unit, entry);
- let return_type = get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
- let address = get_start_address(dwarf, unit, entry);
- let (parameters, variable_arguments) = get_parameters(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
-
- // If we have a raw name, it might be mangled, see if we can demangle it into full_name
- // raw_name should contain a superset of the info we have in full_name
- let mut full_name = None;
- if let Some(possibly_mangled_name) = &raw_name {
- if possibly_mangled_name.starts_with('_') {
- static OPTIONS_MEM: OnceLock<DemangleOptions> = OnceLock::new();
- let demangle_options = OPTIONS_MEM.get_or_init(|| {
- DemangleOptions::new()
- .no_return_type()
- .hide_expression_literal_types()
- .no_params()
- });
-
- static ABI_REGEX_MEM: OnceLock<Regex> = OnceLock::new();
- let abi_regex = ABI_REGEX_MEM.get_or_init(|| {
- Regex::new(r"\[abi:v\d+\]").unwrap()
- });
- if let Ok(sym) = cpp_demangle::Symbol::new(possibly_mangled_name) {
- if let Ok(demangled) = sym.demangle(demangle_options) {
- let cleaned = abi_regex.replace_all(&demangled, "");
- let simplified = simplify_str_to_str(&cleaned);
- full_name = Some(simplified.to_string());
- }
- }
- }
- }
-
- // If we didn't demangle the raw name, fetch the name given
- if full_name.is_none() {
- full_name = debug_info_builder_context.get_name(dwarf, unit, entry)
- }
-
- if raw_name.is_none() && full_name.is_none() {
- debug!(
- "Function entry in DWARF without full or raw name: .debug_info offset {:?}",
- entry.offset().to_debug_info_offset(&unit.header)
- );
- return None;
- }
-
- let use_cfa;
- if let Ok(Some(AttributeValue::Exprloc(mut expression))) = entry.attr_value(constants::DW_AT_frame_base) {
- use_cfa = match Operation::parse(&mut expression.0, unit.encoding()) {
- Ok(Operation::Register { register: _ }) => false, // TODO: handle register-relative encodings later
- Ok(Operation::CallFrameCFA) => true,
- _ => false
- };
- }
- else {
- use_cfa = false;
- }
-
- debug_info_builder.insert_function(full_name, raw_name, return_type, address, &parameters, variable_arguments, use_cfa)
-}
-
-
-pub(crate) fn parse_lexical_block<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
-) -> Option<iset::IntervalSet<u64>> {
- // Return lexical block ranges
- // Must have either DW_AT_ranges or DW_AT_low_pc and DW_AT_high_pc
- let mut result = iset::IntervalSet::new();
- 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)
- {
- while let Ok(Some(range)) = ranges.next() {
- // Ranges where start == end may be ignored (DWARFv5 spec, 2.17.3 line 17)
- if range.begin == range.end {
- continue
- }
- result.insert(range.begin..range.end);
- }
- }
- }
- }
- else if let Ok(Some(low_pc_value)) = entry.attr_value(constants::DW_AT_low_pc) {
- let Ok(Some(low_pc)) = dwarf.attr_address(unit, low_pc_value.clone()) else {
- let unit_base: usize = unit.header.offset().as_debug_info_offset().unwrap().0;
- error!("Failed to read lexical block low_pc for entry {:#x}, please report this bug.", unit_base + entry.offset().0);
- return None;
- };
-
- let Ok(Some(high_pc_value)) = entry.attr_value(constants::DW_AT_high_pc) else {
- let unit_base: usize = unit.header.offset().as_debug_info_offset().unwrap().0;
- error!("Failed to read lexical block high_pc attribute for entry {:#x}, please report this bug.", unit_base + entry.offset().0);
- return None;
- };
-
- let Some(high_pc) = high_pc_value
- .udata_value()
- .and_then(|x| Some(low_pc + x))
- .or_else(|| dwarf.attr_address(unit, high_pc_value).unwrap_or(None))
- else {
- let unit_base: usize = unit.header.offset().as_debug_info_offset().unwrap().0;
- error!("Failed to read lexical block high_pc for entry {:#x}, please report this bug.", unit_base + entry.offset().0);
- return None;
- };
-
- if low_pc < high_pc {
- result.insert(low_pc..high_pc);
- }
- else {
- error!("Invalid lexical block range: {:#x} -> {:#x}", low_pc, high_pc);
- }
- }
- else {
- // If neither case is hit the lexical block doesn't define any ranges and we should ignore it
- return None;
- }
-
- Some(result)
-}
diff --git a/rust/examples/dwarf/dwarf_import/src/helpers.rs b/rust/examples/dwarf/dwarf_import/src/helpers.rs
deleted file mode 100644
index 6aba99d4..00000000
--- a/rust/examples/dwarf/dwarf_import/src/helpers.rs
+++ /dev/null
@@ -1,600 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::ffi::OsStr;
-use std::path::{Path, PathBuf};
-use std::{
- collections::HashMap,
- ops::Deref,
- sync::mpsc,
- str::FromStr
-};
-
-use crate::{DebugInfoBuilderContext, ReaderType};
-use binaryninja::binaryview::BinaryViewBase;
-use binaryninja::filemetadata::FileMetadata;
-use binaryninja::Endianness;
-use binaryninja::{binaryview::{BinaryView, BinaryViewExt}, downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider}, rc::Ref, settings::Settings};
-use gimli::Dwarf;
-use gimli::{
- constants, Attribute, AttributeValue,
- AttributeValue::{DebugInfoRef, DebugInfoRefSup, UnitRef},
- DebuggingInformationEntry, Operation, Unit, UnitOffset, UnitSectionOffset,
-};
-
-use log::warn;
-
-pub(crate) fn get_uid<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
-) -> usize {
- // We set a large gap between supplementary and main entries
- let adj = dwarf.sup().map_or(0, |_| 0x1000000000000000);
- let entry_offset = match entry.offset().to_unit_section_offset(unit) {
- UnitSectionOffset::DebugInfoOffset(o) => o.0,
- UnitSectionOffset::DebugTypesOffset(o) => o.0,
- };
- entry_offset + adj
-}
-
-////////////////////////////////////
-// DIE attr convenience functions
-
-pub(crate) enum DieReference<'a, R: ReaderType> {
- UnitAndOffset((&'a Dwarf<R>, &'a Unit<R>, UnitOffset)),
- Err,
-}
-
-pub(crate) fn get_attr_die<'a, R: ReaderType>(
- dwarf: &'a Dwarf<R>,
- unit: &'a Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &'a DebugInfoBuilderContext<R>,
- attr: constants::DwAt,
-) -> Option<DieReference<'a, R>> {
- match entry.attr_value(attr) {
- Ok(Some(UnitRef(offset))) => Some(DieReference::UnitAndOffset((dwarf, unit, offset))),
- Ok(Some(DebugInfoRef(offset))) => {
- if dwarf.sup().is_some() {
- for source_unit in debug_info_builder_context.units() {
- if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
- return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset)));
- }
- }
- }
- else {
- // This could either have no supplementary file because it is one or because it just doesn't have one
- // operate on supplementary file if dwarf is a supplementary file, else self
-
- // It's possible this is a reference in the supplementary file to itself
- for source_unit in debug_info_builder_context.sup_units() {
- if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
- return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset)));
- }
- }
-
- // ... or it just doesn't have a supplementary file
- for source_unit in debug_info_builder_context.units() {
- if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
- return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset)));
- }
- }
- }
-
- None
- },
- Ok(Some(DebugInfoRefSup(offset))) => {
- for source_unit in debug_info_builder_context.sup_units() {
- if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
- return Some(DieReference::UnitAndOffset((dwarf.sup().unwrap(), source_unit, new_offset)));
- }
- }
- warn!("Failed to fetch DIE. Supplementary debug information may be incomplete.");
- None
- },
- _ => None,
- }
-}
-
-pub(crate) fn resolve_specification<'a, R: ReaderType>(
- dwarf: &'a Dwarf<R>,
- unit: &'a Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &'a DebugInfoBuilderContext<R>,
-) -> DieReference<'a, R> {
- if let Some(die_reference) = get_attr_die(
- dwarf,
- unit,
- entry,
- debug_info_builder_context,
- constants::DW_AT_specification,
- ) {
- match die_reference {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
- if let Ok(entry) = entry_unit.entry(entry_offset) {
- resolve_specification(dwarf, entry_unit, &entry, debug_info_builder_context)
- } else {
- warn!("Failed to fetch DIE for attr DW_AT_specification. Debug information may be incomplete.");
- DieReference::Err
- }
- }
- DieReference::Err => DieReference::Err,
- }
- } else if let Some(die_reference) = get_attr_die(
- dwarf,
- unit,
- entry,
- debug_info_builder_context,
- constants::DW_AT_abstract_origin,
- ) {
- match die_reference {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
- if entry_offset == entry.offset() && unit.header.offset() == entry_unit.header.offset() {
- warn!("DWARF information is invalid (infinite abstract origin reference cycle). Debug information may be incomplete.");
- DieReference::Err
- } else if let Ok(new_entry) = entry_unit.entry(entry_offset) {
- resolve_specification(dwarf, entry_unit, &new_entry, debug_info_builder_context)
- } else {
- warn!("Failed to fetch DIE for attr DW_AT_abstract_origin. Debug information may be incomplete.");
- DieReference::Err
- }
- }
- DieReference::Err => DieReference::Err,
- }
- } else {
- DieReference::UnitAndOffset((dwarf, unit, entry.offset()))
- }
-}
-
-// Get name from DIE, or referenced dependencies
-pub(crate) fn get_name<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<R>,
-) -> Option<String> {
- match resolve_specification(dwarf, unit, entry, debug_info_builder_context) {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
- if let Ok(Some(attr_val)) = entry_unit
- .entry(entry_offset)
- .unwrap()
- .attr_value(constants::DW_AT_name)
- {
- if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val.clone())
- {
- if let Ok(attr_string) = attr_string.to_string() {
- return Some(attr_string.to_string());
- }
- }
- else if let Some(dwarf) = &dwarf.sup {
- if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val)
- {
- if let Ok(attr_string) = attr_string.to_string() {
- return Some(attr_string.to_string());
- }
- }
- }
- }
-
- // if let Some(raw_name) = get_raw_name(unit, entry, debug_info_builder_context) {
- // if let Some(arch) = debug_info_builder_context.default_architecture() {
- // if let Ok((_, names)) = demangle_gnu3(&arch, raw_name, true) {
- // return Some(names.join("::"));
- // }
- // }
- // }
- None
- }
- DieReference::Err => None,
- }
-}
-
-// Get raw name from DIE, or referenced dependencies
-pub(crate) fn get_raw_name<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
-) -> Option<String> {
- if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_linkage_name) {
- if let Ok(attr_string) = dwarf.attr_string(unit, attr_val.clone())
- {
- if let Ok(attr_string) = attr_string.to_string() {
- return Some(attr_string.to_string());
- }
- }
- else if let Some(dwarf) = dwarf.sup() {
- if let Ok(attr_string) = dwarf.attr_string(unit, attr_val)
- {
- if let Ok(attr_string) = attr_string.to_string() {
- return Some(attr_string.to_string());
- }
- }
- }
- }
- None
-}
-
-// Get the size of an object as a usize
-pub(crate) fn get_size_as_usize<R: ReaderType>(
- entry: &DebuggingInformationEntry<R>,
-) -> Option<usize> {
- if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) {
- get_attr_as_usize(attr)
- } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_bit_size) {
- get_attr_as_usize(attr).map(|attr_value| attr_value / 8)
- } else {
- None
- }
-}
-
-// Get the size of an object as a u64
-pub(crate) fn get_size_as_u64<R: ReaderType>(
- entry: &DebuggingInformationEntry<R>,
-) -> Option<u64> {
- if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) {
- get_attr_as_u64(&attr)
- } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_bit_size) {
- get_attr_as_u64(&attr).map(|attr_value| attr_value / 8)
- } else {
- None
- }
-}
-
-// Get the size of a subrange as a u64
-pub(crate) fn get_subrange_size<R: ReaderType>(
- entry: &DebuggingInformationEntry<R>,
-) -> u64 {
- if let Ok(Some(attr)) = entry.attr(constants::DW_AT_upper_bound) {
- get_attr_as_u64(&attr).map_or(0, |v| v + 1)
- } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_count) {
- get_attr_as_u64(&attr).unwrap_or(0)
- } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_lower_bound) {
- get_attr_as_u64(&attr).map_or(0, |v| v + 1)
- } else {
- 0
- }
-}
-
-// Get the start address of a function
-pub(crate) fn get_start_address<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
-) -> Option<u64> {
- if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_low_pc) {
- match dwarf.attr_address(unit, attr_val)
- {
- Ok(Some(val)) => Some(val),
- _ => None,
- }
- } else if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_entry_pc) {
- match dwarf.attr_address(unit, attr_val)
- {
- Ok(Some(val)) => Some(val),
- _ => None,
- }
- } else if let Ok(Some(attr_value)) = entry.attr_value(constants::DW_AT_ranges) {
- if let Ok(Some(ranges_offset)) = dwarf.attr_ranges_offset(unit, attr_value)
- {
- if let Ok(mut ranges) = dwarf.ranges(unit, ranges_offset)
- {
- if let Ok(Some(range)) = ranges.next() {
- return Some(range.begin);
- }
- }
- }
- return None;
- } else {
- None
- }
-}
-
-// Get an attribute value as a u64 if it can be coerced
-pub(crate) fn get_attr_as_u64<R: ReaderType>(attr: &Attribute<R>) -> Option<u64> {
- if let Some(value) = attr.udata_value() {
- Some(value)
- } else if let Some(value) = attr.sdata_value() {
- Some(value as u64)
- } else if let AttributeValue::Block(mut data) = attr.value() {
- match data.len() {
- 1 => data.read_u8().map(u64::from).ok(),
- 2 => data.read_u16().map(u64::from).ok(),
- 4 => data.read_u32().map(u64::from).ok(),
- 8 => data.read_u64().ok(),
- _ => None
- }
- } else {
- None
- }
-}
-
-// Get an attribute value as a usize if it can be coerced
-pub(crate) fn get_attr_as_usize<R: ReaderType>(attr: Attribute<R>) -> Option<usize> {
- if let Some(value) = attr.u8_value() {
- Some(value.into())
- } else if let Some(value) = attr.u16_value() {
- Some(value.into())
- } else if let Some(value) = attr.udata_value() {
- Some(value as usize)
- } else {
- attr.sdata_value().map(|value| value as usize)
- }
-}
-
-// Get an attribute value as a usize if it can be coerced
-// Parses DW_OP_address, DW_OP_const
-pub(crate) fn get_expr_value<R: ReaderType>(
- unit: &Unit<R>,
- attr: Attribute<R>,
-) -> Option<u64> {
- if let AttributeValue::Exprloc(mut expression) = attr.value() {
- match Operation::parse(&mut expression.0, unit.encoding()) {
- Ok(Operation::PlusConstant { value }) => Some(value),
- Ok(Operation::UnsignedConstant { value }) => Some(value),
- Ok(Operation::Address { address: 0 }) => None,
- Ok(Operation::Address { address }) => Some(address),
- _ => None
- }
- } else {
- None
- }
-}
-
-
-pub(crate) fn get_build_id(view: &BinaryView) -> Result<String, String> {
- let mut build_id: Option<String> = None;
-
- if let Ok(raw_view) = view.raw_view() {
- if let Ok(build_id_section) = raw_view.section_by_name(".note.gnu.build-id") {
- // Name size - 4 bytes
- // Desc size - 4 bytes
- // Type - 4 bytes
- // Name - n bytes
- // Desc - n bytes
- let build_id_bytes = raw_view.read_vec(build_id_section.start(), build_id_section.len());
- if build_id_bytes.len() < 12 {
- return Err("Build id section must be at least 12 bytes".to_string());
- }
-
- let name_len: u32;
- let desc_len: u32;
- let note_type: u32;
- match raw_view.default_endianness() {
- Endianness::LittleEndian => {
- name_len = u32::from_le_bytes(build_id_bytes[0..4].try_into().unwrap());
- desc_len = u32::from_le_bytes(build_id_bytes[4..8].try_into().unwrap());
- note_type = u32::from_le_bytes(build_id_bytes[8..12].try_into().unwrap());
- },
- Endianness::BigEndian => {
- name_len = u32::from_be_bytes(build_id_bytes[0..4].try_into().unwrap());
- desc_len = u32::from_be_bytes(build_id_bytes[4..8].try_into().unwrap());
- note_type = u32::from_be_bytes(build_id_bytes[8..12].try_into().unwrap());
- }
- };
-
- if note_type != 3 {
- return Err(format!("Build id section has wrong type: {}", note_type));
- }
-
- let expected_len = (12 + name_len + desc_len) as usize;
-
- if build_id_bytes.len() < expected_len {
- return Err(format!("Build id section not expected length: expected {}, got {}", expected_len, build_id_bytes.len()));
- }
-
- let desc: &[u8] = &build_id_bytes[(12+name_len as usize)..expected_len];
- build_id = Some(desc.iter().map(|b| format!("{:02x}", b)).collect());
- }
- }
-
- if let Some(x) = build_id {
- Ok(x)
- }
- else {
- Err("Failed to get build id".to_string())
- }
-}
-
-
-pub(crate) fn download_debug_info(build_id: &String, view: &BinaryView) -> Result<Ref<BinaryView>, String> {
- let settings = Settings::new("");
-
- let debug_server_urls = settings.get_string_list("network.debuginfodServers", Some(view), None);
-
- for debug_server_url in debug_server_urls.iter() {
- let artifact_url = format!("{}/buildid/{}/debuginfo", debug_server_url, build_id);
-
- // Download from remote
- let (tx, rx) = mpsc::channel();
- let write = move |data: &[u8]| -> usize {
- if let Ok(_) = tx.send(Vec::from(data)) {
- data.len()
- } else {
- 0
- }
- };
-
- let dp = DownloadProvider::try_default().map_err(|_| "No default download provider")?;
- let mut inst = dp
- .create_instance()
- .map_err(|_| "Couldn't create download instance")?;
- let result = inst
- .perform_custom_request(
- "GET",
- artifact_url,
- HashMap::<String, String>::new(),
- DownloadInstanceInputOutputCallbacks {
- read: None,
- write: Some(Box::new(write)),
- progress: None,
- },
- )
- .map_err(|e| e.to_string())?;
- if result.status_code != 200 {
- continue;
- }
-
- let mut expected_length = None;
- for (k, v) in result.headers.iter() {
- if k.to_lowercase() == "content-length" {
- expected_length = Some(usize::from_str(v).map_err(|e| e.to_string())?);
- }
- }
-
- let mut data = vec![];
- while let Ok(packet) = rx.try_recv() {
- data.extend(packet.into_iter());
- }
-
- if let Some(length) = expected_length {
- if data.len() != length {
- return Err(format!(
- "Bad length: expected {} got {}",
- length,
- data.len()
- ));
- }
- }
-
- let options = "{\"analysis.debugInfo.internal\": false}";
- let bv = BinaryView::from_data(FileMetadata::new().deref(), &data)
- .map_err(|_| "Unable to create binary view from downloaded data".to_string())?;
-
- return binaryninja::load_view(bv.deref(), false, Some(options))
- .ok_or("Unable to load binary view from downloaded data".to_string());
- }
- return Err("Could not find a server with debug info for this file".to_string());
-}
-
-
-pub(crate) fn find_local_debug_file_for_build_id(build_id: &String, view: &BinaryView) -> Option<String> {
- let settings = Settings::new("");
- let debug_dirs_enabled = settings.get_bool("analysis.debugInfo.enableDebugDirectories", Some(view), None);
-
- if !debug_dirs_enabled {
- return None;
- }
-
- let debug_info_paths = settings.get_string_list("analysis.debugInfo.debugDirectories", Some(view), None);
-
- if debug_info_paths.is_empty() {
- return None
- }
-
- for debug_info_path in debug_info_paths.into_iter() {
- if let Ok(path) = PathBuf::from_str(&debug_info_path.to_string())
- {
- let elf_path = path
- .join(&build_id[..2])
- .join(&build_id[2..])
- .join("elf");
-
- let debug_ext_path = path
- .join(&build_id[..2])
- .join(format!("{}.debug", &build_id[2..]));
-
- let final_path = if debug_ext_path.exists() {
- debug_ext_path
- }
- else if elf_path.exists() {
- elf_path
- }
- else {
- // No paths exist in this dir, try the next one
- continue;
- };
- return final_path
- .to_str()
- .and_then(|x| Some(x.to_string()));
- }
- }
- None
-}
-
-
-pub(crate) fn load_debug_info_for_build_id(build_id: &String, view: &BinaryView) -> (Option<Ref<BinaryView>>, bool) {
- if let Some(debug_file_path) = find_local_debug_file_for_build_id(build_id, view) {
- return
- (
- binaryninja::load_with_options(
- debug_file_path,
- false,
- Some("{\"analysis.debugInfo.internal\": false}")
- ),
- false
- );
- }
- else if Settings::new("").get_bool("network.enableDebuginfod", Some(view), None) {
- return (
- download_debug_info(build_id, view).ok(),
- true
- );
- }
- (None, false)
-}
-
-
-pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option<String> {
- let settings = Settings::new("");
- let load_sibling_debug = settings.get_bool("analysis.debugInfo.loadSiblingDebugFiles", Some(view), None);
-
- if !load_sibling_debug {
- return None;
- }
-
- let full_file_path = view.file().filename().to_string();
-
- let debug_file = PathBuf::from(format!("{}.debug", full_file_path));
- let dsym_folder = PathBuf::from(format!("{}.dSYM", full_file_path));
- if debug_file.exists() && debug_file.is_file() {
- return Some(debug_file.to_string_lossy().to_string());
- }
-
- if dsym_folder.exists() && dsym_folder.is_dir() {
- let filename = Path::new(&full_file_path)
- .file_name()
- .unwrap_or(OsStr::new(""));
-
- let dsym_file = dsym_folder
- .join("Contents/Resources/DWARF/")
- .join(filename); // TODO: should this just pull any file out? Can there be multiple files?
- if dsym_file.exists() {
- return Some(dsym_file.to_string_lossy().to_string());
- }
- }
-
- None
-}
-
-
-pub(crate) fn load_sibling_debug_file(view: &BinaryView) -> (Option<Ref<BinaryView>>, bool) {
- let Some(debug_file) = find_sibling_debug_file(view) else {
- return (None, false);
- };
-
- let load_settings = match view.default_platform() {
- Some(plat) => format!("{{\"analysis.debugInfo.internal\": false, \"loader.platform\": \"{}\"}}", plat.name()),
- None => "{\"analysis.debugInfo.internal\": false}".to_string()
- };
-
- (
- binaryninja::load_with_options(
- debug_file,
- false,
- Some(load_settings)
- ),
- false
- )
-}
diff --git a/rust/examples/dwarf/dwarf_import/src/lib.rs b/rust/examples/dwarf/dwarf_import/src/lib.rs
deleted file mode 100644
index 6050593f..00000000
--- a/rust/examples/dwarf/dwarf_import/src/lib.rs
+++ /dev/null
@@ -1,723 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-mod die_handlers;
-mod dwarfdebuginfo;
-mod functions;
-mod helpers;
-mod types;
-
-use std::collections::HashMap;
-
-use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext};
-use crate::functions::parse_function_entry;
-use crate::helpers::{get_attr_die, get_name, get_uid, DieReference};
-use crate::types::parse_variable;
-
-use binaryninja::binaryview::BinaryViewBase;
-use binaryninja::{
- binaryview::{BinaryView, BinaryViewExt},
- debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
- settings::Settings,
- templatesimplifier::simplify_str_to_str,
-};
-use dwarfreader::{
- create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf,
-};
-
-use functions::parse_lexical_block;
-use gimli::{constants, CfaRule, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section, SectionId, Unit, UnwindContext, UnwindSection};
-
-use helpers::{get_build_id, load_debug_info_for_build_id};
-use log::{debug, error, warn};
-use binaryninja::logger::Logger;
-
-trait ReaderType: Reader<Offset = usize> {}
-impl<T: Reader<Offset = usize>> ReaderType for T {}
-
-
-pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<(), ()> + 'b>(
- original_fn: F,
- subpart: usize,
- subpart_weights: &[f64],
-) -> Box<dyn Fn(usize, usize) -> Result<(), ()> + 'b> {
- // Normalize weights
- let weight_sum: f64 = subpart_weights.iter().sum();
- if weight_sum < 0.0001 {
- return Box::new(|_, _| Ok(()));
- }
-
- // Keep a running count of weights for the start
- let mut subpart_starts = vec![];
- let mut start = 0f64;
- for w in subpart_weights {
- subpart_starts.push(start);
- start += *w;
- }
-
- let subpart_start = subpart_starts[subpart] / weight_sum;
- let weight = subpart_weights[subpart] / weight_sum;
-
- Box::new(move |cur: usize, max: usize| {
- // Just use a large number for easy divisibility
- let steps = 1000000f64;
- let subpart_size = steps * weight;
- let subpart_progress = ((cur as f64) / (max as f64)) * subpart_size;
-
- original_fn(
- (subpart_start * steps + subpart_progress) as usize,
- steps as usize,
- )
- })
-}
-
-
-fn calculate_total_unit_bytes<R: ReaderType>(
- dwarf: &Dwarf<R>,
- debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
-)
-{
- let mut iter = dwarf.units();
- let mut total_size: usize = 0;
- while let Ok(Some(header)) = iter.next()
- {
- total_size += header.length_including_self();
- }
- debug_info_builder_context.total_unit_size_bytes = total_size;
-}
-
-fn recover_names<R: ReaderType>(
- dwarf: &Dwarf<R>,
- debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
- progress: &dyn Fn(usize, usize) -> Result<(), ()>,
-) -> bool {
-
- let mut res = true;
- if let Some(sup_dwarf) = dwarf.sup() {
- res = recover_names_internal(sup_dwarf, debug_info_builder_context, progress);
- }
-
- if res {
- res = recover_names_internal(dwarf, debug_info_builder_context, progress);
- }
- res
-}
-
-fn recover_names_internal<R: ReaderType>(
- dwarf: &Dwarf<R>,
- debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
- progress: &dyn Fn(usize, usize) -> Result<(), ()>,
-) -> bool {
- let mut iter = dwarf.units();
- let mut current_byte_offset: usize = 0;
- while let Ok(Some(header)) = iter.next() {
- let unit_offset = header.offset().as_debug_info_offset().unwrap().0;
- let unit = dwarf.unit(header).unwrap();
- let mut namespace_qualifiers: Vec<(isize, String)> = 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;
- debug_info_builder_context.total_die_count += 1;
- }
-
- while let Ok(Some((delta_depth, entry))) = entries.next_dfs() {
- debug_info_builder_context.total_die_count += 1;
-
- if (*progress)(current_byte_offset, debug_info_builder_context.total_unit_size_bytes).is_err() {
- return false; // Parsing canceled
- };
- current_byte_offset = unit_offset + entry.offset().0;
-
- depth += delta_depth;
- if depth < 0 {
- error!("DWARF information is seriously malformed. Aborting parsing.");
- return false;
- }
-
- // 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: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<R>,
- namespace_qualifiers: &mut Vec<(isize, String)>,
- depth: isize,
- ) {
- if let Some(namespace_qualifier) =
- get_name(dwarf, unit, entry, debug_info_builder_context)
- {
- namespace_qualifiers.push((depth, namespace_qualifier));
- } else if let Some(die_reference) = get_attr_die(
- dwarf,
- unit,
- entry,
- debug_info_builder_context,
- constants::DW_AT_extension,
- ) {
- match die_reference {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
- resolve_namespace_name(
- dwarf,
- entry_unit,
- &entry_unit.entry(entry_offset).unwrap(),
- debug_info_builder_context,
- namespace_qualifiers,
- depth,
- )
- }
- DieReference::Err => {
- warn!(
- "Failed to fetch DIE when resolving namespace. Debug information may be incomplete."
- );
- }
- }
- } else {
- namespace_qualifiers.push((depth, "anonymous_namespace".to_string()));
- }
- }
-
- resolve_namespace_name(
- dwarf,
- &unit,
- entry,
- debug_info_builder_context,
- &mut namespace_qualifiers,
- depth,
- );
- }
- constants::DW_TAG_class_type
- | constants::DW_TAG_structure_type
- | constants::DW_TAG_union_type => {
- if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
- namespace_qualifiers.push((depth, name))
- } else {
- namespace_qualifiers.push((
- depth,
- match entry.tag() {
- constants::DW_TAG_class_type => "anonymous_class".to_string(),
- constants::DW_TAG_structure_type => {
- "anonymous_structure".to_string()
- }
- constants::DW_TAG_union_type => "anonymous_union".to_string(),
- _ => unreachable!(),
- },
- ))
- }
- debug_info_builder_context.set_name(
- get_uid(dwarf, &unit, entry),
- simplify_str_to_str(
- namespace_qualifiers
- .iter()
- .map(|(_, namespace)| namespace.to_owned())
- .collect::<Vec<String>>()
- .join("::"),
- )
- .to_string(),
- );
- }
- constants::DW_TAG_typedef
- | constants::DW_TAG_subprogram
- | constants::DW_TAG_enumeration_type => {
- if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
- debug_info_builder_context.set_name(
- get_uid(dwarf, &unit, entry),
- simplify_str_to_str(
- namespace_qualifiers
- .iter()
- .chain(vec![&(-1, name)].into_iter())
- .map(|(_, namespace)| namespace.to_owned())
- .collect::<Vec<String>>()
- .join("::"),
- )
- .to_string(),
- );
- }
- }
- _ => {
- if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
- debug_info_builder_context.set_name(get_uid(dwarf, &unit, entry), name);
- }
- }
- }
- }
- }
-
- true
-}
-
-fn parse_unit<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<R>,
- debug_info_builder: &mut DebugInfoBuilder,
- progress: &dyn Fn(usize, usize) -> Result<(), ()>,
- current_die_number: &mut usize,
-) {
- let mut entries = unit.entries();
-
- let mut current_depth: isize = 0;
- let mut functions_by_depth: Vec<(Option<usize>, isize)> = vec![];
- let mut lexical_blocks_by_depth: Vec<(iset::IntervalSet<u64>, isize)> = vec![];
-
- // 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((depth_delta, entry))) = entries.next_dfs() {
- *current_die_number += 1;
- if (*progress)(
- *current_die_number,
- debug_info_builder_context.total_die_count,
- )
- .is_err()
- {
- return; // Parsing canceled
- }
-
- current_depth = current_depth.saturating_add(depth_delta);
-
- loop {
- if let Some((_fn_idx, depth)) = functions_by_depth.last() {
- if current_depth <= *depth {
- functions_by_depth.pop();
- }
- else {
- break
- }
- }
- else {
- break;
- }
-
- if let Some((_lexical_block, depth)) = lexical_blocks_by_depth.last() {
- if current_depth <= *depth {
- lexical_blocks_by_depth.pop();
- }
- else {
- break
- }
- }
- else {
- break;
- }
- }
-
- match entry.tag() {
- constants::DW_TAG_subprogram => {
- let fn_idx = parse_function_entry(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
- functions_by_depth.push((fn_idx, current_depth));
- },
- constants::DW_TAG_lexical_block => {
- if let Some(block_ranges) = parse_lexical_block(dwarf, unit, entry) {
- lexical_blocks_by_depth.push((block_ranges, current_depth));
- }
- },
- constants::DW_TAG_variable => {
- let current_fn_idx = functions_by_depth.last().and_then(|x| x.0);
- let current_lexical_block = lexical_blocks_by_depth.last().and_then(|x| Some(&x.0));
- parse_variable(dwarf, unit, entry, debug_info_builder_context, debug_info_builder, current_fn_idx, current_lexical_block)
- },
- constants::DW_TAG_class_type |
- constants::DW_TAG_enumeration_type |
- constants::DW_TAG_structure_type |
- constants::DW_TAG_union_type |
- constants::DW_TAG_typedef => {
- // Ensure types are loaded even if they're unused
- types::get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
- },
- _ => (),
- }
- }
-}
-
-fn parse_unwind_section<R: Reader, U: UnwindSection<R>>(
- view: &BinaryView,
- unwind_section: U,
-) -> gimli::Result<iset::IntervalMap<u64, i64>>
-where <U as UnwindSection<R>>::Offset: std::hash::Hash {
- let mut bases = gimli::BaseAddresses::default();
-
- if let Ok(section) = view.section_by_name(".eh_frame_hdr").or(view.section_by_name("__eh_frame_hdr")) {
- bases = bases.set_eh_frame_hdr(section.start());
- }
-
- if let Ok(section) = view.section_by_name(".eh_frame").or(view.section_by_name("__eh_frame")) {
- bases = bases.set_eh_frame(section.start());
- } else if let Ok(section) = view.section_by_name(".debug_frame").or(view.section_by_name("__debug_frame")) {
- bases = bases.set_eh_frame(section.start());
- }
-
- if let Ok(section) = view.section_by_name(".text").or(view.section_by_name("__text")) {
- bases = bases.set_text(section.start());
- }
-
- if let Ok(section) = view.section_by_name(".got").or(view.section_by_name("__got")) {
- bases = bases.set_got(section.start());
- }
-
- let mut cies = HashMap::new();
- let mut cfa_offsets = iset::IntervalMap::new();
-
- let mut entries = unwind_section.entries(&bases);
- let mut unwind_context = UnwindContext::new();
- loop {
- match entries.next()? {
- None => return Ok(cfa_offsets),
- Some(gimli::CieOrFde::Cie(_cie)) => {
- // TODO: do we want to do anything with standalone CIEs?
- }
- Some(gimli::CieOrFde::Fde(partial)) => {
- let fde = match partial.parse(|_, bases, o| {
- cies.entry(o)
- .or_insert_with(|| unwind_section.cie_from_offset(bases, o))
- .clone()
- }) {
- Ok(fde) => fde,
- Err(e) => {
- error!("Failed to parse FDE: {}", e);
- continue;
- }
- };
-
- if fde.len() == 0 {
- // This FDE is a terminator
- return Ok(cfa_offsets);
- }
-
- if fde.initial_address().overflowing_add(fde.len()).1 {
- warn!("FDE at offset {:?} exceeds bounds of memory space! {:#x} + length {:#x}", fde.offset(), fde.initial_address(), fde.len());
- } else {
- // Walk the FDE table rows and store their CFA
- let mut fde_table = fde.rows(&unwind_section, &bases, &mut unwind_context)?;
-
- while let Some(row) = fde_table.next_row()? {
- match row.cfa() {
- CfaRule::RegisterAndOffset {register: _, offset} => {
- // TODO: we should store offsets by register
- if row.start_address() < row.end_address() {
- cfa_offsets.insert(
- row.start_address()..row.end_address(),
- *offset,
- );
- }
- else {
- debug!("Invalid FDE table row addresses: {:#x}..{:#x}", row.start_address(), row.end_address());
- }
- },
- CfaRule::Expression(_) => {
- debug!("Unhandled CFA expression when determining offset");
- }
- };
-
- }
- }
- }
- }
- }
-}
-
-fn get_supplementary_build_id(bv: &BinaryView) -> Option<String> {
- let raw_view = bv.raw_view().ok()?;
- if let Ok(section) = raw_view.section_by_name(".gnu_debugaltlink") {
- let start = section.start();
- let len = section.len();
-
- if len < 20 {
- // Not large enough to hold a build id
- return None;
- }
-
- raw_view
- .read_vec(start, len)
- .splitn(2, |x| *x == 0)
- .last()
- .map(|a| {
- a.iter().map(|b| format!("{:02x}", b)).collect()
- })
- }
- else {
- None
- }
-}
-
-fn parse_dwarf(
- _bv: &BinaryView,
- debug_bv: &BinaryView,
- supplementary_bv: Option<&BinaryView>,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
-) -> Result<DebugInfoBuilder, ()> {
- // TODO: warn if no supplementary file and .gnu_debugaltlink section present
-
- // Determine if this is a DWO
- // TODO : Make this more robust...some DWOs follow non-DWO conventions
-
- // Figure out if it's the given view or the raw view that has the dwarf info in it
- let raw_view = &debug_bv.raw_view()?;
- let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) {
- debug_bv
- } else {
- raw_view
- };
-
- let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view);
-
- // gimli setup
- let endian = get_endian(view);
- let mut section_reader =
- |section_id: SectionId| -> _ { create_section_reader(section_id, view, endian, dwo_file) };
-
- let mut dwarf = match Dwarf::load(&mut section_reader) {
- Ok(x) => x,
- Err(e) => {
- error!("Failed to load DWARF info: {}", e);
- return Err(());
- }
- };
-
- if dwo_file {
- dwarf.file_type = DwarfFileType::Dwo;
- }
- else {
- dwarf.file_type = DwarfFileType::Main;
- }
-
- if let Some(sup_bv) = supplementary_bv {
- let sup_endian = get_endian(sup_bv);
- let sup_dwo_file = is_dwo_dwarf(sup_bv) || is_raw_dwo_dwarf(sup_bv);
- let sup_section_reader =
- |section_id: SectionId| -> _ { create_section_reader(section_id, sup_bv, sup_endian, sup_dwo_file) };
- if let Err(e) = dwarf.load_sup(sup_section_reader) {
- error!("Failed to load supplementary file: {}", e);
- }
- }
-
- let range_data_offsets;
- if view.section_by_name(".eh_frame").is_ok() || view.section_by_name("__eh_frame").is_ok() {
- let eh_frame_endian = get_endian(view);
- let mut eh_frame_section_reader =
- |section_id: SectionId| -> _ { create_section_reader(section_id, view, eh_frame_endian, dwo_file) };
- let mut eh_frame = gimli::EhFrame::load(&mut eh_frame_section_reader).unwrap();
- eh_frame.set_address_size(view.address_size() as u8);
- range_data_offsets = parse_unwind_section(view, eh_frame)
- .map_err(|e| error!("Error parsing .eh_frame: {}", e))?;
- }
- else if view.section_by_name(".debug_frame").is_ok() || view.section_by_name("__debug_frame").is_ok() {
- let debug_frame_endian = get_endian(view);
- let mut debug_frame_section_reader =
- |section_id: SectionId| -> _ { create_section_reader(section_id, view, debug_frame_endian, dwo_file) };
- let mut debug_frame = gimli::DebugFrame::load(&mut debug_frame_section_reader).unwrap();
- debug_frame.set_address_size(view.address_size() as u8);
- range_data_offsets = parse_unwind_section(view, debug_frame)
- .map_err(|e| error!("Error parsing .debug_frame: {}", e))?;
- }
- else {
- range_data_offsets = Default::default();
- }
-
-
- // 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();
- debug_info_builder.set_range_data_offsets(range_data_offsets);
-
- if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, &dwarf) {
- calculate_total_unit_bytes(&dwarf, &mut debug_info_builder_context);
-
- let progress_weights = [0.5, 0.5];
- let name_progress = split_progress(&progress, 0, &progress_weights);
- let parse_progress = split_progress(&progress, 1, &progress_weights);
-
- if !recover_names(&dwarf, &mut debug_info_builder_context, &name_progress)
- || debug_info_builder_context.total_die_count == 0
- {
- return Ok(debug_info_builder);
- }
-
- // Parse all the compilation units
- let mut current_die_number = 0;
-
- for unit in debug_info_builder_context.sup_units() {
- parse_unit(
- dwarf.sup().unwrap(),
- &unit,
- &debug_info_builder_context,
- &mut debug_info_builder,
- &parse_progress,
- &mut current_die_number,
- );
- }
-
- for unit in debug_info_builder_context.units() {
- parse_unit(
- &dwarf,
- &unit,
- &debug_info_builder_context,
- &mut debug_info_builder,
- &parse_progress,
- &mut current_die_number,
- );
- }
- }
-
- Ok(debug_info_builder)
-}
-
-struct DWARFParser;
-
-impl CustomDebugInfoParser for DWARFParser {
- fn is_valid(&self, view: &BinaryView) -> bool {
- if dwarfreader::is_valid(view) || dwarfreader::can_use_debuginfod(view) {
- return true;
- }
- if dwarfreader::has_build_id_section(view) {
- if let Ok(build_id) = get_build_id(view) {
- if helpers::find_local_debug_file_for_build_id(&build_id, view).is_some() {
- return true;
- }
- }
- }
- if helpers::find_sibling_debug_file(view).is_some() {
- return true;
- }
- false
- }
-
- fn parse_info(
- &self,
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- ) -> bool {
- let (external_file, close_external) = if !dwarfreader::is_valid(bv) {
- if let (Some(debug_view), x) = helpers::load_sibling_debug_file(bv) {
- (Some(debug_view), x)
- }
- else if let Ok(build_id) = get_build_id(bv) {
- helpers::load_debug_info_for_build_id(&build_id, bv)
- }
- else {
- (None, false)
- }
- }
- else {
- (None, false)
- };
-
- let sup_bv = get_supplementary_build_id(
- external_file
- .as_deref()
- .unwrap_or(debug_file)
- )
- .and_then(|build_id| {
- load_debug_info_for_build_id(&build_id, bv)
- .0
- .map(|x| x.raw_view().unwrap())
- });
-
- let result = match parse_dwarf(
- bv,
- external_file.as_deref().unwrap_or(debug_file),
- sup_bv.as_deref(),
- progress
- )
- {
- Ok(mut builder) => {
- builder.post_process(bv, debug_info).commit_info(debug_info);
- true
- }
- Err(_) => false,
- };
-
- if let (Some(ext), true) = (external_file, close_external) {
- ext.file().close();
- }
-
- result
- }
-}
-
-#[no_mangle]
-pub extern "C" fn CorePluginInit() -> bool {
- Logger::new("DWARF").init();
-
- let settings = Settings::new("");
-
- settings.register_setting_json(
- "network.enableDebuginfod",
- r#"{
- "title" : "Enable Debuginfod Support",
- "type" : "boolean",
- "default" : false,
- "description" : "Enable using Debuginfod servers to fetch DWARF debug info for files with a .note.gnu.build-id section.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "network.debuginfodServers",
- r#"{
- "title" : "Debuginfod Server URLs",
- "type" : "array",
- "sorted" : true,
- "default" : [],
- "description" : "Servers to use for fetching DWARF debug info for files with a .note.gnu.build-id section.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "analysis.debugInfo.enableDebugDirectories",
- r#"{
- "title" : "Enable Debug File Directories",
- "type" : "boolean",
- "default" : true,
- "description" : "Enable searching local debug directories for DWARF debug info.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "analysis.debugInfo.debugDirectories",
- r#"{
- "title" : "Debug File Directories",
- "type" : "array",
- "sorted" : true,
- "default" : [],
- "description" : "Paths to folder containing DWARF debug info stored by build id.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "analysis.debugInfo.loadSiblingDebugFiles",
- r#"{
- "title" : "Enable Loading of Sibling Debug Files",
- "type" : "boolean",
- "default" : true,
- "description" : "Enable automatic loading of X.debug and X.dSYM files next to a file named X.",
- "ignore" : []
- }"#,
- );
-
- 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
deleted file mode 100644
index 4914b501..00000000
--- a/rust/examples/dwarf/dwarf_import/src/types.rs
+++ /dev/null
@@ -1,462 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use crate::{die_handlers::*, ReaderType};
-use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
-use crate::helpers::*;
-
-use binaryninja::{
- rc::*,
- types::{
- MemberAccess, MemberScope, ReferenceType, StructureBuilder, StructureType, Type, TypeClass,
- },
-};
-
-use gimli::{constants, AttributeValue, DebuggingInformationEntry, Dwarf, Operation, Unit};
-
-use log::{debug, error, warn};
-
-pub(crate) fn parse_variable<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<R>,
- debug_info_builder: &mut DebugInfoBuilder,
- function_index: Option<usize>,
- lexical_block: Option<&iset::IntervalSet<u64>>,
-) {
- let full_name = debug_info_builder_context.get_name(dwarf, unit, entry);
- let type_uid = get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
-
- let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) else {
- return
- };
-
- let AttributeValue::Exprloc(mut expression) = attr.value() else {
- return
- };
-
- match Operation::parse(&mut expression.0, unit.encoding()) {
- Ok(Operation::FrameOffset { offset }) => {
- debug_info_builder.add_stack_variable(function_index, offset, full_name, type_uid, lexical_block);
- },
- //Ok(Operation::RegisterOffset { register: _, offset: _, base_type: _ }) => {
- // //TODO: look up register by index (binja register indexes don't match processor indexes?)
- // //TODO: calculate absolute stack offset
- // //TODO: add by absolute offset
- //},
- Ok(Operation::Address { address }) => {
- if let Some(uid) = type_uid {
- debug_info_builder.add_data_variable(address, full_name, uid)
- }
- },
- Ok(Operation::AddressIndex { index }) => {
- if let Some(uid) = type_uid {
- if let Ok(address) = dwarf.address(unit, index) {
- debug_info_builder.add_data_variable(address, full_name, uid)
- }
- else
- {
- warn!("Invalid index into IAT: {}", index.0);
- }
- }
- },
- Ok(op) => {
- debug!("Unhandled operation type for variable: {:?}", op);
- },
- Err(e) => error!("Error parsing operation type for variable {:?}: {}", full_name, e)
- }
-}
-
-fn do_structure_parse<R: ReaderType>(
- dwarf: &Dwarf<R>,
- structure_type: StructureType,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<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, debug_info_builder_context).is_some() {
- debug_info_builder_context.get_name(dwarf, unit, entry)
- } else {
- None
- };
-
- // Create structure with proper size
- let size = get_size_as_u64(entry).unwrap_or(0);
- let 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(dwarf, unit, entry),
- &full_name,
- Type::named_type_from_type(
- full_name.clone(),
- &Type::structure(&structure_builder.finalize()),
- ),
- false,
- );
- } else {
- // We _need_ to have initial typedefs or else we can enter infinite parsing loops
- // These get overwritten in the last step with the actual type, however, so this
- // is either perfectly fine or breaking a bunch of NTRs
- let full_name = format!("anonymous_structure_{:x}", get_uid(dwarf, unit, entry));
- debug_info_builder.add_type(
- get_uid(dwarf, unit, entry),
- &full_name,
- Type::named_type_from_type(&full_name, &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_context,
- debug_info_builder,
- ) {
- if let Some(t) = debug_info_builder.get_type(child_type_id) {
- let child_type = t.get_type();
- if let Some(child_name) = debug_info_builder_context
- .get_name(dwarf, unit, child.entry())
- .map_or(
- if child_type.type_class() == TypeClass::StructureTypeClass {
- Some("".to_string())
- } 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(dwarf, 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(dwarf, unit, entry),
- &format!("{}", finalized_structure),
- finalized_structure,
- false, // Don't commit anonymous unions (because I think it'll break things)
- );
- }
- Some(get_uid(dwarf, 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
-pub(crate) fn get_type<R: ReaderType>(
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- entry: &DebuggingInformationEntry<R>,
- debug_info_builder_context: &DebugInfoBuilderContext<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
- let entry_uid = get_uid(dwarf, unit, entry);
- if debug_info_builder.contains_type(entry_uid) {
- return Some(entry_uid);
- }
-
- // Don't parse types that are just declarations and not definitions
- if let Ok(Some(_)) = entry.attr(constants::DW_AT_declaration) {
- return None;
- }
-
- let entry_type = if let Some(die_reference) = get_attr_die(
- dwarf,
- unit,
- entry,
- debug_info_builder_context,
- constants::DW_AT_type,
- ) {
- // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
- match die_reference {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
- get_type(
- dwarf,
- entry_unit,
- &entry_unit.entry(entry_offset).unwrap(),
- debug_info_builder_context,
- debug_info_builder,
- )
- }
- DieReference::Err => {
- warn!("Failed to fetch DIE when getting type through DW_AT_type. Debug information may be incomplete.");
- None
- }
- }
- } else if let Some(die_reference) = get_attr_die(
- dwarf,
- unit,
- entry,
- debug_info_builder_context,
- constants::DW_AT_abstract_origin,
- ) {
- // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
- match die_reference {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
- get_type(
- dwarf,
- entry_unit,
- &entry_unit.entry(entry_offset).unwrap(),
- debug_info_builder_context,
- debug_info_builder,
- )
- }
- DieReference::Err => {
- warn!("Failed to fetch DIE when getting type through DW_AT_abstract_origin. Debug information may be incomplete.");
- None
- }
- }
- } else {
- // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
- match resolve_specification(dwarf, unit, entry, debug_info_builder_context) {
- DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset))
- if entry_unit.header.offset() != unit.header.offset()
- && entry_offset != entry.offset() =>
- {
- get_type(
- dwarf,
- entry_unit,
- &entry_unit.entry(entry_offset).unwrap(),
- debug_info_builder_context,
- debug_info_builder,
- )
- }
- DieReference::UnitAndOffset(_) => None,
- DieReference::Err => {
- warn!("Failed to fetch DIE when getting type. Debug information may be incomplete.");
- 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(entry_uid) {
- return Some(entry_uid);
- }
-
- // 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, debug_info_builder_context),
- false,
- ),
-
- constants::DW_TAG_structure_type => {
- return do_structure_parse(
- dwarf,
- StructureType::StructStructureType,
- unit,
- entry,
- debug_info_builder_context,
- debug_info_builder,
- )
- }
- constants::DW_TAG_class_type => {
- return do_structure_parse(
- dwarf,
- StructureType::ClassStructureType,
- unit,
- entry,
- debug_info_builder_context,
- debug_info_builder,
- )
- }
- constants::DW_TAG_union_type => {
- return do_structure_parse(
- dwarf,
- StructureType::UnionStructureType,
- unit,
- entry,
- debug_info_builder_context,
- debug_info_builder,
- )
- }
-
- // Enum
- constants::DW_TAG_enumeration_type => {
- (handle_enum(dwarf, unit, entry, debug_info_builder_context), true)
- }
-
- // Basic types
- constants::DW_TAG_typedef => {
- if let Some(name) = debug_info_builder_context.get_name(dwarf, unit, entry) {
- handle_typedef(debug_info_builder, entry_type, &name)
- } else {
- (None, false)
- }
- }
- constants::DW_TAG_pointer_type => (
- handle_pointer(
- entry,
- debug_info_builder_context,
- debug_info_builder,
- entry_type,
- ReferenceType::PointerReferenceType,
- ),
- false,
- ),
- constants::DW_TAG_reference_type => (
- handle_pointer(
- entry,
- debug_info_builder_context,
- debug_info_builder,
- entry_type,
- ReferenceType::ReferenceReferenceType,
- ),
- false,
- ),
- constants::DW_TAG_rvalue_reference_type => (
- handle_pointer(
- entry,
- debug_info_builder_context,
- 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),
- constants::DW_TAG_subroutine_type => (
- handle_function(
- dwarf,
- unit,
- entry,
- debug_info_builder_context,
- 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, debug_info_builder_context).is_some() {
- debug_info_builder_context.get_name(dwarf, unit, entry)
- } else {
- None
- }
- .unwrap_or_else(|| {
- commit = false;
- format!("{}", type_def)
- });
-
- debug_info_builder.add_type(entry_uid, &name, type_def, commit);
- Some(entry_uid)
- } else {
- None
- }
-}
diff --git a/rust/examples/dwarf/dwarfdump/Cargo.toml b/rust/examples/dwarf/dwarfdump/Cargo.toml
deleted file mode 100644
index 3f1e25f6..00000000
--- a/rust/examples/dwarf/dwarfdump/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "dwarfdump"
-version = "0.1.0"
-authors = ["Kyle Martin <kyle@vector35.com>"]
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-dwarfreader = { path = "../shared/" }
-binaryninja = {path="../../../"}
-gimli = "0.31"
diff --git a/rust/examples/dwarf/dwarfdump/readme.md b/rust/examples/dwarf/dwarfdump/readme.md
deleted file mode 100644
index ae3a193b..00000000
--- a/rust/examples/dwarf/dwarfdump/readme.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# DWARF Dump Example
-
-This is actually a fully-developed plugin, rather than a measly example.
-
-Two features this does not support are: files in big endian, and .dwo files
-
-## How to use
-
-Simply `cargo build --release` in this directory, and copy the `.so` from the target directory to your plugin directory
-
-### Attribution
-
-This example makes use of:
- - [gimli] ([gimli license] - MIT)
-
-[gimli license]: https://github.com/gimli-rs/gimli/blob/master/LICENSE-MIT
-[gimli]: https://github.com/gimli-rs/gimli
diff --git a/rust/examples/dwarf/dwarfdump/src/lib.rs b/rust/examples/dwarf/dwarfdump/src/lib.rs
deleted file mode 100644
index 2cfbbbce..00000000
--- a/rust/examples/dwarf/dwarfdump/src/lib.rs
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use binaryninja::{
- binaryview::{BinaryView, BinaryViewExt},
- command::{register, Command},
- disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents},
- flowgraph::{BranchType, EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption},
-};
-use dwarfreader::is_valid;
-
-use gimli::{
- AttributeValue::{Encoding, Flag, UnitRef},
- // BigEndian,
- DebuggingInformationEntry,
- Dwarf,
- EntriesTreeNode,
- Reader,
- ReaderOffset,
- SectionId,
- Unit,
- UnitSectionOffset,
-};
-
-static PADDING: [&str; 23] = [
- "",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
-];
-
-// 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,
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- die_node: &DebuggingInformationEntry<R>,
-) -> Vec<DisassemblyTextLine> {
- let mut disassembly_lines: Vec<DisassemblyTextLine> = Vec::with_capacity(10); // This is an estimate so "most" things won't need to resize
-
- let label_value = match die_node.offset().to_unit_section_offset(unit) {
- UnitSectionOffset::DebugInfoOffset(o) => o.0,
- UnitSectionOffset::DebugTypesOffset(o) => o.0,
- }
- .into_u64();
- let label_string = format!("#0x{:08x}", label_value);
- disassembly_lines.push(DisassemblyTextLine::from(vec![
- InstructionTextToken::new(
- &label_string,
- InstructionTextTokenContents::GotoLabel(label_value),
- ),
- InstructionTextToken::new(":", InstructionTextTokenContents::Text),
- ]));
-
- disassembly_lines.push(DisassemblyTextLine::from(vec![InstructionTextToken::new(
- die_node.tag().static_string().unwrap(),
- InstructionTextTokenContents::TypeName, // TODO : KeywordToken?
- )]));
-
- let mut attrs = die_node.attrs();
- while let Some(attr) = attrs.next().unwrap() {
- let mut attr_line: Vec<InstructionTextToken> = Vec::with_capacity(5);
- attr_line.push(InstructionTextToken::new(
- " ",
- InstructionTextTokenContents::Indentation,
- ));
-
- let len;
- if let Some(n) = attr.name().static_string() {
- len = n.len();
- attr_line.push(InstructionTextToken::new(
- n,
- InstructionTextTokenContents::FieldName,
- ));
- } else {
- // This is rather unlikely, I think
- len = 1;
- attr_line.push(InstructionTextToken::new(
- "?",
- InstructionTextTokenContents::FieldName,
- ));
- }
-
- // On command line the magic number that looks good is 22, but that's too much whitespace in a basic block, so I chose 18 (22 is the max with the current padding provided)
- if len < 18 {
- attr_line.push(InstructionTextToken::new(
- PADDING[18 - len],
- InstructionTextTokenContents::Text,
- ));
- }
- attr_line.push(InstructionTextToken::new(
- " = ",
- InstructionTextTokenContents::Text,
- ));
-
- if let Ok(Some(addr)) = dwarf.attr_address(unit, attr.value()) {
- let addr_string = format!("0x{:08x}", addr);
- attr_line.push(InstructionTextToken::new(
- &addr_string,
- InstructionTextTokenContents::Integer(addr),
- ));
- } else if let Ok(attr_reader) = dwarf.attr_string(unit, attr.value()) {
- if let Ok(attr_string) = attr_reader.to_string() {
- attr_line.push(InstructionTextToken::new(
- attr_string.as_ref(),
- InstructionTextTokenContents::String({
- let (_, id, offset) =
- dwarf.lookup_offset_id(attr_reader.offset_id()).unwrap();
- offset.into_u64() + view.section_by_name(id.name()).unwrap().start()
- }),
- ));
- } else {
- attr_line.push(InstructionTextToken::new(
- "??",
- InstructionTextTokenContents::Text,
- ));
- }
- } else if let Encoding(type_class) = attr.value() {
- attr_line.push(InstructionTextToken::new(
- type_class.static_string().unwrap(),
- InstructionTextTokenContents::TypeName,
- ));
- } else if let UnitRef(offset) = attr.value() {
- let addr = match offset.to_unit_section_offset(unit) {
- UnitSectionOffset::DebugInfoOffset(o) => o.0,
- UnitSectionOffset::DebugTypesOffset(o) => o.0,
- }
- .into_u64();
- let addr_string = format!("#0x{:08x}", addr);
- attr_line.push(InstructionTextToken::new(
- &addr_string,
- InstructionTextTokenContents::GotoLabel(addr),
- ));
- } else if let Flag(true) = attr.value() {
- attr_line.push(InstructionTextToken::new(
- "true",
- InstructionTextTokenContents::Integer(1),
- ));
- } else if let Flag(false) = attr.value() {
- attr_line.push(InstructionTextToken::new(
- "false",
- InstructionTextTokenContents::Integer(1),
- ));
-
- // Fall-back cases
- } else if let Some(value) = attr.u8_value() {
- let value_string = format!("{}", value);
- attr_line.push(InstructionTextToken::new(
- &value_string,
- InstructionTextTokenContents::Integer(value.into()),
- ));
- } else if let Some(value) = attr.u16_value() {
- let value_string = format!("{}", value);
- attr_line.push(InstructionTextToken::new(
- &value_string,
- InstructionTextTokenContents::Integer(value.into()),
- ));
- } else if let Some(value) = attr.udata_value() {
- let value_string = format!("{}", value);
- attr_line.push(InstructionTextToken::new(
- &value_string,
- InstructionTextTokenContents::Integer(value),
- ));
- } else if let Some(value) = attr.sdata_value() {
- let value_string = format!("{}", value);
- attr_line.push(InstructionTextToken::new(
- &value_string,
- InstructionTextTokenContents::Integer(value as u64),
- ));
- } else {
- let attr_string = format!("{:?}", attr.value());
- attr_line.push(InstructionTextToken::new(
- &attr_string,
- InstructionTextTokenContents::Text,
- ));
- }
- disassembly_lines.push(DisassemblyTextLine::from(attr_line));
- }
-
- disassembly_lines
-}
-
-fn process_tree<R: Reader>(
- view: &BinaryView,
- dwarf: &Dwarf<R>,
- unit: &Unit<R>,
- graph: &FlowGraph,
- graph_parent: &FlowGraphNode,
- die_node: EntriesTreeNode<R>,
-) {
- // Namespaces only - really interesting to look at!
- // if (die_node.entry().tag() == constants::DW_TAG_namespace)
- // || (die_node.entry().tag() == constants::DW_TAG_class_type)
- // || (die_node.entry().tag() == constants::DW_TAG_compile_unit)
- // || (die_node.entry().tag() == constants::DW_TAG_subprogram)
- // {
- let new_node = FlowGraphNode::new(graph);
-
- let attr_string = get_info_string(view, dwarf, unit, die_node.entry());
- new_node.set_disassembly_lines(&attr_string);
-
- graph.append(&new_node);
- graph_parent.add_outgoing_edge(
- BranchType::UnconditionalBranch,
- &new_node,
- &EdgeStyle::default(),
- );
-
- let mut children = die_node.children();
- while let Some(child) = children.next().unwrap() {
- process_tree(view, dwarf, unit, graph, &new_node, child);
- }
- // }
-}
-
-fn dump_dwarf(bv: &BinaryView) {
- let view = if bv.section_by_name(".debug_info").is_ok() {
- bv.to_owned()
- } else {
- bv.parent_view().unwrap()
- };
-
- let graph = FlowGraph::new();
- graph.set_option(FlowGraphOption::FlowGraphUsesBlockHighlights, true);
- graph.set_option(FlowGraphOption::FlowGraphUsesInstructionHighlights, true);
-
- let graph_root = FlowGraphNode::new(&graph);
- graph_root.set_lines(vec!["Graph Root"]);
- graph.append(&graph_root);
-
- let endian = dwarfreader::get_endian(bv);
- let section_reader = |section_id: SectionId| -> _ {
- dwarfreader::create_section_reader(section_id, bv, endian, false)
- };
- let dwarf = Dwarf::load(&section_reader).unwrap();
-
- let mut iter = dwarf.units();
- while let Some(header) = iter.next().unwrap() {
- let unit = dwarf.unit(header).unwrap();
- let mut entries = unit.entries();
- let mut depth = 0;
-
- if let Some((delta_depth, entry)) = entries.next_dfs().unwrap() {
- depth += delta_depth;
- assert!(depth >= 0);
-
- let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
- let root = tree.root().unwrap();
-
- process_tree(&view, &dwarf, &unit, &graph, &graph_root, root);
- }
- }
-
- view.show_graph_report("DWARF", &graph);
-}
-
-struct DWARFDump;
-
-impl Command for DWARFDump {
- fn action(&self, view: &BinaryView) {
- dump_dwarf(view);
- }
-
- fn valid(&self, view: &BinaryView) -> bool {
- is_valid(view)
- }
-}
-
-#[no_mangle]
-pub extern "C" fn UIPluginInit() -> bool {
- register(
- "DWARF Dump",
- "Show embedded DWARF info as a tree structure for you to navigate",
- DWARFDump {},
- );
- true
-}
diff --git a/rust/examples/dwarf/shared/Cargo.toml b/rust/examples/dwarf/shared/Cargo.toml
deleted file mode 100644
index ac20aac1..00000000
--- a/rust/examples/dwarf/shared/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "dwarfreader"
-version = "0.1.0"
-authors = ["Kyle Martin <kyle@vector35.com>"]
-edition = "2021"
-
-[dependencies]
-binaryninja = {path="../../../"}
-gimli = "0.31"
-zstd = "0.13.2"
-thiserror = "1.0"
diff --git a/rust/examples/dwarf/shared/src/lib.rs b/rust/examples/dwarf/shared/src/lib.rs
deleted file mode 100644
index 23baa42c..00000000
--- a/rust/examples/dwarf/shared/src/lib.rs
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use gimli::{EndianRcSlice, Endianity, RunTimeEndian, SectionId};
-
-use binaryninja::{
- binaryview::{BinaryView, BinaryViewBase, BinaryViewExt},
- Endianness,
- settings::Settings,
-};
-
-use std::rc::Rc;
-
-//////////////////////
-// Dwarf Validation
-
-#[derive(thiserror::Error, Debug)]
-pub enum Error {
- #[error("unknown section compression method {0:#x}")]
- UnknownCompressionMethod(u32),
-
- #[error("{0}")]
- GimliError(#[from] gimli::Error),
-
- #[error("{0}")]
- IoError(#[from] std::io::Error),
-}
-
-pub fn is_non_dwo_dwarf(view: &BinaryView) -> bool {
- view.section_by_name(".debug_info").is_ok() || 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()
- || 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 can_use_debuginfod(view: &BinaryView) -> bool {
- has_build_id_section(view) &&
- Settings::new("")
- .get_bool("network.enableDebuginfod", Some(view), None)
-}
-
-pub fn has_build_id_section(view: &BinaryView) -> bool {
- if let Ok(raw_view) = view.raw_view() {
- return raw_view.section_by_name(".note.gnu.build-id").is_ok()
- }
- 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,
- }
-}
-
-pub fn create_section_reader<'a, Endian: 'a + Endianity>(
- section_id: SectionId,
- view: &'a BinaryView,
- endian: Endian,
- dwo_file: bool,
-) -> Result<EndianRcSlice<Endian>, Error> {
- 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.t();
- let data = view.read_vec(data_var.address(), data_type.width() as usize);
- let element_type = data_type.element_type().unwrap().contents;
-
- if let Some(current_section_header) = data
- .chunks(element_type.width() as usize)
- .find(|section_header| {
- if view.address_size() == 4 {
- endian.read_u32(&section_header[16..20]) as u64 == section.start()
- }
- else {
- endian.read_u64(&section_header[24..32]) == section.start()
- }
- })
- {
- let section_flags = if view.address_size() == 4 {
- endian.read_u32(&current_section_header[8..12]) as u64
- }
- else {
- endian.read_u64(&current_section_header[8..16])
- };
- // If the section has the compressed bit set
- if (section_flags & 2048) != 0 {
- // Get section, trim header, decompress, return
- let compressed_header_size = view.address_size()*3;
-
- let offset = section.start() + compressed_header_size as u64;
- let len = section.len() - compressed_header_size;
-
- let ch_type_vec = view.read_vec(section.start(), 4);
- let ch_type = endian.read_u32(&ch_type_vec);
-
- if let Ok(buffer) = view.read_buffer(offset, len) {
- match ch_type {
- 1 => {
- return Ok(EndianRcSlice::new(
- buffer.zlib_decompress().get_data().into(),
- endian,
- ));
- },
- 2 => {
- return Ok(EndianRcSlice::new(
- zstd::decode_all(buffer.get_data())?.as_slice().into(),
- endian
- ));
- },
- x => {
- return Err(Error::UnknownCompressionMethod(x));
- }
- }
- }
- }
- }
- }
- }
- let offset = section.start();
- let len = section.len();
- if len == 0 {
- Ok(EndianRcSlice::new(Rc::from([]), endian))
- } else {
- Ok(EndianRcSlice::new(
- Rc::from(view.read_vec(offset, len).as_slice()),
- endian,
- ))
- }
- } else if let Ok(section) = view.section_by_name("__".to_string() + &section_name[1..]) {
- Ok(EndianRcSlice::new(
- Rc::from(view.read_vec(section.start(), section.len()).as_slice()),
- endian,
- ))
- } else {
- Ok(EndianRcSlice::new(Rc::from([]), endian))
- }
-}
diff --git a/rust/examples/flowgraph.rs b/rust/examples/flowgraph.rs
new file mode 100644
index 00000000..6666b7fb
--- /dev/null
+++ b/rust/examples/flowgraph.rs
@@ -0,0 +1,56 @@
+use binaryninja::{
+ binary_view::{BinaryView, BinaryViewExt},
+ disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind},
+ flowgraph::{BranchType, EdgePenStyle, EdgeStyle, FlowGraph, FlowGraphNode, ThemeColor},
+};
+
+fn test_graph(view: &BinaryView) {
+ let graph = FlowGraph::new();
+
+ let disassembly_lines_a = vec![DisassemblyTextLine::new(vec![
+ InstructionTextToken::new("Li", InstructionTextTokenKind::Text),
+ InstructionTextToken::new("ne", InstructionTextTokenKind::Text),
+ InstructionTextToken::new(" 1", InstructionTextTokenKind::Text),
+ ])];
+
+ let node_a = FlowGraphNode::new(&graph);
+ node_a.set_lines(disassembly_lines_a);
+
+ let node_b = FlowGraphNode::new(&graph);
+ let disassembly_lines_b = vec![DisassemblyTextLine::new(vec![
+ InstructionTextToken::new("Li", InstructionTextTokenKind::Text),
+ InstructionTextToken::new("ne", InstructionTextTokenKind::Text),
+ InstructionTextToken::new(" 2", InstructionTextTokenKind::Text),
+ ])];
+ node_b.set_lines(disassembly_lines_b);
+
+ graph.append(&node_a);
+ graph.append(&node_b);
+
+ let edge = EdgeStyle::new(EdgePenStyle::DashDotDotLine, 2, ThemeColor::AddressColor);
+ node_a.add_outgoing_edge(BranchType::UserDefinedBranch, &node_b, edge);
+ node_b.add_outgoing_edge(
+ BranchType::UnconditionalBranch,
+ &node_a,
+ EdgeStyle::default(),
+ );
+
+ view.show_graph_report("Rust Graph Title", &graph);
+}
+
+fn main() {
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
+
+ println!("Loading binary...");
+ let bv = headless_session
+ .load("/bin/cat")
+ .expect("Couldn't open `/bin/cat`");
+
+ // TODO: Register BNInteractionHandlerCallbacks with showGraphReport pointing at our function
+ // TODO: Idea: register showGraphReport that dumps a dotgraph to stdin
+
+ test_graph(&bv);
+}
diff --git a/rust/examples/flowgraph/Cargo.toml b/rust/examples/flowgraph/Cargo.toml
deleted file mode 100644
index 84db987e..00000000
--- a/rust/examples/flowgraph/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "flowgraph"
-version = "0.1.0"
-authors = ["Kyle Martin <kyle@vector35.com>"]
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-binaryninja = {path="../../"}
diff --git a/rust/examples/flowgraph/src/lib.rs b/rust/examples/flowgraph/src/lib.rs
deleted file mode 100644
index 2a03341c..00000000
--- a/rust/examples/flowgraph/src/lib.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use binaryninja::{
- binaryview::{BinaryView, BinaryViewExt},
- command::register,
- disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents},
- flowgraph::{BranchType, EdgePenStyle, EdgeStyle, FlowGraph, FlowGraphNode, ThemeColor},
-};
-
-fn test_graph(view: &BinaryView) {
- let graph = FlowGraph::new();
-
- let disassembly_lines_a = vec![DisassemblyTextLine::from(vec![
- InstructionTextToken::new("Li", InstructionTextTokenContents::Text),
- InstructionTextToken::new("ne", InstructionTextTokenContents::Text),
- InstructionTextToken::new(" 1", InstructionTextTokenContents::Text),
- ])];
-
- let node_a = FlowGraphNode::new(&graph);
- node_a.set_disassembly_lines(&disassembly_lines_a);
-
- let node_b = FlowGraphNode::new(&graph);
- let disassembly_lines_b = vec![DisassemblyTextLine::from(&vec!["Li", "ne", " 2"])];
- node_b.set_disassembly_lines(&disassembly_lines_b);
-
- let node_c = FlowGraphNode::new(&graph);
- node_c.set_lines(vec!["Line 3", "Line 4", "Line 5"]);
-
- graph.append(&node_a);
- graph.append(&node_b);
- graph.append(&node_c);
-
- let edge = EdgeStyle::new(EdgePenStyle::DashDotDotLine, 2, ThemeColor::AddressColor);
- node_a.add_outgoing_edge(BranchType::UserDefinedBranch, &node_b, &edge);
- node_a.add_outgoing_edge(
- BranchType::UnconditionalBranch,
- &node_c,
- &EdgeStyle::default(),
- );
-
- view.show_graph_report("Rust Graph Title", &graph);
-}
-
-#[no_mangle]
-pub extern "C" fn UIPluginInit() -> bool {
- register(
- "Rust Graph Test Title",
- "Rust Graph Test Description",
- test_graph,
- );
- true
-}
diff --git a/rust/examples/high_level_il.rs b/rust/examples/high_level_il.rs
new file mode 100644
index 00000000..d57509da
--- /dev/null
+++ b/rust/examples/high_level_il.rs
@@ -0,0 +1,124 @@
+use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt};
+
+fn main() {
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
+
+ println!("Loading binary...");
+ let bv = headless_session
+ .load("/bin/cat")
+ .expect("Couldn't open `/bin/cat`");
+
+ println!("Filename: `{}`", bv.file().filename());
+ println!("File size: `{:#x}`", bv.len());
+ println!("Function count: {}", bv.functions().len());
+
+ for func in &bv.functions() {
+ println!("{}:", func.symbol().full_name());
+
+ let Ok(il) = func.high_level_il(true) else {
+ continue;
+ };
+
+ // Get the SSA form for this function
+ let il = il.ssa_form();
+
+ // Loop through all blocks in the function
+ for block in il.basic_blocks().iter() {
+ // Loop though each instruction in the block
+ for instr in block.iter() {
+ // Uplift the instruction into a native rust format
+ let lifted = instr.lift();
+ let address = instr.address;
+
+ // Print the lifted instruction
+ println!("{address:08x}: {lifted:#x?}");
+
+ // Generically parse the IL tree and display the parts
+ visitor::print_il_expr(&lifted, 2);
+ }
+ }
+ }
+}
+
+mod visitor {
+ use binaryninja::high_level_il::HighLevelILLiftedOperand::*;
+ use binaryninja::high_level_il::{HighLevelILFunction, HighLevelILLiftedInstruction};
+ use binaryninja::variable::Variable;
+
+ fn print_indent(indent: usize) {
+ print!("{:<indent$}", "")
+ }
+
+ fn print_operation(operation: &HighLevelILLiftedInstruction) {
+ print!("{}", operation.name());
+ }
+
+ fn print_variable(func: &HighLevelILFunction, var: &Variable) {
+ print!("{}", func.function().variable_name(var));
+ }
+
+ pub(crate) fn print_il_expr(instr: &HighLevelILLiftedInstruction, mut indent: usize) {
+ print_indent(indent);
+ print_operation(instr);
+ println!();
+
+ indent += 1;
+
+ for (_name, operand) in instr.operands() {
+ match operand {
+ Int(int) => {
+ print_indent(indent);
+ println!("int 0x{:x}", int);
+ }
+ Float(float) => {
+ print_indent(indent);
+ println!("int {:e}", float);
+ }
+ Expr(expr) => print_il_expr(&expr, indent),
+ Var(var) => {
+ print_indent(indent);
+ print!("var ");
+ print_variable(&instr.function, &var);
+ println!();
+ }
+ VarSsa(var) => {
+ print_indent(indent);
+ print!("ssa var ");
+ print_variable(&instr.function, &var.variable);
+ println!("#{}", var.version);
+ }
+ IntList(list) => {
+ print_indent(indent);
+ print!("index list ");
+ for i in list {
+ print!("{i} ");
+ }
+ println!();
+ }
+ VarSsaList(list) => {
+ print_indent(indent);
+ print!("ssa var list ");
+ for i in list {
+ print_variable(&instr.function, &i.variable);
+ print!("#{} ", i.version);
+ }
+ println!();
+ }
+ ExprList(list) => {
+ print_indent(indent);
+ println!("expr list");
+ for i in list {
+ print_il_expr(&i, indent + 1);
+ }
+ }
+ Label(label) => println!("label {}", label.name()),
+ MemberIndex(mem_idx) => println!("member_index {:?}", mem_idx),
+ ConstantData(_) => println!("constant_data TODO"),
+ Intrinsic(_) => println!("intrinsic TODO"),
+ }
+ }
+ }
+}
diff --git a/rust/examples/hlil_lifter/Cargo.toml b/rust/examples/hlil_lifter/Cargo.toml
deleted file mode 100644
index c6c8794a..00000000
--- a/rust/examples/hlil_lifter/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "hlil_lifter"
-version = "0.1.0"
-edition = "2021"
-
-# Uncomment this if you're writing a plugin (plugins are shared objects loaded by the core):
-# [lib]
-# crate-type = ["cdylib"]
-
-# You can point at the BinaryNinja dependency in one of two ways, via path:
-[dependencies]
-binaryninja = {path="../../"}
-
-# Or directly at the git repo:
-# [dependencies]
-# binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
diff --git a/rust/examples/hlil_lifter/build.rs b/rust/examples/hlil_lifter/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/hlil_lifter/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/hlil_lifter/src/main.rs b/rust/examples/hlil_lifter/src/main.rs
deleted file mode 100644
index 36984ef1..00000000
--- a/rust/examples/hlil_lifter/src/main.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use std::env;
-
-use binaryninja::binaryview::BinaryViewExt;
-
-// Standalone executables need to provide a main function for rustc
-// Plugins should refer to `binaryninja::command::*` for the various registration callbacks.
-fn main() {
- let mut args = env::args();
- let _ = args.next().unwrap();
- let Some(filename) = args.next() else {
- panic!("Expected input filename\n");
- };
-
- // This loads all the core architecture, platform, etc plugins
- // Standalone executables probably need to call this, but plugins do not
- println!("Loading plugins...");
- binaryninja::headless::init();
-
- // Your code here...
- println!("Loading binary...");
- let bv = binaryninja::load(filename).expect("Couldn't open binary file");
-
- // Go through all functions in the binary
- for func in bv.functions().iter() {
- let sym = func.symbol();
- println!("Function {}:", sym.full_name());
-
- let Ok(il) = func.high_level_il(true) else {
- println!(" Does not have HLIL\n");
- continue;
- };
- // Get the SSA form for this function
- let il = il.ssa_form();
-
- // Loop through all blocks in the function
- for block in il.basic_blocks().iter() {
- // Loop though each instruction in the block
- for instr in block.iter() {
- // Uplift the instruction into a native rust format
- let lifted = instr.lift();
- let address = instr.address;
-
- // print the lifted instruction
- println!("{address:08x}: {lifted:x?}");
- }
- }
- println!();
- }
-
- // Important! Standalone executables need to call shutdown or they will hang forever
- binaryninja::headless::shutdown();
-}
diff --git a/rust/examples/hlil_visitor/Cargo.toml b/rust/examples/hlil_visitor/Cargo.toml
deleted file mode 100644
index 036414a5..00000000
--- a/rust/examples/hlil_visitor/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "hlil_visitor"
-version = "0.1.0"
-edition = "2021"
-
-# Uncomment this if you're writing a plugin (plugins are shared objects loaded by the core):
-# [lib]
-# crate-type = ["cdylib"]
-
-# You can point at the BinaryNinja dependency in one of two ways, via path:
-[dependencies]
-binaryninja = {path="../../"}
-
-# Or directly at the git repo:
-# [dependencies]
-# binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
diff --git a/rust/examples/hlil_visitor/build.rs b/rust/examples/hlil_visitor/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/hlil_visitor/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/hlil_visitor/src/main.rs b/rust/examples/hlil_visitor/src/main.rs
deleted file mode 100644
index e1bdefb0..00000000
--- a/rust/examples/hlil_visitor/src/main.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-use std::env;
-
-use binaryninja::binaryview::BinaryViewExt;
-use binaryninja::hlil::HighLevelILLiftedOperand;
-use binaryninja::hlil::{HighLevelILFunction, HighLevelILLiftedInstruction};
-use binaryninja::types::Variable;
-
-fn print_indent(indent: usize) {
- print!("{:<indent$}", "")
-}
-
-fn print_operation(operation: &HighLevelILLiftedInstruction) {
- print!("{}", operation.name());
-}
-
-fn print_variable(func: &HighLevelILFunction, var: &Variable) {
- print!("{}", func.get_function().get_variable_name(var));
-}
-
-fn print_il_expr(instr: &HighLevelILLiftedInstruction, mut indent: usize) {
- print_indent(indent);
- print_operation(instr);
- println!();
-
- indent += 1;
-
- use HighLevelILLiftedOperand::*;
- for (_name, operand) in instr.operands() {
- match operand {
- Int(int) => {
- print_indent(indent);
- println!("int 0x{:x}", int);
- }
- Float(float) => {
- print_indent(indent);
- println!("int {:e}", float);
- }
- Expr(expr) => print_il_expr(&expr, indent),
- Var(var) => {
- print_indent(indent);
- print!("var ");
- print_variable(&instr.function, &var);
- println!();
- }
- VarSsa(var) => {
- print_indent(indent);
- print!("ssa var ");
- print_variable(&instr.function, &var.variable);
- println!("#{}", var.version);
- }
- IntList(list) => {
- print_indent(indent);
- print!("index list ");
- for i in list {
- print!("{i} ");
- }
- println!();
- }
- VarSsaList(list) => {
- print_indent(indent);
- print!("ssa var list ");
- for i in list {
- print_variable(&instr.function, &i.variable);
- print!("#{} ", i.version);
- }
- println!();
- }
- ExprList(list) => {
- print_indent(indent);
- println!("expr list");
- for i in list {
- print_il_expr(&i, indent + 1);
- }
- }
- Label(label) => println!("label {}", label.name()),
- MemberIndex(mem_idx) => println!("member_index {:?}", mem_idx),
- ConstantData(_) => println!("constant_data TODO"),
- Intrinsic(_) => println!("intrinsic TODO"),
- }
- }
-}
-
-// Standalone executables need to provide a main function for rustc
-// Plugins should refer to `binaryninja::command::*` for the various registration callbacks.
-fn main() {
- let mut args = env::args();
- let _ = args.next().unwrap();
- let Some(filename) = args.next() else {
- panic!("Expected input filename\n");
- };
-
- // This loads all the core architecture, platform, etc plugins
- // Standalone executables probably need to call this, but plugins do not
- println!("Loading plugins...");
- binaryninja::headless::init();
-
- // Your code here...
- println!("Loading binary...");
- let bv = binaryninja::load(filename).expect("Couldn't open binary file");
-
- // Go through all functions in the binary
- for func in bv.functions().iter() {
- let sym = func.symbol();
- println!("Function {}:", sym.full_name());
-
- let Ok(il) = func.high_level_il(true) else {
- println!(" Does not have HLIL\n");
- continue;
- };
-
- // Loop through all blocks in the function
- for block in il.basic_blocks().iter() {
- // Loop though each instruction in the block
- for instr in block.iter() {
- // Generically parse the IL tree and display the parts
- print_il_expr(&instr.lift(), 2);
- }
- }
- println!();
- }
-
- // Important! Standalone executables need to call shutdown or they will hang forever
- binaryninja::headless::shutdown();
-}
diff --git a/rust/examples/idb_import/CMakeLists.txt b/rust/examples/idb_import/CMakeLists.txt
deleted file mode 100644
index 5ae51716..00000000
--- a/rust/examples/idb_import/CMakeLists.txt
+++ /dev/null
@@ -1,91 +0,0 @@
-cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
-
-project(idb_import)
-
-file(GLOB_RECURSE PLUGIN_SOURCES CONFIGURE_DEPENDS
- ${PROJECT_SOURCE_DIR}/Cargo.toml
- ${PROJECT_SOURCE_DIR}/src/*.rs)
-
-file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
- ${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)
- set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}idb_import.pdb)
-endif()
-
-set(OUTPUT_FILE ${CMAKE_STATIC_LIBRARY_PREFIX}idb_import${CMAKE_SHARED_LIBRARY_SUFFIX})
-set(PLUGIN_PATH ${TARGET_DIR}/${OUTPUT_FILE})
-
-add_custom_target(idb_import ALL DEPENDS ${PLUGIN_PATH})
-add_dependencies(idb_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}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${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() \ No newline at end of file
diff --git a/rust/examples/idb_import/Cargo.toml b/rust/examples/idb_import/Cargo.toml
deleted file mode 100644
index 6845d8d2..00000000
--- a/rust/examples/idb_import/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-name = "idb_import"
-version = "0.1.0"
-authors = ["Rubens Brandao <git@rubens.io>"]
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-anyhow = { version = "1.0.86", features = ["backtrace"] }
-binaryninja = { path = "../../" }
-idb-rs = { git = "https://github.com/Vector35/idb-rs", version = "0.1.6" }
-log = "0.4.20"
diff --git a/rust/examples/idb_import/src/addr_info.rs b/rust/examples/idb_import/src/addr_info.rs
deleted file mode 100644
index 8d1b1c1b..00000000
--- a/rust/examples/idb_import/src/addr_info.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use std::collections::HashMap;
-
-use anyhow::Result;
-
-use idb_rs::id0::ID0Section;
-use idb_rs::til;
-
-#[derive(Default)]
-pub struct AddrInfo<'a> {
- // TODO does binja diferenciate comments types on the API?
- pub comments: Vec<&'a [u8]>,
- pub label: Option<&'a str>,
- // TODO make this a ref
- pub ty: Option<til::Type>,
-}
-
-pub fn get_info(id0: &ID0Section, version: u16) -> Result<HashMap<u64, AddrInfo<'_>>> {
- let mut addr_info: HashMap<u64, AddrInfo> = HashMap::new();
-
- // the old style comments, most likely empty on new versions
- let old_comments = id0.functions_and_comments()?.filter_map(|fc| {
- use idb_rs::id0::FunctionsAndComments::*;
- match fc {
- Err(e) => Some(Err(e)),
- Ok(Comment { address, comment }) => Some(Ok((address, comment))),
- Ok(Name | Function(_) | Unknown { .. }) => None,
- }
- });
- for old_comment in old_comments {
- let (addr, comment) = old_comment?;
- let comment = comment.message();
- addr_info.entry(addr).or_default().comments.push(comment);
- }
-
- // comments defined on the address information
- for info in id0.address_info(version)? {
- use idb_rs::id0::AddressInfo::*;
- let (addr, info) = info?;
- let entry = addr_info.entry(addr).or_default();
- match info {
- Comment(comments) => entry.comments.push(comments.message()),
- Label(name) => {
- if let Some(_old) = entry.label.replace(name) {
- panic!("Duplicated label for an address should be impossible this is most likelly a programing error")
- }
- }
- TilType(ty) => {
- if let Some(_old) = entry.ty.replace(ty) {
- panic!("Duplicated type for an address should be impossible this is most likelly a programing error")
- }
- }
- Other { .. } => {}
- }
- }
-
- Ok(addr_info)
-}
diff --git a/rust/examples/idb_import/src/lib.rs b/rust/examples/idb_import/src/lib.rs
deleted file mode 100644
index 48d1b8fd..00000000
--- a/rust/examples/idb_import/src/lib.rs
+++ /dev/null
@@ -1,344 +0,0 @@
-mod types;
-use types::*;
-mod addr_info;
-use addr_info::*;
-
-use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
-use binaryninja::debuginfo::{
- CustomDebugInfoParser, DebugFunctionInfo, DebugInfo, DebugInfoParser,
-};
-
-use idb_rs::id0::{ID0Section, IDBParam1, IDBParam2};
-use idb_rs::til::section::TILSection;
-use idb_rs::til::Type as TILType;
-
-use log::{error, trace, warn, LevelFilter};
-
-use anyhow::Result;
-use binaryninja::logger::Logger;
-
-struct IDBDebugInfoParser;
-impl CustomDebugInfoParser for IDBDebugInfoParser {
- fn is_valid(&self, view: &BinaryView) -> bool {
- if let Some(project_file) = view.file().get_project_file() {
- project_file.name().as_str().ends_with(".i64")
- || project_file.name().as_str().ends_with(".idb")
- } else {
- view.file().filename().as_str().ends_with(".i64")
- || view.file().filename().as_str().ends_with(".idb")
- }
- }
-
- fn parse_info(
- &self,
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- ) -> bool {
- match parse_idb_info(debug_info, bv, debug_file, progress) {
- Ok(()) => true,
- Err(error) => {
- error!("Unable to parse IDB file: {error}");
- false
- }
- }
- }
-}
-
-struct TILDebugInfoParser;
-impl CustomDebugInfoParser for TILDebugInfoParser {
- fn is_valid(&self, view: &BinaryView) -> bool {
- if let Some(project_file) = view.file().get_project_file() {
- project_file.name().as_str().ends_with(".til")
- } else {
- view.file().filename().as_str().ends_with(".til")
- }
- }
-
- fn parse_info(
- &self,
- debug_info: &mut DebugInfo,
- _bv: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- ) -> bool {
- match parse_til_info(debug_info, debug_file, progress) {
- Ok(()) => true,
- Err(error) => {
- error!("Unable to parse TIL file: {error}");
- false
- }
- }
- }
-}
-
-struct BinaryViewReader<'a> {
- bv: &'a BinaryView,
- offset: u64,
-}
-impl std::io::Read for BinaryViewReader<'_> {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- if !self.bv.offset_valid(self.offset) {
- return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
- }
- let len = self.bv.read(buf, self.offset);
- self.offset += u64::try_from(len).unwrap();
- Ok(len)
- }
-}
-
-impl std::io::Seek for BinaryViewReader<'_> {
- fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
- let new_offset = match pos {
- std::io::SeekFrom::Start(offset) => Some(offset),
- std::io::SeekFrom::End(end) => u64::try_from(self.bv.len())
- .unwrap()
- .checked_add_signed(end),
- std::io::SeekFrom::Current(next) => self.offset.checked_add_signed(next),
- };
- let new_offset =
- new_offset.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""))?;
- if !self.bv.offset_valid(new_offset) {
- return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
- }
- self.offset = new_offset;
- Ok(new_offset)
- }
-}
-
-fn parse_idb_info(
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
-) -> Result<()> {
- trace!("Opening a IDB file");
- let file = BinaryViewReader {
- bv: debug_file,
- offset: 0,
- };
- trace!("Parsing a IDB file");
- let file = std::io::BufReader::new(file);
- let mut parser = idb_rs::IDBParser::new(file)?;
- if let Some(til_section) = parser.til_section_offset() {
- trace!("Parsing the TIL section");
- let til = parser.read_til_section(til_section)?;
- // progress 0%-50%
- import_til_section(debug_info, debug_file, &til, progress)?;
- }
-
- if let Some(id0_section) = parser.id0_section_offset() {
- trace!("Parsing the ID0 section");
- let id0 = parser.read_id0_section(id0_section)?;
- // progress 50%-100%
- parse_id0_section_info(debug_info, bv, debug_file, &id0)?;
- }
-
- Ok(())
-}
-
-fn parse_til_info(
- debug_info: &mut DebugInfo,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
-) -> Result<()> {
- trace!("Opening a TIL file");
- let file = BinaryViewReader {
- bv: debug_file,
- offset: 0,
- };
- let file = std::io::BufReader::new(file);
- trace!("Parsing the TIL section");
- let til = TILSection::parse(file)?;
- import_til_section(debug_info, debug_file, &til, progress)
-}
-
-pub fn import_til_section(
- debug_info: &mut DebugInfo,
- debug_file: &BinaryView,
- til: &TILSection,
- progress: impl Fn(usize, usize) -> Result<(), ()>,
-) -> Result<()> {
- let types = types::translate_til_types(debug_file.default_arch().unwrap(), til, progress)?;
-
- // print any errors
- for ty in &types {
- match &ty.ty {
- TranslateTypeResult::NotYet => {
- panic!(
- "type could not be processed `{}`: {:#?}",
- &String::from_utf8_lossy(&ty.name),
- &ty.og_ty
- );
- }
- TranslateTypeResult::Error(error) => {
- error!(
- "Unable to parse type `{}`: {error}",
- &String::from_utf8_lossy(&ty.name)
- );
- }
- TranslateTypeResult::PartiallyTranslated(_, error) => {
- if let Some(error) = error {
- error!(
- "Unable to parse type `{}` correctly: {error}",
- &String::from_utf8_lossy(&ty.name)
- );
- } else {
- warn!(
- "Type `{}` maybe not be fully translated",
- &String::from_utf8_lossy(&ty.name)
- );
- }
- }
- TranslateTypeResult::Translated(_) => {}
- };
- }
-
- // add all type to binary ninja
- for ty in &types {
- if let TranslateTypeResult::Translated(bn_ty)
- | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty
- {
- if !debug_info.add_type(&String::from_utf8_lossy(&ty.name), bn_ty, &[/* TODO */]) {
- error!(
- "Unable to add type `{}`",
- &String::from_utf8_lossy(&ty.name)
- )
- }
- }
- }
-
- // add a second time to fix the references LOL
- for ty in &types {
- if let TranslateTypeResult::Translated(bn_ty)
- | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty
- {
- if !debug_info.add_type(&String::from_utf8_lossy(&ty.name), bn_ty, &[/* TODO */]) {
- error!(
- "Unable to fix type `{}`",
- &String::from_utf8_lossy(&ty.name)
- )
- }
- }
- }
-
- Ok(())
-}
-
-fn parse_id0_section_info(
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- id0: &ID0Section,
-) -> Result<()> {
- let version = match id0.ida_info()? {
- idb_rs::id0::IDBParam::V1(IDBParam1 { version, .. })
- | idb_rs::id0::IDBParam::V2(IDBParam2 { version, .. }) => version,
- };
-
- for (addr, info) in get_info(id0, version)? {
- // just in case we change this struct in the future, this line will for us to review this code
- // TODO merge this data with folder locations
- let AddrInfo {
- comments,
- label,
- ty,
- } = info;
- // TODO set comments to address here
- for function in &bv.functions_containing(addr) {
- function.set_comment_at(
- addr,
- String::from_utf8_lossy(&comments.join(&b"\n"[..])).to_string(),
- );
- }
-
- let bnty = ty
- .as_ref()
- .and_then(|ty| match translate_ephemeral_type(debug_file, ty) {
- TranslateTypeResult::Translated(result) => Some(result),
- TranslateTypeResult::PartiallyTranslated(result, None) => {
- warn!("Unable to fully translate the type at {addr:#x}");
- Some(result)
- }
- TranslateTypeResult::NotYet => {
- error!("Unable to translate the type at {addr:#x}");
- None
- }
- TranslateTypeResult::PartiallyTranslated(_, Some(bn_type_error))
- | TranslateTypeResult::Error(bn_type_error) => {
- error!("Unable to translate the type at {addr:#x}: {bn_type_error}",);
- None
- }
- });
-
- match (label, &ty, bnty) {
- (_, Some(TILType::Function(_)), bnty) => {
- if bnty.is_none() {
- error!("Unable to convert the function type at {addr:#x}",)
- }
- if !debug_info.add_function(DebugFunctionInfo::new(
- None,
- None,
- label.map(str::to_string),
- bnty,
- Some(addr),
- None,
- vec![],
- vec![],
- )) {
- error!("Unable to add the function at {addr:#x}")
- }
- }
- (_, Some(_ty), Some(bnty)) => {
- if !debug_info.add_data_variable(addr, &bnty, label, &[]) {
- error!("Unable to add the type at {addr:#x}")
- }
- }
- (_, Some(_ty), None) => {
- // TODO types come from the TIL sections, can we make all types be just NamedTypes?
- error!("Unable to convert type {addr:#x}");
- // TODO how to add a label without a type associacted with it?
- if let Some(name) = label {
- if !debug_info.add_data_variable(
- addr,
- &binaryninja::types::Type::void(),
- Some(name),
- &[],
- ) {
- error!("Unable to add the label at {addr:#x}")
- }
- }
- }
- (Some(name), None, None) => {
- // TODO how to add a label without a type associacted with it?
- if !debug_info.add_data_variable(
- addr,
- &binaryninja::types::Type::void(),
- Some(name),
- &[],
- ) {
- error!("Unable to add the label at {addr:#x}")
- }
- }
-
- // just comments at this address
- (None, None, None) => {}
-
- (_, None, Some(_)) => unreachable!(),
- }
- }
-
- Ok(())
-}
-
-#[allow(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn CorePluginInit() -> bool {
- Logger::new("IDB Import")
- .with_level(LevelFilter::Error)
- .init();
- DebugInfoParser::register("IDB Parser", IDBDebugInfoParser);
- DebugInfoParser::register("TIL Parser", TILDebugInfoParser);
- true
-}
diff --git a/rust/examples/idb_import/src/types.rs b/rust/examples/idb_import/src/types.rs
deleted file mode 100644
index c614634b..00000000
--- a/rust/examples/idb_import/src/types.rs
+++ /dev/null
@@ -1,699 +0,0 @@
-use std::collections::HashMap;
-
-use anyhow::{anyhow, Result};
-use binaryninja::architecture::CoreArchitecture;
-use binaryninja::binaryninjacore_sys::{BNMemberAccess, BNMemberScope};
-use binaryninja::binaryview::{BinaryView, BinaryViewExt};
-use binaryninja::rc::Ref;
-use binaryninja::types::{
- Conf, EnumerationBuilder, FunctionParameter, StructureBuilder, StructureType, Type,
-};
-use idb_rs::til::{
- array::Array as TILArray, function::Function as TILFunction, r#enum::Enum as TILEnum,
- r#struct::Struct as TILStruct, r#struct::StructMember as TILStructMember, section::TILSection,
- union::Union as TILUnion, TILTypeInfo, Type as TILType, Typedef as TILTypedef,
-};
-
-#[derive(Debug, Clone)]
-pub enum BnTypeError {
- // TODO delete this and make this verification during the TIL/IDB parsing, translating the ordinal
- // into a kind of type_idx
- OrdinalNotFound(u32),
- NameNotFound(String),
-
- Typedef(Box<BnTypeError>),
- Function(BnTypeFunctionError),
- Array(Box<BnTypeError>),
- Pointer(Box<BnTypeError>),
- /// Error for members
- Struct(Vec<(usize, BnTypeError)>),
- Union(Vec<(usize, BnTypeError)>),
-}
-
-#[derive(Default, Debug, Clone)]
-pub struct BnTypeFunctionError {
- pub ret: Option<Box<BnTypeError>>,
- pub args: Vec<(usize, BnTypeError)>,
-}
-
-impl std::fmt::Display for BnTypeError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- BnTypeError::OrdinalNotFound(i) => write!(f, "Reference to non existing Ordinal {i}"),
- BnTypeError::NameNotFound(name) => write!(f, "Reference to non existing name {name}"),
- BnTypeError::Typedef(error) => write!(f, "Typedef: {error}"),
- BnTypeError::Function(BnTypeFunctionError { ret, args }) => {
- if let Some(error) = ret {
- write!(f, "Function return: {error} ")?;
- }
- for (i, error) in args {
- write!(f, "Function argument {i}: {error} ")?;
- }
- Ok(())
- }
- BnTypeError::Array(error) => write!(f, "Array: {error}"),
- BnTypeError::Struct(errors) => {
- for (i, error) in errors {
- write!(f, "Struct Member {i}: {error} ")?;
- }
- Ok(())
- }
- BnTypeError::Union(errors) => {
- for (i, error) in errors {
- write!(f, "Union Member {i}: {error} ")?;
- }
- Ok(())
- }
- BnTypeError::Pointer(error) => write!(f, "Pointer: {error}"),
- }
- }
-}
-
-#[derive(Default)]
-pub enum TranslateTypeResult {
- #[default]
- NotYet,
- /// Unable to solve type, there is no point in trying again
- Error(BnTypeError),
- /// a type that is not final, but equivalent to the final type, if error, there is no
- /// point in trying again
- PartiallyTranslated(Ref<Type>, Option<BnTypeError>),
- Translated(Ref<Type>),
-}
-
-impl From<Result<Ref<Type>, BnTypeError>> for TranslateTypeResult {
- fn from(value: Result<Ref<Type>, BnTypeError>) -> Self {
- match value {
- Ok(ty) => Self::Translated(ty),
- Err(error) => Self::Error(error),
- }
- }
-}
-
-pub struct TranslatesIDBType<'a> {
- // sanitized name from IDB
- pub name: Vec<u8>,
- // the result, if converted
- pub ty: TranslateTypeResult,
- pub og_ty: &'a TILTypeInfo,
- pub is_symbol: bool,
-}
-
-pub struct TranslateIDBTypes<'a, F: Fn(usize, usize) -> Result<(), ()>> {
- pub arch: CoreArchitecture,
- pub progress: F,
- pub til: &'a TILSection,
- // note it's mapped 1:1 with the same index from til types.chain(symbols)
- pub types: Vec<TranslatesIDBType<'a>>,
- // ordinals with index to types
- pub types_by_ord: HashMap<u64, usize>,
- // original names with index to types
- pub types_by_name: HashMap<Vec<u8>, usize>,
-}
-
-impl<F: Fn(usize, usize) -> Result<(), ()>> TranslateIDBTypes<'_, F> {
- fn find_typedef_by_ordinal(&self, ord: u64) -> Option<TranslateTypeResult> {
- self.types_by_ord
- .get(&ord)
- .map(|idx| self.find_typedef(&self.types[*idx]))
- }
-
- fn find_typedef_by_name(&self, name: &[u8]) -> Option<TranslateTypeResult> {
- if name.is_empty() {
- // TODO this is my assumption, maybe an empty names Typedef means something else.
- return Some(TranslateTypeResult::Translated(Type::void()));
- }
-
- if let Some(other_ty) = self
- .types_by_name
- .get(name)
- .map(|idx| self.find_typedef(&self.types[*idx]))
- {
- return Some(other_ty);
- }
-
- // check for types that ar usually not defined directly
- match name {
- b"Unkown" | b"uint8_t" => Some(TranslateTypeResult::Translated(Type::int(1, false))),
- b"IUnkown" | b"int8_t" => Some(TranslateTypeResult::Translated(Type::int(1, true))),
- b"SHORT" | b"USHORT" => Some(TranslateTypeResult::Translated(Type::int(
- self.til.size_short.get() as usize,
- name == b"SHORT",
- ))),
- b"int16_t" => Some(TranslateTypeResult::Translated(Type::int(2, true))),
- b"uint16_t" => Some(TranslateTypeResult::Translated(Type::int(2, false))),
- b"int32_t" => Some(TranslateTypeResult::Translated(Type::int(4, true))),
- b"uint32_t" => Some(TranslateTypeResult::Translated(Type::int(4, false))),
- b"int64_t" => Some(TranslateTypeResult::Translated(Type::int(8, true))),
- b"uint64_t" => Some(TranslateTypeResult::Translated(Type::int(8, false))),
- b"int128_t" => Some(TranslateTypeResult::Translated(Type::int(16, true))),
- b"uint128_t" => Some(TranslateTypeResult::Translated(Type::int(16, false))),
- _ => None,
- }
- }
-
- fn find_typedef(&self, ty: &TranslatesIDBType) -> TranslateTypeResult {
- // only return a typedef, if it's solved, at least partially
- match &ty.ty {
- TranslateTypeResult::NotYet => TranslateTypeResult::NotYet,
- TranslateTypeResult::Error(error) => {
- TranslateTypeResult::Error(BnTypeError::Typedef(Box::new(error.to_owned())))
- }
- TranslateTypeResult::PartiallyTranslated(og_ty, error) => {
- TranslateTypeResult::PartiallyTranslated(
- Type::named_type_from_type(&String::from_utf8_lossy(&ty.name), og_ty),
- error
- .as_ref()
- .map(|x| BnTypeError::Typedef(Box::new(x.clone())))
- .clone(),
- )
- }
- TranslateTypeResult::Translated(og_ty) => TranslateTypeResult::Translated(
- Type::named_type_from_type(&String::from_utf8_lossy(&ty.name), og_ty),
- ),
- }
- }
-
- fn translate_pointer(&self, ty: &TILType) -> TranslateTypeResult {
- match self.translate_type(ty) {
- TranslateTypeResult::Translated(trans) => {
- TranslateTypeResult::Translated(Type::pointer(&self.arch, &trans))
- }
- TranslateTypeResult::PartiallyTranslated(trans, error) => {
- TranslateTypeResult::PartiallyTranslated(
- Type::pointer(&self.arch, &trans),
- error.map(|e| BnTypeError::Pointer(Box::new(e))),
- )
- }
- TranslateTypeResult::Error(error) => TranslateTypeResult::PartiallyTranslated(
- Type::pointer(&self.arch, &Type::void()),
- Some(error),
- ),
- TranslateTypeResult::NotYet => TranslateTypeResult::PartiallyTranslated(
- Type::pointer(&self.arch, &Type::void()),
- None,
- ),
- }
- }
-
- fn translate_function(&self, fun: &TILFunction) -> TranslateTypeResult {
- let mut is_partial = false;
- let mut errors: BnTypeFunctionError = Default::default();
- // funtions are always 0 len, so it's translated or partial(void)
- let return_ty = match self.translate_type(&fun.ret) {
- TranslateTypeResult::Translated(trans) => trans,
- TranslateTypeResult::PartiallyTranslated(trans, error) => {
- is_partial |= true;
- errors.ret = error.map(Box::new);
- trans
- }
- TranslateTypeResult::Error(error) => {
- errors.ret = Some(Box::new(error));
- return TranslateTypeResult::PartiallyTranslated(
- Type::void(),
- Some(BnTypeError::Function(errors)),
- );
- }
- TranslateTypeResult::NotYet => {
- return TranslateTypeResult::PartiallyTranslated(Type::void(), None)
- }
- };
- let mut partial_error_args = vec![];
- let mut bn_args = Vec::with_capacity(fun.args.len());
- for (i, (arg_name, arg_type, _arg_loc)) in fun.args.iter().enumerate() {
- let arg = match self.translate_type(arg_type) {
- TranslateTypeResult::Translated(trans) => trans,
- TranslateTypeResult::PartiallyTranslated(trans, error) => {
- is_partial = true;
- if let Some(error) = error {
- errors.args.push((i, error));
- }
- trans
- }
- TranslateTypeResult::NotYet => {
- return TranslateTypeResult::PartiallyTranslated(Type::void(), None)
- }
- TranslateTypeResult::Error(error) => {
- partial_error_args.push((i, error));
- return TranslateTypeResult::PartiallyTranslated(
- Type::void(),
- Some(BnTypeError::Function(errors)),
- );
- }
- };
- // TODO create location from `arg_loc`?
- let loc = None;
- let name = arg_name
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .unwrap_or_else(|| format!("arg_{i}"));
- bn_args.push(FunctionParameter::new(arg, name, loc));
- }
-
- let ty = Type::function(&return_ty, &bn_args, false);
- if is_partial {
- let error = (errors.ret.is_some() || !errors.args.is_empty())
- .then(|| BnTypeError::Function(errors));
- TranslateTypeResult::PartiallyTranslated(ty, error)
- } else {
- assert!(errors.ret.is_none() && errors.args.is_empty());
- TranslateTypeResult::Translated(ty)
- }
- }
-
- fn translate_array(&self, array: &TILArray) -> TranslateTypeResult {
- match self.translate_type(&array.elem_type) {
- TranslateTypeResult::NotYet => TranslateTypeResult::NotYet,
- TranslateTypeResult::Translated(ty) => {
- TranslateTypeResult::Translated(Type::array(&ty, array.nelem.into()))
- }
- TranslateTypeResult::PartiallyTranslated(ty, error) => {
- TranslateTypeResult::PartiallyTranslated(
- Type::array(&ty, array.nelem.into()),
- error.map(Box::new).map(BnTypeError::Array),
- )
- }
- TranslateTypeResult::Error(error) => {
- TranslateTypeResult::Error(BnTypeError::Array(Box::new(error)))
- }
- }
- }
-
- fn condensate_bitfields_from_struct(
- &self,
- offset: usize,
- members_slice: &[TILStructMember],
- struct_builder: &StructureBuilder,
- ) {
- if members_slice.is_empty() {
- unreachable!()
- }
- let mut members = members_slice
- .iter()
- .map(|ty| match &ty.member_type {
- TILType::Bitfield(b) => b,
- _ => unreachable!(),
- })
- .enumerate();
- let (_, first_field) = members.next().unwrap();
- let mut current_field_bytes = first_field.nbytes;
- let mut current_field_bits: u32 = first_field.width.into();
- let mut start_idx = 0;
-
- let create_field = |start_idx, i, bytes| {
- let name = if start_idx == i - 1 {
- let member: &TILStructMember = &members_slice[i - 1];
- member
- .name
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .unwrap_or_else(|| format!("bitfield_{}", offset + start_idx))
- } else {
- format!("bitfield_{}_{}", offset + start_idx, offset + (i - 1))
- };
- let field = field_from_bytes(bytes);
- struct_builder.append(
- &field,
- name,
- BNMemberAccess::NoAccess,
- BNMemberScope::NoScope,
- );
- };
-
- for (i, member) in members {
- // starting a new field
- let max_bits = u32::try_from(current_field_bytes).unwrap() * 8;
- // this bitfield start a a new field, or can't contain other bitfields
- // finish the previous and start a new
- if current_field_bytes != member.nbytes
- || max_bits < current_field_bits + u32::from(member.width)
- {
- create_field(start_idx, i, current_field_bytes);
- current_field_bytes = member.nbytes;
- current_field_bits = 0;
- start_idx = i;
- }
-
- // just add the current bitfield into the field
- current_field_bits += u32::from(member.width);
- }
-
- if current_field_bits != 0 {
- create_field(start_idx, members_slice.len(), current_field_bytes);
- }
- }
-
- fn translate_struct(
- &self,
- members: &[TILStructMember],
- effective_alignment: u16,
- ) -> TranslateTypeResult {
- if members.is_empty() {
- // binary ninja crashes if you create an empty struct, because it divide by 0
- return TranslateTypeResult::Translated(Type::void());
- }
- let mut is_partial = false;
- let structure = StructureBuilder::new();
- structure.set_alignment(effective_alignment.into());
-
- let mut errors = vec![];
- let mut first_bitfield_seq = None;
- for (i, member) in members.iter().enumerate() {
- match (&member.member_type, first_bitfield_seq) {
- // accumulate the bitfield to be condensated
- (TILType::Bitfield(_bit), None) => {
- first_bitfield_seq = Some(i);
- continue;
- }
- (TILType::Bitfield(_bit), Some(_)) => continue,
-
- // condensate the bitfields into byte-wide fields
- (_, Some(start_idx)) => {
- first_bitfield_seq = None;
- let members_bitrange = &members[start_idx..i];
- self.condensate_bitfields_from_struct(start_idx, members_bitrange, &structure);
- }
-
- (_, None) => {}
- }
-
- let mem = match self.translate_type(&member.member_type) {
- TranslateTypeResult::Translated(ty) => ty,
- TranslateTypeResult::PartiallyTranslated(partial_ty, error) => {
- is_partial |= true;
- if let Some(error) = error {
- errors.push((i, error));
- }
- partial_ty
- }
- TranslateTypeResult::NotYet => return TranslateTypeResult::NotYet,
- TranslateTypeResult::Error(error) => {
- errors.push((i, error));
- return TranslateTypeResult::Error(BnTypeError::Struct(errors));
- }
- };
- let name = member
- .name
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .unwrap_or_else(|| format!("member_{i}"));
- structure.append(&mem, name, BNMemberAccess::NoAccess, BNMemberScope::NoScope);
- }
- if let Some(start_idx) = first_bitfield_seq {
- let members_bitrange = &members[start_idx..];
- self.condensate_bitfields_from_struct(start_idx, members_bitrange, &structure);
- }
- let bn_ty = Type::structure(&structure.finalize());
- if is_partial {
- let partial_error = (!errors.is_empty()).then_some(BnTypeError::Struct(errors));
- TranslateTypeResult::PartiallyTranslated(bn_ty, partial_error)
- } else {
- assert!(errors.is_empty());
- TranslateTypeResult::Translated(bn_ty)
- }
- }
-
- fn translate_union(
- &self,
- members: &[(Option<Vec<u8>>, TILType)],
- _effective_alignment: u16,
- ) -> TranslateTypeResult {
- let mut is_partial = false;
- let structure = StructureBuilder::new();
- structure.set_structure_type(StructureType::UnionStructureType);
- let mut errors = vec![];
- for (i, (member_name, member_type)) in members.iter().enumerate() {
- // bitfields can be translated into complete fields
- let mem = match member_type {
- TILType::Bitfield(field) => field_from_bytes(field.nbytes),
- member_type => match self.translate_type(member_type) {
- TranslateTypeResult::Translated(ty) => ty,
- TranslateTypeResult::Error(error) => {
- errors.push((i, error));
- return TranslateTypeResult::Error(BnTypeError::Union(errors));
- }
- TranslateTypeResult::NotYet => return TranslateTypeResult::NotYet,
- TranslateTypeResult::PartiallyTranslated(partial, error) => {
- is_partial |= true;
- if let Some(error) = error {
- errors.push((i, error));
- }
- partial
- }
- },
- };
-
- let name = member_name
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .unwrap_or_else(|| format!("member_{i}"));
- structure.append(&mem, name, BNMemberAccess::NoAccess, BNMemberScope::NoScope);
- }
- let str_ref = structure.finalize();
-
- let bn_ty = Type::structure(&str_ref);
- if is_partial {
- let partial_error = (!errors.is_empty()).then_some(BnTypeError::Struct(errors));
- TranslateTypeResult::PartiallyTranslated(bn_ty, partial_error)
- } else {
- assert!(errors.is_empty());
- TranslateTypeResult::Translated(bn_ty)
- }
- }
-
- fn translate_enum(members: &[(Option<Vec<u8>>, u64)], bytesize: u64) -> Ref<Type> {
- let eb = EnumerationBuilder::new();
- for (i, (name, bytesize)) in members.iter().enumerate() {
- let name = name
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .unwrap_or_else(|| format!("member_{i}"));
- eb.insert(name, *bytesize);
- }
- Type::enumeration(
- &eb.finalize(),
- usize::try_from(bytesize).unwrap(),
- Conf::new(false, 0),
- )
- }
-
- fn translate_basic(mdata: &idb_rs::til::Basic) -> Ref<Type> {
- match *mdata {
- idb_rs::til::Basic::Void => Type::void(),
- idb_rs::til::Basic::Unknown { bytes: 0 } => Type::void(),
- idb_rs::til::Basic::Unknown { bytes } => Type::array(&Type::char(), bytes.into()),
- idb_rs::til::Basic::Bool { bytes } if bytes.get() == 1 => Type::bool(),
- // NOTE Binja don't have any representation for bool other then the default
- idb_rs::til::Basic::Bool { bytes } => Type::int(bytes.get().into(), false),
- idb_rs::til::Basic::Char => Type::char(),
- // TODO what exacly is Segment Register?
- idb_rs::til::Basic::SegReg => Type::char(),
- idb_rs::til::Basic::Int { bytes, is_signed } => {
- // default into signed
- let is_signed = is_signed.as_ref().copied().unwrap_or(true);
- Type::int(bytes.get().into(), is_signed)
- }
- idb_rs::til::Basic::Float { bytes } => Type::float(bytes.get().into()),
- }
- }
-
- pub fn translate_type(&self, ty: &TILType) -> TranslateTypeResult {
- match &ty {
- // types that are always translatable
- TILType::Basic(meta) => TranslateTypeResult::Translated(Self::translate_basic(meta)),
- TILType::Bitfield(bit) => TranslateTypeResult::Translated(field_from_bytes(bit.nbytes)),
- TILType::Enum(TILEnum::NonRef {
- members, bytesize, ..
- }) => TranslateTypeResult::Translated(Self::translate_enum(members, *bytesize)),
- TILType::Typedef(TILTypedef::Ordinal(ord)) => self
- .find_typedef_by_ordinal((*ord).into())
- .unwrap_or_else(|| TranslateTypeResult::Error(BnTypeError::OrdinalNotFound(*ord))),
- TILType::Typedef(TILTypedef::Name(name)) => {
- self.find_typedef_by_name(name).unwrap_or_else(|| {
- TranslateTypeResult::Error(BnTypeError::NameNotFound(
- String::from_utf8_lossy(name).to_string(),
- ))
- })
- }
-
- // may not be translatable imediatly, but the size is known and can be
- // updated after alBasicers are finished
- TILType::Union(TILUnion::Ref { ref_type, .. })
- | TILType::Struct(TILStruct::Ref { ref_type, .. })
- | TILType::Enum(TILEnum::Ref { ref_type, .. }) => self.translate_pointer(ref_type),
- TILType::Pointer(ty) => self.translate_pointer(&ty.typ),
- TILType::Function(fun) => self.translate_function(fun),
-
- // can only be partially solved if all fields are solved or partially solved
- TILType::Array(array) => self.translate_array(array),
- TILType::Struct(TILStruct::NonRef {
- members,
- effective_alignment,
- ..
- }) => self.translate_struct(members, *effective_alignment),
- TILType::Union(TILUnion::NonRef {
- members,
- effective_alignment,
- ..
- }) => self.translate_union(members, *effective_alignment),
- }
- }
-}
-
-pub fn translate_ephemeral_type(debug_file: &BinaryView, ty: &TILType) -> TranslateTypeResult {
- // in case we need to translate types
- let translator = TranslateIDBTypes {
- arch: debug_file.default_arch().unwrap(/* TODO */),
- progress: |_, _| Ok(()),
- // TODO it's unclear what to do here
- til: &TILSection {
- format: 12,
- title: Vec::new(),
- description: Vec::new(),
- id: 0,
- cm: 0,
- def_align: 1,
- symbols: vec![],
- type_ordinal_alias: None,
- types: vec![],
- size_i: 4.try_into().unwrap(),
- size_b: 1.try_into().unwrap(),
- size_short: 2.try_into().unwrap(),
- size_long: 4.try_into().unwrap(),
- size_long_long: 8.try_into().unwrap(),
- size_long_double: None,
- macros: None,
- is_universal: false,
- },
- types: vec![],
- types_by_ord: HashMap::new(),
- types_by_name: HashMap::new(),
- };
-
- translator.translate_type(ty)
-}
-
-pub fn translate_til_types(
- arch: CoreArchitecture,
- til: &TILSection,
- progress: impl Fn(usize, usize) -> Result<(), ()>,
-) -> Result<Vec<TranslatesIDBType>> {
- let total = til.symbols.len() + til.types.len();
- let mut types = Vec::with_capacity(total);
- let mut types_by_ord = HashMap::with_capacity(total);
- let mut types_by_name = HashMap::with_capacity(total);
- let all_types = til.types.iter().zip(core::iter::repeat(false));
- // TODO: it's unclear how the demangle symbols and types names/ord, for now only parse types
- //let all_types = all_types.chain(til.symbols.iter().zip(core::iter::repeat(true)));
- for (i, (ty, is_symbol)) in all_types.enumerate() {
- // TODO sanitized the input
- // TODO find out how the namespaces used by TIL works
- let name = ty.name.to_owned();
- types.push(TranslatesIDBType {
- name,
- is_symbol,
- og_ty: ty,
- ty: TranslateTypeResult::NotYet,
- });
- if ty.ordinal != 0 && !is_symbol {
- let dup1 = types_by_ord.insert(ty.ordinal, i);
- if let Some(old) = dup1 {
- let old_type = &types[old];
- let new_type = types.last().unwrap();
- // TODO error?
- panic!(
- "dup ord {}:{} {}:\n{:?}\n{:?}",
- old_type.is_symbol,
- new_type.is_symbol,
- ty.ordinal,
- &old_type.og_ty,
- &new_type.og_ty,
- )
- }
- }
- if !ty.name.is_empty() {
- let dup2 = types_by_name.insert(ty.name.to_owned(), i);
- if let Some(old) = dup2 {
- let old_type = &types[old];
- let new_type = types.last().unwrap();
- // TODO error?
- panic!(
- "dup name {}:{}: {}:\n{:?}\n{:?}",
- old_type.is_symbol,
- new_type.is_symbol,
- &String::from_utf8_lossy(&ty.name),
- &old_type.og_ty,
- &new_type.og_ty,
- )
- }
- }
- }
-
- let mut translator = TranslateIDBTypes {
- arch,
- progress,
- til,
- types,
- types_by_ord,
- types_by_name,
- };
- if (translator.progress)(0, total).is_err() {
- return Err(anyhow!("IDB import aborted"));
- }
-
- // solve types until there is nothing else that can be solved
- loop {
- // if something was solved, mark this variable as true
- let mut did_something = false;
- let mut num_translated = 0usize;
- for i in 0..translator.types.len() {
- match &translator.types[i].ty {
- TranslateTypeResult::NotYet => {
- let result = translator.translate_type(&translator.types[i].og_ty.tinfo);
- did_something |= !matches!(&result, TranslateTypeResult::NotYet);
- translator.types[i].ty = result;
- }
- TranslateTypeResult::PartiallyTranslated(_, None) => {
- let result = translator.translate_type(&translator.types[i].og_ty.tinfo);
- // don't allow regress, it can goes from PartiallyTranslated to any state other then NotYet
- assert!(!matches!(&result, TranslateTypeResult::NotYet));
- did_something |=
- !matches!(&result, TranslateTypeResult::PartiallyTranslated(_, None));
- translator.types[i].ty = result;
- // don't need to add again they will be fixed on the loop below
- }
- // if an error was produced, there is no point in try again
- TranslateTypeResult::PartiallyTranslated(_, Some(_)) => {}
- // NOTE for now we are just accumulating errors, just try to translate the max number
- // of types as possible
- TranslateTypeResult::Error(_) => {}
- // already translated, nothing do to here
- TranslateTypeResult::Translated(_) => {}
- }
-
- // count the number of finished types
- if let TranslateTypeResult::Translated(_) = &translator.types[i].ty {
- num_translated += 1
- }
- }
-
- if !did_something {
- // means we acomplilshed nothing during this loop, there is no point in trying again
- break;
- }
- if (translator.progress)(num_translated, total).is_err() {
- // error means the user aborted the progress
- break;
- }
- }
-
- Ok(translator.types)
-}
-
-fn field_from_bytes(bytes: i32) -> Ref<Type> {
- match bytes {
- 0 => unreachable!(),
- num @ (1 | 2 | 4 | 8 | 16) => Type::int(num.try_into().unwrap(), false),
- nelem => Type::array(&Type::char(), nelem.try_into().unwrap()),
- }
-}
diff --git a/rust/examples/medium_level_il.rs b/rust/examples/medium_level_il.rs
new file mode 100644
index 00000000..61c3a330
--- /dev/null
+++ b/rust/examples/medium_level_il.rs
@@ -0,0 +1,145 @@
+use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt};
+
+fn main() {
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
+
+ println!("Loading binary...");
+ let bv = headless_session
+ .load("/bin/cat")
+ .expect("Couldn't open `/bin/cat`");
+
+ println!("Filename: `{}`", bv.file().filename());
+ println!("File size: `{:#x}`", bv.len());
+ println!("Function count: {}", bv.functions().len());
+
+ for func in &bv.functions() {
+ println!("{}:", func.symbol().full_name());
+
+ let Ok(il) = func.medium_level_il() else {
+ continue;
+ };
+
+ // Get the SSA form for this function
+ let il = il.ssa_form();
+
+ // Loop through all blocks in the function
+ for block in il.basic_blocks().iter() {
+ // Loop though each instruction in the block
+ for instr in block.iter() {
+ // Uplift the instruction into a native rust format
+ let lifted = instr.lift();
+ let address = instr.address;
+
+ // Print the lifted instruction
+ println!("{address:08x}: {lifted:#x?}");
+
+ // Generically parse the IL tree and display the parts
+ visitor::print_il_expr(&lifted, 2);
+ }
+ }
+ }
+}
+
+mod visitor {
+ use binaryninja::architecture::Intrinsic;
+ use binaryninja::medium_level_il::MediumLevelILLiftedOperand::*;
+ use binaryninja::medium_level_il::{MediumLevelILFunction, MediumLevelILLiftedInstruction};
+ use binaryninja::variable::Variable;
+
+ fn print_indent(indent: usize) {
+ print!("{:<indent$}", "")
+ }
+
+ fn print_operation(operation: &MediumLevelILLiftedInstruction) {
+ print!("{}", operation.name());
+ }
+
+ fn print_variable(func: &MediumLevelILFunction, var: &Variable) {
+ print!("{}", func.function().variable_name(var));
+ }
+
+ pub(crate) fn print_il_expr(instr: &MediumLevelILLiftedInstruction, mut indent: usize) {
+ print_indent(indent);
+ print_operation(instr);
+
+ println!();
+
+ indent += 1;
+
+ for (_name, operand) in instr.operands() {
+ match operand {
+ Int(int) => {
+ print_indent(indent);
+ println!("int 0x{:x}", int);
+ }
+ Float(float) => {
+ print_indent(indent);
+ println!("float {:e}", float);
+ }
+ Expr(expr) => print_il_expr(&expr, indent),
+ Var(var) => {
+ print_indent(indent);
+ print!("var ");
+ print_variable(&instr.function, &var);
+ println!();
+ }
+ VarSsa(var) => {
+ print_indent(indent);
+ print!("ssa var ");
+ print_variable(&instr.function, &var.variable);
+ println!("#{}", var.version);
+ }
+ IntList(list) => {
+ print_indent(indent);
+ print!("index list ");
+ for i in list {
+ print!("{i} ");
+ }
+ println!();
+ }
+ VarList(list) => {
+ print_indent(indent);
+ print!("var list ");
+ for i in list {
+ print_variable(&instr.function, &i);
+ print!(" ");
+ }
+ println!();
+ }
+ VarSsaList(list) => {
+ print_indent(indent);
+ print!("ssa var list ");
+ for i in list {
+ print_variable(&instr.function, &i.variable);
+ print!("#{} ", i.version);
+ }
+ println!();
+ }
+ ExprList(list) => {
+ print_indent(indent);
+ println!("expr list");
+ for i in list {
+ print_il_expr(&i, indent + 1);
+ }
+ }
+ TargetMap(list) => {
+ print_indent(indent);
+ print!("target map ");
+ for (i, f) in list {
+ print!("({i}, {f}) ");
+ }
+ println!();
+ }
+ ConstantData(_) => println!("contantdata"),
+ Intrinsic(intrinsic) => println!("intrinsic {}", intrinsic.name()),
+ InstructionIndex(idx) => {
+ print_indent(indent);
+ println!("index {}", idx);
+ }
+ }
+ }
+ }
+}
diff --git a/rust/examples/minidump/.gitignore b/rust/examples/minidump/.gitignore
deleted file mode 100644
index 4fffb2f8..00000000
--- a/rust/examples/minidump/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/target
-/Cargo.lock
diff --git a/rust/examples/minidump/Cargo.toml b/rust/examples/minidump/Cargo.toml
deleted file mode 100644
index 5f5a7317..00000000
--- a/rust/examples/minidump/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "minidump_bn"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-binaryninja = {path="../../"}
-log = "0.4.20"
-minidump = "0.23.0"
diff --git a/rust/examples/minidump/README.md b/rust/examples/minidump/README.md
deleted file mode 100644
index 3c54ca8e..00000000
--- a/rust/examples/minidump/README.md
+++ /dev/null
@@ -1,65 +0,0 @@
-# Binary Ninja Minidump Loader
-
-A Minidump memory dump loader plugin for Binary Ninja.
-
-![Screenshot of Binary Ninja using the "Minidump" Binary View, with a minidump loaded and the virtual addresses of the memory segments of the minidump showing in the Memory Map window](images/loaded-minidump-screenshot-border.png)
-
-This plugin adds a new _Minidump_ binary view type. When a binary with the magic number `MDMP` is opened, this plugin will automatically try to load in the binary as a minidump, and create a new _Minidump_ binary view to view the contents.
-
-The architecture is determined automatically from the platform information embedded in the minidump.
-
-![Screenshot showing the Minidump binary view type in the dropdown list of available binary views for an open binary](images/minidump-binary-view-type-screenshot-border.png)
-
-The loaded minidump's memory regions and modules can be navigated via the _Memory Map_ window. In the _Minidump_ binary view, the meanings of "Segments" and "Sections" in the Memory Map window are modified to mean the following:
-
-- The memory regions in the minidump are loaded as _Segments_. The _Data Offset_ and _Data Length_ fields of each segment are the corresponding addresses in the minidump file where the data for that memory region is located.
-- The modules in the minidump are loaded as _Sections_, with the name of each section being the path to the module.
-
-![Screenshot showing the Memory Map window with the loaded minidump's memory segments and modules (i.e. "sections")](images/minidump-segments-sections-screenshot-border.png)
-
-## Supported Minidump Types
-
-This plugin currently only supports loading minidump files generated by the Windows [`MiniDumpWriteDump` API](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump).
-
-This includes dumps generated from:
-
-- The [`.dump` command](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/-dump--create-dump-file-) in WinDbg.
-- The `.dump` command in Binary Ninja's debugger for Windows targets (which uses the same debugging engine as WinDbg).
-
-For both of the above, it's recommended to generate a full dump:
-
-```
-.dump /ma dumpfile.dmp
-```
-
-- The [`minidump` command](https://help.x64dbg.com/en/latest/commands/memory-operations/minidump.html) in x64dbg.
-
-```
-minidump dumpfile.dmp
-```
-
-- Right clicking on a listed process and then clicking "Create dump file" / "Create full dump" from Windows Task Manager, Process Hacker, Sysinternals Process Explorer, etc...
-
-## Unsupported Features (for now)
-
-- Loading Minidump files from platforms or APIs other than Windows' `MinidumpWriteDump`, such as those generated by [Google Breakpad](https://chromium.googlesource.com/breakpad/breakpad/).
-- Loading and applyng debug information from the minidump file. In Windows minidump files, `MinidumpModuleList` streams contain information about the PDB file which contains the debug information for the module; this isn't currently read or applied, however.
-- Integration with Binary Ninja's built-in debugger. Minidump files can contain information about threads, register values, and stack frames, and it would be nice in the future for minidump files to be loadable back into the debugger in order to resume a debugging session. This isn't currently done, however.
-
-## Building and Installing
-
-This plugin currently needs to be built from source, then copied into your user plugin folder.
-
-```
-cargo build --release
-cp target/release/libminidump_bn.so ~/.binaryninja/plugins/
-```
-
-The code in this plugin targets the `dev` branch of the [Binary Ninja Rust API](https://github.com/Vector35/binaryninja-api/tree/dev/rust).
-
-To update the Binary Ninja Rust API dependency:
-
-```
-cargo update -p binaryninja
-cargo build --release
-``` \ No newline at end of file
diff --git a/rust/examples/minidump/build.rs b/rust/examples/minidump/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/minidump/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/minidump/images/loaded-minidump-screenshot-border.png b/rust/examples/minidump/images/loaded-minidump-screenshot-border.png
deleted file mode 100644
index 51b8282c..00000000
--- a/rust/examples/minidump/images/loaded-minidump-screenshot-border.png
+++ /dev/null
Binary files differ
diff --git a/rust/examples/minidump/images/minidump-binary-view-type-screenshot-border.png b/rust/examples/minidump/images/minidump-binary-view-type-screenshot-border.png
deleted file mode 100644
index 9a52dd66..00000000
--- a/rust/examples/minidump/images/minidump-binary-view-type-screenshot-border.png
+++ /dev/null
Binary files differ
diff --git a/rust/examples/minidump/images/minidump-segments-sections-screenshot-border.png b/rust/examples/minidump/images/minidump-segments-sections-screenshot-border.png
deleted file mode 100644
index 0035acca..00000000
--- a/rust/examples/minidump/images/minidump-segments-sections-screenshot-border.png
+++ /dev/null
Binary files differ
diff --git a/rust/examples/minidump/src/command.rs b/rust/examples/minidump/src/command.rs
deleted file mode 100644
index d33a8d0b..00000000
--- a/rust/examples/minidump/src/command.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use std::str;
-
-use log::{debug, error, info};
-use minidump::{Minidump, MinidumpMemoryInfoList};
-
-use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
-
-pub fn print_memory_information(bv: &BinaryView) {
- debug!("Printing memory information");
- if let Ok(minidump_bv) = bv.parent_view() {
- if let Ok(read_buffer) = minidump_bv.read_buffer(0, minidump_bv.len()) {
- if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) {
- if let Ok(memory_info_list) = minidump_obj.get_stream::<MinidumpMemoryInfoList>() {
- let mut memory_info_list_writer = Vec::new();
- match memory_info_list.print(&mut memory_info_list_writer) {
- Ok(_) => {
- if let Ok(memory_info_str) = str::from_utf8(&memory_info_list_writer) {
- info!("{memory_info_str}");
- } else {
- error!("Could not convert the memory information description from minidump into a valid string");
- }
- }
- Err(_) => {
- error!("Could not get memory information from minidump");
- }
- }
- } else {
- error!(
- "Could not parse a valid MinidumpMemoryInfoList stream from the minidump"
- );
- }
- } else {
- error!("Could not parse a valid minidump file from the parent binary view's data buffer");
- }
- } else {
- error!("Could not read data from parent binary view");
- }
- } else {
- error!("Could not get the parent binary view");
- }
-}
diff --git a/rust/examples/minidump/src/lib.rs b/rust/examples/minidump/src/lib.rs
deleted file mode 100644
index 62898301..00000000
--- a/rust/examples/minidump/src/lib.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use binaryninja::binaryview::BinaryView;
-use binaryninja::command::{register, Command};
-use binaryninja::custombinaryview::register_view_type;
-use log::{debug, LevelFilter};
-use binaryninja::logger::Logger;
-
-mod command;
-mod view;
-
-struct PrintMemoryInformationCommand;
-
-impl Command for PrintMemoryInformationCommand {
- fn action(&self, binary_view: &BinaryView) {
- command::print_memory_information(binary_view);
- }
-
- fn valid(&self, _binary_view: &BinaryView) -> bool {
- true // TODO: Of course, the command will not always be valid!
- }
-}
-
-#[no_mangle]
-#[allow(non_snake_case)]
-pub extern "C" fn CorePluginInit() -> bool {
- Logger::new("Minidump").with_level(LevelFilter::Trace).init();
-
- debug!("Registering minidump binary view type");
- register_view_type("Minidump", "Minidump", view::MinidumpBinaryViewType::new);
-
- debug!("Registering minidump plugin commands");
- register(
- "Minidump\\[DEBUG] Print Minidump Memory Information",
- "Print a human-readable description of the contents of the MinidumpMemoryInfoList stream in the loaded minidump",
- PrintMemoryInformationCommand {},
- );
-
- true
-}
diff --git a/rust/examples/minidump/src/view.rs b/rust/examples/minidump/src/view.rs
deleted file mode 100644
index ddb18c77..00000000
--- a/rust/examples/minidump/src/view.rs
+++ /dev/null
@@ -1,402 +0,0 @@
-use std::collections::HashMap;
-use std::ops::Range;
-
-use binaryninja::section::Section;
-use binaryninja::segment::Segment;
-use log::{debug, error, info, warn};
-use minidump::format::MemoryProtection;
-use minidump::{
- Minidump, MinidumpMemory64List, MinidumpMemoryInfoList, MinidumpMemoryList, MinidumpModuleList,
- MinidumpStream, MinidumpSystemInfo, Module,
-};
-
-use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
-use binaryninja::custombinaryview::{
- BinaryViewType, BinaryViewTypeBase, CustomBinaryView, CustomBinaryViewType, CustomView,
- CustomViewBuilder,
-};
-use binaryninja::platform::Platform;
-use binaryninja::Endianness;
-
-type BinaryViewResult<R> = binaryninja::binaryview::Result<R>;
-
-/// The _Minidump_ binary view type, which the Rust plugin registers with the Binary Ninja core
-/// (via `binaryninja::custombinaryview::register_view_type`) as a possible binary view
-/// that can be applied to opened binaries.
-///
-/// If this view type is valid for an opened binary (determined by `is_valid_for`),
-/// the Binary Ninja core then uses this view type to create an actual instance of the _Minidump_
-/// binary view (via `create_custom_view`).
-pub struct MinidumpBinaryViewType {
- view_type: BinaryViewType,
-}
-
-impl MinidumpBinaryViewType {
- pub fn new(view_type: BinaryViewType) -> Self {
- MinidumpBinaryViewType { view_type }
- }
-}
-
-impl AsRef<BinaryViewType> for MinidumpBinaryViewType {
- fn as_ref(&self) -> &BinaryViewType {
- &self.view_type
- }
-}
-
-impl BinaryViewTypeBase for MinidumpBinaryViewType {
- fn is_deprecated(&self) -> bool {
- false
- }
-
- fn is_force_loadable(&self) -> bool {
- false
- }
-
- fn is_valid_for(&self, data: &BinaryView) -> bool {
- let mut magic_number = Vec::<u8>::new();
- data.read_into_vec(&mut magic_number, 0, 4);
-
- magic_number == b"MDMP"
- }
-}
-
-impl CustomBinaryViewType for MinidumpBinaryViewType {
- fn create_custom_view<'builder>(
- &self,
- data: &BinaryView,
- builder: CustomViewBuilder<'builder, Self>,
- ) -> BinaryViewResult<CustomView<'builder>> {
- debug!("Creating MinidumpBinaryView from registered MinidumpBinaryViewType");
-
- let binary_view = builder.create::<MinidumpBinaryView>(data, ());
- binary_view
- }
-}
-
-#[derive(Debug)]
-struct SegmentData {
- rva_range: Range<u64>,
- mapped_addr_range: Range<u64>,
-}
-
-impl SegmentData {
- fn from_addresses_and_size(rva: u64, mapped_addr: u64, size: u64) -> Self {
- SegmentData {
- rva_range: Range {
- start: rva,
- end: rva + size,
- },
- mapped_addr_range: Range {
- start: mapped_addr,
- end: mapped_addr + size,
- },
- }
- }
-}
-
-#[derive(Debug)]
-struct SegmentMemoryProtection {
- readable: bool,
- writable: bool,
- executable: bool,
-}
-
-/// An instance of the actual _Minidump_ custom binary view.
-/// This contains the main logic to load the memory segments inside a minidump file into the binary view.
-pub struct MinidumpBinaryView {
- /// The handle to the "real" BinaryView object, in the Binary Ninja core.
- inner: binaryninja::rc::Ref<BinaryView>,
-}
-
-impl MinidumpBinaryView {
- fn new(view: &BinaryView) -> Self {
- MinidumpBinaryView {
- inner: view.to_owned(),
- }
- }
-
- fn init(&self) -> BinaryViewResult<()> {
- let parent_view = self.parent_view()?;
- let read_buffer = parent_view.read_buffer(0, parent_view.len())?;
-
- if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) {
- // Architecture, platform information
- if let Ok(minidump_system_info) = minidump_obj.get_stream::<MinidumpSystemInfo>() {
- if let Some(platform) = MinidumpBinaryView::translate_minidump_platform(
- minidump_system_info.cpu,
- minidump_obj.endian,
- minidump_system_info.os,
- ) {
- self.set_default_platform(&platform);
- } else {
- error!(
- "Could not parse valid system information from minidump: could not map system information in MinidumpSystemInfo stream (arch {:?}, endian {:?}, os {:?}) to a known architecture",
- minidump_system_info.cpu,
- minidump_obj.endian,
- minidump_system_info.os,
- );
- return Err(());
- }
- } else {
- error!("Could not parse system information from minidump: could not find a valid MinidumpSystemInfo stream");
- return Err(());
- }
-
- // Memory segments
- let mut segment_data = Vec::<SegmentData>::new();
-
- // Memory segments in a full memory dump (MinidumpMemory64List)
- // Grab the shared base RVA for all entries in the MinidumpMemory64List,
- // since the minidump crate doesn't expose this to us
- if let Ok(raw_stream) = minidump_obj.get_raw_stream(MinidumpMemory64List::STREAM_TYPE) {
- if let Ok(base_rva_array) = raw_stream[8..16].try_into() {
- let base_rva = u64::from_le_bytes(base_rva_array);
- debug!("Found BaseRVA value {:#x}", base_rva);
-
- if let Ok(minidump_memory_list) =
- minidump_obj.get_stream::<MinidumpMemory64List>()
- {
- let mut current_rva = base_rva;
- for memory_segment in minidump_memory_list.iter() {
- debug!(
- "Found memory segment at RVA {:#x} with virtual address {:#x} and size {:#x}",
- current_rva,
- memory_segment.base_address,
- memory_segment.size,
- );
- segment_data.push(SegmentData::from_addresses_and_size(
- current_rva,
- memory_segment.base_address,
- memory_segment.size,
- ));
- current_rva += memory_segment.size;
- }
- }
- } else {
- error!("Could not parse BaseRVA value shared by all entries in the MinidumpMemory64List stream")
- }
- } else {
- warn!("Could not read memory from minidump: could not find a valid MinidumpMemory64List stream. This minidump may not be a full memory dump. Trying to find partial dump memory from a MinidumpMemoryList now...");
- // Memory segments in a regular memory dump (MinidumpMemoryList),
- // i.e. one that does not include the full process memory data.
- if let Ok(minidump_memory_list) = minidump_obj.get_stream::<MinidumpMemoryList>() {
- for memory_segment in minidump_memory_list.by_addr() {
- debug!(
- "Found memory segment at RVA {:#x} with virtual address {:#x} and size {:#x}",
- memory_segment.desc.memory.rva,
- memory_segment.base_address,
- memory_segment.size
- );
- segment_data.push(SegmentData::from_addresses_and_size(
- memory_segment.desc.memory.rva as u64,
- memory_segment.base_address,
- memory_segment.size,
- ));
- }
- } else {
- error!("Could not read any memory from minidump: could not find a valid MinidumpMemory64List stream or a valid MinidumpMemoryList stream.");
- }
- }
-
- // Memory protection information
- let mut segment_protection_data = HashMap::new();
-
- if let Ok(minidump_memory_info_list) =
- minidump_obj.get_stream::<MinidumpMemoryInfoList>()
- {
- for memory_info in minidump_memory_info_list.iter() {
- if let Some(memory_range) = memory_info.memory_range() {
- debug!(
- "Found memory protection info for memory segment ranging from virtual address {:#x} to {:#x}: {:#?}",
- memory_range.start,
- memory_range.end,
- memory_info.protection
- );
- segment_protection_data.insert(
- // The range returned to us by MinidumpMemoryInfoList is an
- // end-inclusive range_map::Range; we need to add 1 to
- // the end index to make it into an end-exclusive std::ops::Range.
- Range {
- start: memory_range.start,
- end: memory_range.end + 1,
- },
- memory_info.protection,
- );
- }
- }
- }
-
- for segment in segment_data.iter() {
- if let Some(segment_protection) =
- segment_protection_data.get(&segment.mapped_addr_range)
- {
- let segment_memory_protection =
- MinidumpBinaryView::translate_memory_protection(*segment_protection);
-
- info!(
- "Adding memory segment at virtual address {:#x} to {:#x}, from data range {:#x} to {:#x}, with protections readable {}, writable {}, executable {}",
- segment.mapped_addr_range.start,
- segment.mapped_addr_range.end,
- segment.rva_range.start,
- segment.rva_range.end,
- segment_memory_protection.readable,
- segment_memory_protection.writable,
- segment_memory_protection.executable,
- );
-
- self.add_segment(
- Segment::builder(segment.mapped_addr_range.clone())
- .parent_backing(segment.rva_range.clone())
- .is_auto(true)
- .readable(segment_memory_protection.readable)
- .writable(segment_memory_protection.writable)
- .executable(segment_memory_protection.executable),
- );
- } else {
- error!(
- "Could not find memory protection information for memory segment from {:#x} to {:#x}", segment.mapped_addr_range.start,
- segment.mapped_addr_range.end,
- );
- }
- }
-
- // Module information
- // This stretches the concept a bit, but we can add each module as a
- // separate "section" of the binary.
- // Sections can be named, and can span multiple segments.
- if let Ok(minidump_module_list) = minidump_obj.get_stream::<MinidumpModuleList>() {
- for module_info in minidump_module_list.by_addr() {
- info!(
- "Found module with name {} at virtual address {:#x} with size {:#x}",
- module_info.name,
- module_info.base_address(),
- module_info.size(),
- );
- let module_address_range = Range {
- start: module_info.base_address(),
- end: module_info.base_address() + module_info.size(),
- };
- self.add_section(
- Section::builder(module_info.name.clone(), module_address_range)
- .is_auto(true),
- );
- }
- } else {
- warn!("Could not find valid module information in minidump: could not find a valid MinidumpModuleList stream");
- }
- } else {
- error!("Could not parse data as minidump");
- return Err(());
- }
- Ok(())
- }
-
- fn translate_minidump_platform(
- minidump_cpu_arch: minidump::system_info::Cpu,
- minidump_endian: minidump::Endian,
- minidump_os: minidump::system_info::Os,
- ) -> Option<binaryninja::rc::Ref<Platform>> {
- match minidump_os {
- minidump::system_info::Os::Windows => match minidump_cpu_arch {
- minidump::system_info::Cpu::Arm64 => Platform::by_name("windows-aarch64"),
- minidump::system_info::Cpu::Arm => Platform::by_name("windows-armv7"),
- minidump::system_info::Cpu::X86 => Platform::by_name("windows-x86"),
- minidump::system_info::Cpu::X86_64 => Platform::by_name("windows-x86_64"),
- _ => None,
- },
- minidump::system_info::Os::MacOs => match minidump_cpu_arch {
- minidump::system_info::Cpu::Arm64 => Platform::by_name("mac-aarch64"),
- minidump::system_info::Cpu::Arm => Platform::by_name("mac-armv7"),
- minidump::system_info::Cpu::X86 => Platform::by_name("mac-x86"),
- minidump::system_info::Cpu::X86_64 => Platform::by_name("mac-x86_64"),
- _ => None,
- },
- minidump::system_info::Os::Linux => match minidump_cpu_arch {
- minidump::system_info::Cpu::Arm64 => Platform::by_name("linux-aarch64"),
- minidump::system_info::Cpu::Arm => Platform::by_name("linux-armv7"),
- minidump::system_info::Cpu::X86 => Platform::by_name("linux-x86"),
- minidump::system_info::Cpu::X86_64 => Platform::by_name("linux-x86_64"),
- minidump::system_info::Cpu::Ppc => match minidump_endian {
- minidump::Endian::Little => Platform::by_name("linux-ppc32_le"),
- minidump::Endian::Big => Platform::by_name("linux-ppc32"),
- },
- minidump::system_info::Cpu::Ppc64 => match minidump_endian {
- minidump::Endian::Little => Platform::by_name("linux-ppc64_le"),
- minidump::Endian::Big => Platform::by_name("linux-ppc64"),
- },
- _ => None,
- },
- minidump::system_info::Os::NaCl => None,
- minidump::system_info::Os::Android => None,
- minidump::system_info::Os::Ios => None,
- minidump::system_info::Os::Ps3 => None,
- minidump::system_info::Os::Solaris => None,
- _ => None,
- }
- }
-
- fn translate_memory_protection(
- minidump_memory_protection: MemoryProtection,
- ) -> SegmentMemoryProtection {
- let (readable, writable, executable) = match minidump_memory_protection {
- MemoryProtection::PAGE_NOACCESS => (false, false, false),
- MemoryProtection::PAGE_READONLY => (true, false, false),
- MemoryProtection::PAGE_READWRITE => (true, true, false),
- MemoryProtection::PAGE_WRITECOPY => (true, true, false),
- MemoryProtection::PAGE_EXECUTE => (false, false, true),
- MemoryProtection::PAGE_EXECUTE_READ => (true, false, true),
- MemoryProtection::PAGE_EXECUTE_READWRITE => (true, true, true),
- MemoryProtection::PAGE_EXECUTE_WRITECOPY => (true, true, true),
- MemoryProtection::ACCESS_MASK => (false, false, false),
- MemoryProtection::PAGE_GUARD => (false, false, false),
- MemoryProtection::PAGE_NOCACHE => (false, false, false),
- MemoryProtection::PAGE_WRITECOMBINE => (false, false, false),
- _ => (false, false, false),
- };
- SegmentMemoryProtection {
- readable,
- writable,
- executable,
- }
- }
-}
-
-impl AsRef<BinaryView> for MinidumpBinaryView {
- fn as_ref(&self) -> &BinaryView {
- &self.inner
- }
-}
-
-impl BinaryViewBase for MinidumpBinaryView {
- // TODO: This should be filled out with the actual address size
- // from the platform information in the minidump.
- fn address_size(&self) -> usize {
- 0
- }
-
- fn default_endianness(&self) -> Endianness {
- // TODO: This should be filled out with the actual endianness
- // from the platform information in the minidump.
- Endianness::LittleEndian
- }
-
- fn entry_point(&self) -> u64 {
- // TODO: We should fill this out with a real entry point.
- // This can be done by getting the main module of the minidump
- // with MinidumpModuleList::main_module,
- // then parsing the PE metadata of the main module to find its entry point(s).
- 0
- }
-}
-
-unsafe impl CustomBinaryView for MinidumpBinaryView {
- type Args = ();
-
- fn new(handle: &BinaryView, _args: &Self::Args) -> BinaryViewResult<Self> {
- Ok(MinidumpBinaryView::new(handle))
- }
-
- fn init(&mut self, _args: Self::Args) -> BinaryViewResult<()> {
- MinidumpBinaryView::init(self)
- }
-}
diff --git a/rust/examples/mlil_lifter/Cargo.toml b/rust/examples/mlil_lifter/Cargo.toml
deleted file mode 100644
index 4ea158fc..00000000
--- a/rust/examples/mlil_lifter/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "mlil_lifter"
-version = "0.1.0"
-edition = "2021"
-
-# Uncomment this if you're writing a plugin (plugins are shared objects loaded by the core):
-# [lib]
-# crate-type = ["cdylib"]
-
-# You can point at the BinaryNinja dependency in one of two ways, via path:
-[dependencies]
-binaryninja = {path="../../"}
-
-# Or directly at the git repo:
-# [dependencies]
-# binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
diff --git a/rust/examples/mlil_lifter/build.rs b/rust/examples/mlil_lifter/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/mlil_lifter/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/mlil_lifter/src/main.rs b/rust/examples/mlil_lifter/src/main.rs
deleted file mode 100644
index f6e04f22..00000000
--- a/rust/examples/mlil_lifter/src/main.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use std::env;
-
-use binaryninja::binaryview::BinaryViewExt;
-
-// Standalone executables need to provide a main function for rustc
-// Plugins should refer to `binaryninja::command::*` for the various registration callbacks.
-fn main() {
- let mut args = env::args();
- let _ = args.next().unwrap();
- let Some(filename) = args.next() else {
- panic!("Expected input filename\n");
- };
-
- // This loads all the core architecture, platform, etc plugins
- // Standalone executables probably need to call this, but plugins do not
- println!("Loading plugins...");
- let _headless_session = binaryninja::headless::Session::new();
-
- // Your code here...
- println!("Loading binary...");
- let bv = binaryninja::load(filename).expect("Couldn't open binary file");
-
- // Go through all functions in the binary
- for func in bv.functions().iter() {
- let sym = func.symbol();
- println!("Function {}:", sym.full_name());
-
- let Ok(il) = func.medium_level_il() else {
- println!(" Does not have MLIL\n");
- continue;
- };
- // Get the SSA form for this function
- let il = il.ssa_form();
-
- // Loop through all blocks in the function
- for block in il.basic_blocks().iter() {
- // Loop though each instruction in the block
- for instr in block.iter() {
- // Uplift the instruction into a native rust format
- let lifted = instr.lift();
- let address = instr.address;
-
- // print the lifted instruction
- println!("{address:08x}: {lifted:x?}");
- }
- }
- println!();
- }
-}
diff --git a/rust/examples/mlil_visitor/Cargo.toml b/rust/examples/mlil_visitor/Cargo.toml
deleted file mode 100644
index 76ca1222..00000000
--- a/rust/examples/mlil_visitor/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "mlil_visitor"
-version = "0.1.0"
-edition = "2021"
-
-# Uncomment this if you're writing a plugin (plugins are shared objects loaded by the core):
-# [lib]
-# crate-type = ["cdylib"]
-
-# You can point at the BinaryNinja dependency in one of two ways, via path:
-[dependencies]
-binaryninja = {path="../../"}
-
-# Or directly at the git repo:
-# [dependencies]
-# binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
diff --git a/rust/examples/mlil_visitor/build.rs b/rust/examples/mlil_visitor/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/mlil_visitor/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/mlil_visitor/src/main.rs b/rust/examples/mlil_visitor/src/main.rs
deleted file mode 100644
index 752548c9..00000000
--- a/rust/examples/mlil_visitor/src/main.rs
+++ /dev/null
@@ -1,137 +0,0 @@
-use std::env;
-
-use binaryninja::architecture::Intrinsic;
-use binaryninja::binaryview::BinaryViewExt;
-use binaryninja::mlil::MediumLevelILLiftedOperand;
-use binaryninja::mlil::{MediumLevelILFunction, MediumLevelILLiftedInstruction};
-use binaryninja::types::Variable;
-
-fn print_indent(indent: usize) {
- print!("{:<indent$}", "")
-}
-
-fn print_operation(operation: &MediumLevelILLiftedInstruction) {
- print!("{}", operation.name());
-}
-
-fn print_variable(func: &MediumLevelILFunction, var: &Variable) {
- print!("{}", func.get_function().get_variable_name(var));
-}
-
-fn print_il_expr(instr: &MediumLevelILLiftedInstruction, mut indent: usize) {
- print_indent(indent);
- print_operation(instr);
- println!();
-
- indent += 1;
-
- use MediumLevelILLiftedOperand::*;
- for (_name, operand) in instr.operands() {
- match operand {
- Int(int) => {
- print_indent(indent);
- println!("int 0x{:x}", int);
- }
- Float(float) => {
- print_indent(indent);
- println!("int {:e}", float);
- }
- Expr(expr) => print_il_expr(&expr, indent),
- Var(var) => {
- print_indent(indent);
- print!("var ");
- print_variable(&instr.function, &var);
- println!();
- }
- VarSsa(var) => {
- print_indent(indent);
- print!("ssa var ");
- print_variable(&instr.function, &var.variable);
- println!("#{}", var.version);
- }
- IntList(list) => {
- print_indent(indent);
- print!("index list ");
- for i in list {
- print!("{i} ");
- }
- println!();
- }
- VarList(list) => {
- print_indent(indent);
- print!("var list ");
- for i in list {
- print_variable(&instr.function, &i);
- print!(" ");
- }
- println!();
- }
- VarSsaList(list) => {
- print_indent(indent);
- print!("ssa var list ");
- for i in list {
- print_variable(&instr.function, &i.variable);
- print!("#{} ", i.version);
- }
- println!();
- }
- ExprList(list) => {
- print_indent(indent);
- println!("expr list");
- for i in list {
- print_il_expr(&i, indent + 1);
- }
- }
- TargetMap(list) => {
- print_indent(indent);
- print!("target map ");
- for (i, f) in list {
- print!("({i}, {f}) ");
- }
- println!();
- }
- ConstantData(_) => println!("contantdata"),
- Intrinsic(intrinsic) => println!("intrinsic {}", intrinsic.name()),
- }
- }
-}
-
-// Standalone executables need to provide a main function for rustc
-// Plugins should refer to `binaryninja::command::*` for the various registration callbacks.
-fn main() {
- let mut args = env::args();
- let _ = args.next().unwrap();
- let Some(filename) = args.next() else {
- panic!("Expected input filename\n");
- };
-
- // This loads all the core architecture, platform, etc plugins
- // Standalone executables probably need to call this, but plugins do not
- println!("Loading plugins...");
- let _headless_session = binaryninja::headless::Session::new();
-
- // Your code here...
- println!("Loading binary...");
- let bv = binaryninja::load(filename).expect("Couldn't open binary file");
-
- // Go through all functions in the binary
- for func in bv.functions().iter() {
- let sym = func.symbol();
- println!("Function {}:", sym.full_name());
-
- let Ok(il) = func.medium_level_il() else {
- println!(" Does not have MLIL\n");
- continue;
- };
-
- // Loop through all blocks in the function
- for block in il.basic_blocks().iter() {
- // Loop though each instruction in the block
- for instr in block.iter() {
- // Generically parse the IL tree and display the parts
- print_il_expr(&instr.lift(), 2);
- }
- }
- println!();
- }
-}
diff --git a/rust/examples/pdb-ng/.gitignore b/rust/examples/pdb-ng/.gitignore
deleted file mode 100644
index eb5a316c..00000000
--- a/rust/examples/pdb-ng/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-target
diff --git a/rust/examples/pdb-ng/CMakeLists.txt b/rust/examples/pdb-ng/CMakeLists.txt
deleted file mode 100644
index c88d4125..00000000
--- a/rust/examples/pdb-ng/CMakeLists.txt
+++ /dev/null
@@ -1,138 +0,0 @@
-cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
-
-project(pdb_import_plugin)
-
-file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS
- ${PROJECT_SOURCE_DIR}/Cargo.toml
- ${PROJECT_SOURCE_DIR}/src/*.rs)
-
-file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
- ${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()
-
-if(FORCE_COLORED_OUTPUT)
- set(CARGO_OPTS ${CARGO_OPTS} --color always)
-endif()
-
-if(DEMO)
- set(CARGO_FEATURES --features demo --manifest-path ${PROJECT_SOURCE_DIR}/demo/Cargo.toml)
-
- set(OUTPUT_FILE_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}${PROJECT_NAME}_static${CMAKE_STATIC_LIBRARY_SUFFIX})
- set(OUTPUT_PDB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}${PROJECT_NAME}.pdb)
- set(OUTPUT_FILE_PATH ${CMAKE_BINARY_DIR}/${OUTPUT_FILE_NAME})
- set(OUTPUT_PDB_PATH ${CMAKE_BINARY_DIR}/${OUTPUT_PDB_NAME})
-
- set(BINJA_LIB_DIR $<TARGET_FILE_DIR:binaryninjacore>)
-else()
- set(CARGO_FEATURES "")
-
- set(OUTPUT_FILE_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
- set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PROJECT_NAME}.pdb)
- set(OUTPUT_FILE_PATH ${BN_CORE_PLUGIN_DIR}/${OUTPUT_FILE_NAME})
- set(OUTPUT_PDB_PATH ${BN_CORE_PLUGIN_DIR}/${OUTPUT_PDB_NAME})
-
- set(BINJA_LIB_DIR ${BN_INSTALL_BIN_DIR})
-endif()
-
-add_custom_target(${PROJECT_NAME} ALL DEPENDS ${OUTPUT_FILE_PATH})
-add_dependencies(${PROJECT_NAME} binaryninjaapi)
-get_target_property(BN_API_SOURCE_DIR binaryninjaapi SOURCE_DIR)
-list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake")
-find_package(BinaryNinjaCore REQUIRED)
-
-set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_FILE_PATH ${OUTPUT_FILE_PATH})
-
-find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
-set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_STABLE_VERSION} cargo)
-
-if(APPLE)
- if(UNIVERSAL)
- if(CMAKE_BUILD_TYPE MATCHES Debug)
- set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE_NAME})
- set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE_NAME})
- else()
- set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE_NAME})
- set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE_NAME})
- endif()
-
- add_custom_command(
- OUTPUT ${OUTPUT_FILE_PATH}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
- ${RUSTUP_COMMAND} clean --target=aarch64-apple-darwin ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
- ${RUSTUP_COMMAND} clean --target=x86_64-apple-darwin ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
- ${RUSTUP_COMMAND} build --target=aarch64-apple-darwin ${CARGO_OPTS} ${CARGO_FEATURES}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
- ${RUSTUP_COMMAND} build --target=x86_64-apple-darwin ${CARGO_OPTS} ${CARGO_FEATURES}
- COMMAND lipo -create ${AARCH64_LIB_PATH} ${X86_64_LIB_PATH} -output ${OUTPUT_FILE_PATH}
- 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_NAME})
- else()
- set(LIB_PATH ${PROJECT_BINARY_DIR}/target/release/${OUTPUT_FILE_NAME})
- endif()
-
- add_custom_command(
- OUTPUT ${OUTPUT_FILE_PATH}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
- ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E env
- MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
- ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
- COMMAND ${CMAKE_COMMAND} -E copy ${LIB_PATH} ${OUTPUT_FILE_PATH}
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
- )
- endif()
-elseif(WIN32)
- if(DEMO)
- add_custom_command(
- OUTPUT ${OUTPUT_FILE_PATH}
- COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
- )
- else()
- add_custom_command(
- OUTPUT ${OUTPUT_FILE_PATH}
- COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${OUTPUT_PDB_PATH}
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
- )
- endif()
-else()
- add_custom_command(
- OUTPUT ${OUTPUT_FILE_PATH}
- COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
- COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
- COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
- )
-endif()
diff --git a/rust/examples/pdb-ng/Cargo.toml b/rust/examples/pdb-ng/Cargo.toml
deleted file mode 100644
index 6302f121..00000000
--- a/rust/examples/pdb-ng/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "pdb-import-plugin"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-anyhow = "^1.0"
-binaryninja = {path = "../../"}
-itertools = "^0.11"
-log = "^0.4"
-pdb = "^0.8"
-regex = "1"
-
-[features]
-demo = []
diff --git a/rust/examples/pdb-ng/demo/Cargo.toml b/rust/examples/pdb-ng/demo/Cargo.toml
deleted file mode 100644
index 656d1c6c..00000000
--- a/rust/examples/pdb-ng/demo/Cargo.toml
+++ /dev/null
@@ -1,21 +0,0 @@
-[package]
-name = "pdb-import-plugin-static"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["staticlib"]
-path = "../src/lib.rs"
-
-[dependencies]
-anyhow = "^1.0"
-binaryninja = {path = "../../../"}
-home = "^0.5.5"
-itertools = "^0.11"
-log = "^0.4"
-pdb = "^0.8"
-cab = "^0.4"
-regex = "1"
-
-[features]
-demo = []
diff --git a/rust/examples/pdb-ng/pdb-0.8.0-patched b/rust/examples/pdb-ng/pdb-0.8.0-patched
deleted file mode 160000
-Subproject 601617722313562526393425414717960606249
diff --git a/rust/examples/pdb-ng/src/lib.rs b/rust/examples/pdb-ng/src/lib.rs
deleted file mode 100644
index 4fffc20d..00000000
--- a/rust/examples/pdb-ng/src/lib.rs
+++ /dev/null
@@ -1,937 +0,0 @@
-// Copyright 2022-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::HashMap;
-use std::env::{current_dir, current_exe, temp_dir};
-use std::io::Cursor;
-use std::path::PathBuf;
-use std::str::FromStr;
-use std::sync::mpsc;
-use std::{env, fs};
-
-use anyhow::{anyhow, Result};
-use log::{debug, error, info};
-use pdb::PDB;
-
-use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
-use binaryninja::debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser};
-use binaryninja::downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider};
-use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet};
-use binaryninja::settings::Settings;
-use binaryninja::string::BnString;
-use binaryninja::{add_optional_plugin_dependency, interaction, user_directory};
-use binaryninja::logger::Logger;
-use parser::PDBParserInstance;
-
-/// PDB Parser!!
-///
-/// General project structure:
-/// - lib.rs: Interaction with DebugInfoParser and plugin actions
-/// - parser.rs: PDB Parser base functionality, puts the internal structures into the DebugInfo
-/// - type_parser.rs: Parses all the TPI type stream information into both named and indexed types
-/// - symbol_parser.rs: Parses, one module at a time, symbol information into named symbols
-/// - struct_grouper.rs: Ugly algorithm for handling union and structure members
-mod parser;
-mod struct_grouper;
-mod symbol_parser;
-mod type_parser;
-
-// struct PDBLoad;
-// struct PDBLoadFile;
-// struct PDBSetSymbolPath;
-
-#[allow(dead_code)]
-struct PDBInfo {
- path: String,
- file_name: String,
- age: u32,
- guid: Vec<u8>,
- guid_age_string: String,
-}
-
-fn is_pdb(view: &BinaryView) -> bool {
- let pdb_magic_bytes = "Microsoft C/C++ MSF 7.00\r\n\x1A\x44\x53\x00\x00\x00";
- if let Ok(raw_view) = view.raw_view() {
- raw_view.read_vec(0, pdb_magic_bytes.len()) == pdb_magic_bytes.as_bytes()
- } else {
- false
- }
-}
-
-fn default_local_cache() -> Result<String> {
- // The default value is a directory named "sym" immediately below the program directory
- // of the calling application. This is sometimes referred to as the default local cache.
- let current_path = current_exe()?;
- let parent_path = current_path
- .parent()
- .ok_or_else(|| anyhow!("No parent to current exe"))?;
- let mut cache_path = PathBuf::from(parent_path);
- cache_path.push("sym");
- return Ok(cache_path
- .to_str()
- .ok_or_else(|| anyhow!("Could not convert cache path to string"))?
- .to_string());
-}
-
-fn active_local_cache(view: Option<&BinaryView>) -> Result<String> {
- // Check the local symbol store
- let mut local_store_path = Settings::new("")
- .get_string("pdb.files.localStoreAbsolute", view, None)
- .to_string();
- if local_store_path.is_empty() {
- local_store_path = match user_directory() {
- Ok(mut dir) => {
- dir.push(
- Settings::new("")
- .get_string("pdb.files.localStoreRelative", view, None)
- .to_string(),
- );
- match dir.to_str() {
- Some(s) => s.to_string(),
- _ => "".to_string(),
- }
- }
- _ => "".to_string(),
- };
- }
- if !local_store_path.is_empty() {
- Ok(local_store_path)
- } else if let Ok(default_cache) = default_local_cache() {
- Ok(default_cache)
- } else if let Ok(current) = current_dir().map(|d| {
- d.to_str()
- .expect("Expected current dir to be a valid string")
- .to_string()
- }) {
- Ok(current)
- } else {
- Ok(temp_dir()
- .to_str()
- .expect("Expected temp dir to be a valid string")
- .to_string())
- }
-}
-
-fn parse_sym_srv(
- symbol_path: &String,
- default_store: &String,
-) -> Result<Box<dyn Iterator<Item = String>>> {
- // https://docs.microsoft.com/en-us/windows/win32/debug/using-symsrv
- // Why
-
- // ... the symbol path (_NT_SYMBOL_PATH environment variable) can be made up of several path
- // elements separated by semicolons. If any one or more of these path elements begins with
- // the text "srv*", then the element is a symbol server and will use SymSrv to locate
- // symbol files.
-
- // If the "srv*" text is not specified but the actual path element is a symbol server store,
- // then the symbol handler will act as if "srv*" were specified. The symbol handler makes
- // this determination by searching for the existence of a file called "pingme.txt" in
- // the root directory of the specified path.
-
- // ... symbol servers are made up of symbol store elements separated by asterisks. There can
- // be up to 10 symbol stores after the "srv*" prefix.
-
- let mut sym_srv_results = vec![];
-
- // 'path elements separated by semicolons'
- for path_element in symbol_path.split(';') {
- // 'begins with the text "srv*"'
- if path_element.to_lowercase().starts_with("srv*") {
- // 'symbol store elements separated by asterisks'
- for store_element in path_element[4..].split('*') {
- if store_element.is_empty() {
- sym_srv_results.push(default_store.clone());
- } else {
- sym_srv_results.push(store_element.to_string());
- }
- }
- } else if PathBuf::from(path_element).exists() {
- // 'searching for the existence of a file called "pingme.txt" in the root directory'
- let pingme_txt = path_element.to_string() + "/" + "pingme.txt";
- if PathBuf::from(pingme_txt).exists() {
- sym_srv_results.push(path_element.to_string());
- }
- }
- }
-
- Ok(Box::new(sym_srv_results.into_iter()))
-}
-
-fn read_from_sym_store(bv: &BinaryView, path: &String) -> Result<(bool, Vec<u8>)> {
- if !path.contains("://") {
- // Local file
- info!("Read local file: {}", path);
- let conts = fs::read(path)?;
- return Ok((false, conts));
- }
-
- if !Settings::new("").get_bool("network.pdbAutoDownload", Some(bv), None) {
- return Err(anyhow!("Auto download disabled"));
- }
-
- // Download from remote
- let (tx, rx) = mpsc::channel();
- let write = move |data: &[u8]| -> usize {
- if let Ok(_) = tx.send(Vec::from(data)) {
- data.len()
- } else {
- 0
- }
- };
-
- info!("GET: {}", path);
-
- let dp =
- DownloadProvider::try_default().map_err(|_| anyhow!("No default download provider"))?;
- let mut inst = dp
- .create_instance()
- .map_err(|_| anyhow!("Couldn't create download instance"))?;
- let result = inst
- .perform_custom_request(
- "GET",
- path.clone(),
- HashMap::<BnString, BnString>::new(),
- DownloadInstanceInputOutputCallbacks {
- read: None,
- write: Some(Box::new(write)),
- progress: None,
- },
- )
- .map_err(|e| anyhow!(e.to_string()))?;
- if result.status_code != 200 {
- return Err(anyhow!("Path does not exist"));
- }
-
- let mut expected_length = None;
- for (k, v) in result.headers.iter() {
- if k.to_lowercase() == "content-length" {
- expected_length = Some(usize::from_str(v)?);
- }
- }
-
- let mut data = vec![];
- while let Ok(packet) = rx.try_recv() {
- data.extend(packet.into_iter());
- }
-
- if let Some(length) = expected_length {
- if data.len() != length {
- return Err(anyhow!(format!(
- "Bad length: expected {} got {}",
- length,
- data.len()
- )));
- }
- }
-
- Ok((true, data))
-}
-
-fn search_sym_store(bv: &BinaryView, store_path: &String, pdb_info: &PDBInfo) -> Result<Option<Vec<u8>>> {
- // https://www.technlg.net/windows/symbol-server-path-windbg-debugging/
- // For symbol servers, to identify the files path easily, Windbg uses the format
- // binaryname.pdb/GUID
-
- // Doesn't actually say what the format is, just gives an example:
- // https://docs.microsoft.com/en-us/windows/win32/debug/using-symstore
- // In this example, the lookup path for the acpi.dbg symbol file might look something
- // like this: \\mybuilds\symsrv\acpi.dbg\37cdb03962040.
- let base_path =
- store_path.clone() + "/" + &pdb_info.file_name + "/" + &pdb_info.guid_age_string;
-
- // Three files may exist inside the lookup directory:
- // 1. If the file was stored, then acpi.dbg will exist there.
- // 2. If a pointer was stored, then a file called file.ptr will exist and contain the path
- // to the actual symbol file.
- // 3. A file called refs.ptr, which contains a list of all the current locations for
- // acpi.dbg with this timestamp and image size that are currently added to the
- // symbol store.
-
- // We don't care about #3 because it says we don't
-
- let direct_path = base_path.clone() + "/" + &pdb_info.file_name;
- if let Ok((_remote, conts)) = read_from_sym_store(bv, &direct_path) {
- return Ok(Some(conts));
- }
-
- let file_ptr = base_path.clone() + "/" + "file.ptr";
- if let Ok((_remote, conts)) = read_from_sym_store(bv, &file_ptr) {
- let path = String::from_utf8(conts)?;
- // PATH:https://full/path
- if path.starts_with("PATH:") {
- if let Ok((_remote, conts)) = read_from_sym_store(bv, &path[5..].to_string()) {
- return Ok(Some(conts));
- }
- }
- }
-
- return Ok(None);
-}
-
-fn parse_pdb_info(view: &BinaryView) -> Option<PDBInfo> {
- match view.get_metadata::<u64, _>("DEBUG_INFO_TYPE") {
- Some(Ok(0x53445352 /* 'SDSR' */)) => {}
- _ => return None,
- }
-
- // This is stored in the BV by the PE loader
- let file_path = match view.get_metadata::<String, _>("PDB_FILENAME") {
- Some(Ok(md)) => md,
- _ => return None,
- };
- let mut guid = match view.get_metadata::<Vec<u8>, _>("PDB_GUID") {
- Some(Ok(md)) => md,
- _ => return None,
- };
- let age = match view.get_metadata::<u64, _>("PDB_AGE") {
- Some(Ok(md)) => md as u32,
- _ => return None,
- };
-
- if guid.len() != 16 {
- return None;
- }
-
- // struct _GUID {
- // uint32_t Data1;
- // uint16_t Data2;
- // uint16_t Data3;
- // uint8_t Data4[8];
- // };
-
- // Endian swap
- // Data1
- guid.swap(0, 3);
- guid.swap(1, 2);
- // Data2
- guid.swap(4, 5);
- // Data3
- guid.swap(6, 7);
-
- let guid_age_string = guid
- .iter()
- .take(16)
- .map(|ch| format!("{:02X}", ch))
- .collect::<Vec<_>>()
- .join("")
- + &format!("{:X}", age);
-
- // Just assume all the paths are /
- let file_path = if cfg!(windows) {
- file_path
- } else {
- file_path.replace("\\", "/")
- };
- let path = file_path;
- let file_name = if let Some(idx) = path.rfind("\\") {
- path[(idx + 1)..].to_string()
- } else if let Some(idx) = path.rfind("/") {
- path[(idx + 1)..].to_string()
- } else {
- path.clone()
- };
-
- Some(PDBInfo {
- path,
- file_name,
- age,
- guid,
- guid_age_string,
- })
-}
-
-struct PDBParser;
-impl PDBParser {
- fn load_from_file(
- &self,
- conts: &Vec<u8>,
- debug_info: &mut DebugInfo,
- view: &BinaryView,
- progress: &Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- check_guid: bool,
- did_download: bool,
- ) -> Result<()> {
- let mut pdb = PDB::open(Cursor::new(&conts))?;
-
- let settings = Settings::new("");
-
- if let Some(info) = parse_pdb_info(view) {
- let pdb_info = &pdb.pdb_information()?;
- if info.guid.as_slice() != pdb_info.guid.as_ref() {
- if check_guid {
- return Err(anyhow!("PDB GUID does not match"));
- } else {
- let ask = settings.get_string(
- "pdb.features.loadMismatchedPDB",
- Some(view),
- None,
- );
-
- match ask.as_str() {
- "true" => {},
- "ask" => {
- if interaction::show_message_box(
- "Mismatched PDB",
- "This PDB does not look like it matches your binary. Do you want to load it anyway?",
- MessageBoxButtonSet::YesNoButtonSet,
- binaryninja::interaction::MessageBoxIcon::QuestionIcon
- ) == MessageBoxButtonResult::NoButton {
- return Err(anyhow!("User cancelled mismatched load"));
- }
- }
- _ => {
- return Err(anyhow!("PDB GUID does not match"));
- }
- }
- }
- }
-
- // Microsoft's symbol server sometimes gives us a different version of the PDB
- // than what we ask for. It's weird, but if they're doing it, I trust it will work.
- if info.age != pdb_info.age {
- if info.age > pdb_info.age {
- // Have not seen this case, so I'm not sure if this is fatal
- info!("PDB age is older than our binary! Loading it anyway, but there may be missing information.");
- } else {
- info!("PDB age is newer than our binary! Loading it anyway, there probably shouldn't be any issues.");
- }
- }
-
- if did_download && settings.get_bool("pdb.files.localStoreCache", None, None) {
- match active_local_cache(Some(view)) {
- Ok(cache) => {
- let mut cab_path = PathBuf::from(&cache);
- cab_path.push(&info.file_name);
- cab_path.push(
- pdb_info
- .guid
- .as_ref()
- .iter()
- .map(|ch| format!("{:02X}", ch))
- .collect::<Vec<_>>()
- .join("")
- + &format!("{:X}", pdb_info.age),
- );
- let has_dir = if cab_path.is_dir() {
- true
- } else {
- match fs::create_dir_all(&cab_path) {
- Ok(_) => true,
- Err(e) => {
- error!("Could not create PDB cache dir: {}", e);
- false
- }
- }
- };
- if has_dir {
- cab_path.push(&info.file_name);
- match fs::write(&cab_path, &conts) {
- Ok(_) => {
- info!("Downloaded to: {}", cab_path.to_string_lossy());
- }
- Err(e) => error!("Could not write PDB to cache: {}", e),
- }
- }
-
- // Also write with the age we expect in our binary view
- if info.age < pdb_info.age {
- let mut cab_path = PathBuf::from(&cache);
- cab_path.push(&info.file_name);
- cab_path.push(
- pdb_info
- .guid
- .as_ref()
- .iter()
- .map(|ch| format!("{:02X}", ch))
- .collect::<Vec<_>>()
- .join("")
- + &format!("{:X}", info.age), // XXX: BV's pdb age
- );
- let has_dir = if cab_path.is_dir() {
- true
- } else {
- match fs::create_dir_all(&cab_path) {
- Ok(_) => true,
- Err(e) => {
- error!("Could not create PDB cache dir: {}", e);
- false
- }
- }
- };
- if has_dir {
- cab_path.push(&info.file_name);
- match fs::write(&cab_path, &conts) {
- Ok(_) => {
- info!("Downloaded to: {}", cab_path.to_string_lossy());
- }
- Err(e) => error!("Could not write PDB to cache: {}", e),
- }
- }
- }
- }
- Err(e) => error!("Could not get local cache for writing: {}", e),
- }
- }
- } else {
- if check_guid {
- return Err(anyhow!("File not compiled with PDB information"));
- } else {
- let ask = settings.get_string(
- "pdb.features.loadMismatchedPDB",
- Some(view),
- None,
- );
-
- match ask.as_str() {
- "true" => {},
- "ask" => {
- if interaction::show_message_box(
- "No PDB Information",
- "This file does not look like it was compiled with a PDB, so your PDB might not correctly apply to the analysis. Do you want to load it anyway?",
- MessageBoxButtonSet::YesNoButtonSet,
- binaryninja::interaction::MessageBoxIcon::QuestionIcon
- ) == MessageBoxButtonResult::NoButton {
- return Err(anyhow!("User cancelled missing info load"));
- }
- }
- _ => {
- return Err(anyhow!("File not compiled with PDB information"));
- }
- }
- }
- }
-
- let mut inst = match PDBParserInstance::new(debug_info, view, pdb) {
- Ok(inst) => {
- info!("Loaded PDB, parsing...");
- inst
- }
- Err(e) => {
- error!("Could not open PDB: {}", e);
- return Err(e);
- }
- };
- match inst.try_parse_info(Box::new(|cur, max| {
- (*progress)(cur, max).map_err(|_| anyhow!("Cancelled"))
- })) {
- Ok(()) => {
- info!("Parsed pdb");
- Ok(())
- }
- Err(e) => {
- error!("Could not parse PDB: {}", e);
- if e.to_string() == "Todo" {
- Ok(())
- } else {
- Err(e)
- }
- }
- }
- }
-}
-
-impl CustomDebugInfoParser for PDBParser {
- fn is_valid(&self, view: &BinaryView) -> bool {
- view.type_name().to_string() == "PE" || is_pdb(view)
- }
-
- fn parse_info(
- &self,
- debug_info: &mut DebugInfo,
- view: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- ) -> bool {
- if is_pdb(debug_file) {
- match self.load_from_file(
- &debug_file.read_vec(0, debug_file.len()),
- debug_info,
- view,
- &progress,
- false,
- false,
- ) {
- Ok(_) => return true,
- Err(e) if e.to_string() == "Cancelled" => return false,
- Err(_) => {
- error!("Chosen PDB file failed to load");
- return false;
- }
- }
- }
-
- // See if we can get pdb info from the view
- if let Some(info) = parse_pdb_info(view) {
- // First, check _NT_SYMBOL_PATH
- if let Ok(sym_path) = env::var("_NT_SYMBOL_PATH") {
- let stores = if let Ok(default_cache) = active_local_cache(Some(view)) {
- parse_sym_srv(&sym_path, &default_cache)
- } else {
- Err(anyhow!("No local cache found"))
- };
- if let Ok(stores) = stores {
- for store in stores {
- match search_sym_store(view, &store, &info) {
- Ok(Some(conts)) => {
- match self
- .load_from_file(&conts, debug_info, view, &progress, true, true)
- {
- Ok(_) => return true,
- Err(e) if e.to_string() == "Cancelled" => return false,
- Err(e) => debug!("Skipping, {}", e.to_string()),
- }
- }
- Ok(None) => {}
- e => error!("Error searching symbol store {}: {:?}", store, e),
- }
- }
- }
- }
-
- // Does the raw path just exist?
- if PathBuf::from(&info.path).exists() {
- match fs::read(&info.path) {
- Ok(conts) => match self
- .load_from_file(&conts, debug_info, view, &progress, true, false)
- {
- Ok(_) => return true,
- Err(e) if e.to_string() == "Cancelled" => return false,
- Err(e) => debug!("Skipping, {}", e.to_string()),
- },
- Err(e) if e.to_string() == "Cancelled" => return false,
- Err(e) => debug!("Could not read pdb: {}", e.to_string()),
- }
- }
-
- // Try in the same directory as the file
- let mut potential_path = PathBuf::from(view.file().filename().to_string());
- potential_path.pop();
- potential_path.push(&info.file_name);
- if potential_path.exists() {
- match fs::read(
- &potential_path
- .to_str()
- .expect("Potential path is a real string")
- .to_string(),
- ) {
- Ok(conts) => match self
- .load_from_file(&conts, debug_info, view, &progress, true, false)
- {
- Ok(_) => return true,
- Err(e) if e.to_string() == "Cancelled" => return false,
- Err(e) => debug!("Skipping, {}", e.to_string()),
- },
- Err(e) if e.to_string() == "Cancelled" => return false,
- Err(e) => debug!("Could not read pdb: {}", e.to_string()),
- }
- }
-
- // Check the local symbol store
- if let Ok(local_store_path) = active_local_cache(Some(view)) {
- match search_sym_store(view, &local_store_path, &info) {
- Ok(Some(conts)) => {
- match self.load_from_file(&conts, debug_info, view, &progress, true, false)
- {
- Ok(_) => return true,
- Err(e) if e.to_string() == "Cancelled" => return false,
- Err(e) => debug!("Skipping, {}", e.to_string()),
- }
- }
- Ok(None) => {}
- e => error!(
- "Error searching local symbol store {}: {:?}",
- local_store_path, e
- ),
- }
- }
-
- // Next, try downloading from all symbol servers in the server list
- let server_list =
- Settings::new("").get_string_list("pdb.files.symbolServerList", Some(view), None);
-
- for server in server_list.iter() {
- match search_sym_store(view, &server.to_string(), &info) {
- Ok(Some(conts)) => {
- match self.load_from_file(&conts, debug_info, view, &progress, true, true) {
- Ok(_) => return true,
- Err(e) if e.to_string() == "Cancelled" => return false,
- Err(e) => debug!("Skipping, {}", e.to_string()),
- }
- }
- Ok(None) => {}
- e => error!("Error searching remote symbol server {}: {:?}", server, e),
- }
- }
- }
- false
- }
-}
-
-#[cfg(not(feature = "demo"))]
-#[no_mangle]
-pub extern "C" fn CorePluginDependencies() {
- add_optional_plugin_dependency("view_pe");
-}
-
-#[cfg(not(feature = "demo"))]
-#[no_mangle]
-pub extern "C" fn CorePluginInit() -> bool {
- init_plugin()
-}
-
-#[cfg(feature = "demo")]
-#[no_mangle]
-pub extern "C" fn PDBPluginInit() -> bool {
- init_plugin()
-}
-
-fn init_plugin() -> bool {
- Logger::new("PDB").init();
- DebugInfoParser::register("PDB", PDBParser {});
-
- let settings = Settings::new("");
- settings.register_group("pdb", "PDB Loader");
- settings.register_setting_json(
- "pdb.files.localStoreAbsolute",
- r#"{
- "title" : "Local Symbol Store Absolute Path",
- "type" : "string",
- "default" : "",
- "aliases" : ["pdb.local-store-absolute", "pdb.localStoreAbsolute"],
- "description" : "Absolute path specifying where the PDB symbol store exists on this machine, overrides relative path.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.files.localStoreRelative",
- r#"{
- "title" : "Local Symbol Store Relative Path",
- "type" : "string",
- "default" : "symbols",
- "aliases" : ["pdb.local-store-relative", "pdb.localStoreRelative"],
- "description" : "Path *relative* to the binaryninja _user_ directory, specifying the pdb symbol store. If the Local Symbol Store Absolute Path is specified, this is ignored.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.files.localStoreCache",
- r#"{
- "title" : "Cache Downloaded PDBs in Local Store",
- "type" : "boolean",
- "default" : true,
- "aliases" : ["pdb.localStoreCache"],
- "description" : "Store PDBs downloaded from Symbol Servers in the local Symbol Store Path.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "network.pdbAutoDownload",
- r#"{
- "title" : "Enable Auto Downloading PDBs",
- "type" : "boolean",
- "default" : true,
- "aliases" : ["pdb.autoDownload", "pdb.auto-download-pdb"],
- "description" : "Automatically search for and download pdb files from specified symbol servers.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.files.symbolServerList",
- r#"{
- "title" : "Symbol Server List",
- "type" : "array",
- "sorted" : false,
- "default" : ["https://msdl.microsoft.com/download/symbols"],
- "aliases" : ["pdb.symbol-server-list", "pdb.symbolServerList"],
- "description" : "List of servers to query for pdb symbols.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.features.expandRTTIStructures",
- r#"{
- "title" : "Expand RTTI Structures",
- "type" : "boolean",
- "default" : true,
- "aliases" : ["pdb.expandRTTIStructures"],
- "description" : "Create structures for RTTI symbols with variable-sized names and arrays.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.features.generateVTables",
- r#"{
- "title" : "Generate Virtual Table Structures",
- "type" : "boolean",
- "default" : true,
- "aliases" : ["pdb.generateVTables"],
- "description" : "Create Virtual Table (VTable) structures for C++ classes found when parsing.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.features.loadGlobalSymbols",
- r#"{
- "title" : "Load Global Module Symbols",
- "type" : "boolean",
- "default" : true,
- "aliases" : ["pdb.loadGlobalSymbols"],
- "description" : "Load symbols in the Global module of the PDB. These symbols have generally lower quality types due to relying on the demangler.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.features.allowUnnamedVoidSymbols",
- r#"{
- "title" : "Allow Unnamed Untyped Symbols",
- "type" : "boolean",
- "default" : false,
- "aliases" : ["pdb.allowUnnamedVoidSymbols"],
- "description" : "Allow creation of symbols with no name and void types, often used as static local variables. Generally, these are just noisy and not relevant.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.features.allowVoidGlobals",
- r#"{
- "title" : "Allow Untyped Symbols",
- "type" : "boolean",
- "default" : true,
- "aliases" : ["pdb.allowVoidGlobals"],
- "description" : "Allow creation of symbols that have no type, and will be created as void-typed symbols. Generally, this happens in a stripped PDB when a Global symbol's mangled name does not contain type information.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.features.createMissingNamedTypes",
- r#"{
- "title" : "Create Missing Named Types",
- "type" : "boolean",
- "default" : true,
- "aliases" : ["pdb.createMissingNamedTypes"],
- "description" : "Allow creation of types named by function signatures which are not found in the PDB's types list or the Binary View. These types are usually found in stripped PDBs that have no type information but function signatures reference the stripped types.",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.features.loadMismatchedPDB",
- r#"{
- "title" : "Load Mismatched PDB",
- "type" : "string",
- "default" : "ask",
- "enum" : ["true", "ask", "false"],
- "enumDescriptions" : [
- "Always load the PDB",
- "Use the Interaction system to ask if the PDB should be loaded",
- "Never load the PDB"
- ],
- "aliases" : [],
- "description" : "If a manually loaded PDB has a mismatched GUID, should it be loaded?",
- "ignore" : []
- }"#,
- );
-
- settings.register_setting_json(
- "pdb.features.parseSymbols",
- r#"{
- "title" : "Parse PDB Symbols",
- "type" : "boolean",
- "default" : true,
- "aliases" : [],
- "description" : "Parse Symbol names and types. If you turn this off, you will only load Types.",
- "ignore" : []
- }"#,
- );
-
- true
-}
-
-#[test]
-fn test_default_cache_path() {
- println!("{:?}", default_local_cache());
-}
-
-#[test]
-fn test_sym_srv() {
- assert_eq!(
- parse_sym_srv(
- &r"srv*\\mybuilds\mysymbols".to_string(),
- &r"DEFAULT_STORE".to_string()
- )
- .expect("parse success")
- .collect::<Vec<_>>(),
- vec![r"\\mybuilds\mysymbols".to_string()]
- );
- assert_eq!(
- parse_sym_srv(
- &r"srv*c:\localsymbols*\\mybuilds\mysymbols".to_string(),
- &r"DEFAULT_STORE".to_string()
- )
- .expect("parse success")
- .collect::<Vec<_>>(),
- vec![
- r"c:\localsymbols".to_string(),
- r"\\mybuilds\mysymbols".to_string()
- ]
- );
- assert_eq!(
- parse_sym_srv(
- &r"srv**\\mybuilds\mysymbols".to_string(),
- &r"DEFAULT_STORE".to_string()
- )
- .expect("parse success")
- .collect::<Vec<_>>(),
- vec![
- r"DEFAULT_STORE".to_string(),
- r"\\mybuilds\mysymbols".to_string()
- ]
- );
- assert_eq!(
- parse_sym_srv(
- &r"srv*c:\localsymbols*\\NearbyServer\store*https://DistantServer".to_string(),
- &r"DEFAULT_STORE".to_string()
- )
- .expect("parse success")
- .collect::<Vec<_>>(),
- vec![
- r"c:\localsymbols".to_string(),
- r"\\NearbyServer\store".to_string(),
- r"https://DistantServer".to_string()
- ]
- );
- assert_eq!(
- parse_sym_srv(
- &r"srv*c:\DownstreamStore*https://msdl.microsoft.com/download/symbols".to_string(),
- &r"DEFAULT_STORE".to_string()
- )
- .expect("parse success")
- .collect::<Vec<_>>(),
- vec![
- r"c:\DownstreamStore".to_string(),
- r"https://msdl.microsoft.com/download/symbols".to_string()
- ]
- );
-}
diff --git a/rust/examples/pdb-ng/src/parser.rs b/rust/examples/pdb-ng/src/parser.rs
deleted file mode 100644
index 2d56a76d..00000000
--- a/rust/examples/pdb-ng/src/parser.rs
+++ /dev/null
@@ -1,508 +0,0 @@
-// Copyright 2022-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::{BTreeMap, HashMap, HashSet};
-use std::env;
-use std::fmt::Display;
-use std::sync::OnceLock;
-
-use anyhow::{anyhow, Result};
-use log::{debug, info};
-use pdb::*;
-
-use binaryninja::architecture::{Architecture, CoreArchitecture};
-use binaryninja::binaryview::{BinaryView, BinaryViewExt};
-use binaryninja::callingconvention::CallingConvention;
-use binaryninja::debuginfo::{DebugFunctionInfo, DebugInfo};
-use binaryninja::platform::Platform;
-use binaryninja::rc::Ref;
-use binaryninja::settings::Settings;
-use binaryninja::types::{
- min_confidence, Conf, DataVariableAndName, EnumerationBuilder, NamedTypeReference,
- NamedTypeReferenceClass, StructureBuilder, StructureType, Type, TypeClass,
-};
-
-use crate::symbol_parser::{ParsedDataSymbol, ParsedProcedure, ParsedSymbol};
-use crate::type_parser::ParsedType;
-
-/// Megastruct for all the parsing
-/// Certain fields are only used by specific files, as marked below.
-/// Why not make new structs for them? Because vvvv this garbage
-pub struct PDBParserInstance<'a, S: Source<'a> + 'a> {
- /// DebugInfo where types/functions will be stored eventually
- pub(crate) debug_info: &'a mut DebugInfo,
- /// Parent binary view (usually during BinaryView::Finalize)
- pub(crate) bv: &'a BinaryView,
- /// Default arch of self.bv
- pub(crate) arch: CoreArchitecture,
- /// Default calling convention for self.arch
- pub(crate) default_cc: Ref<CallingConvention<CoreArchitecture>>,
- /// Thiscall calling convention for self.bv, or default_cc if we can't find one
- pub(crate) thiscall_cc: Ref<CallingConvention<CoreArchitecture>>,
- /// Cdecl calling convention for self.bv, or default_cc if we can't find one
- pub(crate) cdecl_cc: Ref<CallingConvention<CoreArchitecture>>,
- /// Default platform of self.bv
- pub(crate) platform: Ref<Platform>,
- /// pdb-rs structure for making lifetime hell a real place
- pub(crate) pdb: PDB<'a, S>,
- /// pdb-rs Mapping of modules to addresses for resolving RVAs
- pub(crate) address_map: AddressMap<'a>,
- /// Binja Settings instance (for optimization)
- pub(crate) settings: Ref<Settings>,
-
- /// type_parser.rs
-
- /// TypeIndex -> ParsedType enum used during parsing
- pub(crate) indexed_types: BTreeMap<TypeIndex, ParsedType>,
- /// QName -> Binja Type for finished types
- pub(crate) named_types: BTreeMap<String, Ref<Type>>,
- /// Raw (mangled) name -> TypeIndex for resolving forward references
- pub(crate) full_type_indices: BTreeMap<String, TypeIndex>,
- /// Stack of types we're currently parsing
- pub(crate) type_stack: Vec<TypeIndex>,
- /// Stack of parent types we're parsing nested types inside of
- pub(crate) namespace_stack: Vec<String>,
- /// Type Index -> Does it return on the stack
- pub(crate) type_default_returnable: BTreeMap<TypeIndex, bool>,
-
- /// symbol_parser.rs
-
- /// List of fully parsed symbols from all modules
- pub(crate) parsed_symbols: Vec<ParsedSymbol>,
- /// Raw name -> index in parsed_symbols
- pub(crate) parsed_symbols_by_name: BTreeMap<String, usize>,
- /// Raw name -> Symbol index for looking up symbols for the currently parsing module (mostly for thunks)
- pub(crate) named_symbols: BTreeMap<String, SymbolIndex>,
- /// Parent -> Children symbol index tree for the currently parsing module
- pub(crate) symbol_tree: BTreeMap<SymbolIndex, Vec<SymbolIndex>>,
- /// Child -> Parent symbol index mapping, inverse of symbol_tree
- pub(crate) symbol_parents: BTreeMap<SymbolIndex, SymbolIndex>,
- /// Stack of (start, end) indices for the current symbols being parsed while constructing the tree
- pub(crate) symbol_stack: Vec<(SymbolIndex, SymbolIndex)>,
- /// Index -> parsed symbol for the currently parsing module
- pub(crate) indexed_symbols: BTreeMap<SymbolIndex, ParsedSymbol>,
- /// Symbol address -> Symbol for looking up by address
- pub(crate) addressed_symbols: BTreeMap<u64, Vec<ParsedSymbol>>,
- /// CPU type of the currently parsing module
- pub(crate) module_cpu_type: Option<CPUType>,
-}
-
-impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
- /// Try to create a new parser instance from a given bv/pdb
- pub fn new(
- debug_info: &'a mut DebugInfo,
- bv: &'a BinaryView,
- mut pdb: PDB<'a, S>,
- ) -> Result<Self> {
- let arch = if let Some(arch) = bv.default_arch() {
- arch
- } else {
- return Err(anyhow!("Cannot parse to view with no architecture"));
- };
-
- let platform = bv
- .default_platform()
- .expect("Expected bv to have a platform");
-
- let address_map = pdb.address_map()?;
-
- let default_cc = platform
- .get_default_calling_convention()
- .expect("Expected default calling convention");
-
- let thiscall_cc = Self::find_calling_convention(platform.as_ref(), "thiscall")
- .unwrap_or(default_cc.clone());
-
- let cdecl_cc = platform
- .get_cdecl_calling_convention()
- .unwrap_or(default_cc.clone());
-
- Ok(Self {
- debug_info,
- bv,
- arch,
- default_cc,
- thiscall_cc,
- cdecl_cc,
- platform,
- pdb,
- address_map,
- settings: Settings::new(""),
- indexed_types: Default::default(),
- named_types: Default::default(),
- full_type_indices: Default::default(),
- type_stack: Default::default(),
- namespace_stack: Default::default(),
- type_default_returnable: Default::default(),
- parsed_symbols: Default::default(),
- parsed_symbols_by_name: Default::default(),
- named_symbols: Default::default(),
- symbol_tree: Default::default(),
- symbol_parents: Default::default(),
- symbol_stack: Default::default(),
- indexed_symbols: Default::default(),
- addressed_symbols: Default::default(),
- module_cpu_type: None,
- })
- }
-
- /// Try to parse the pdb into the DebugInfo
- pub fn try_parse_info(
- &mut self,
- progress: Box<dyn Fn(usize, usize) -> Result<()> + 'a>,
- ) -> Result<()> {
- self.parse_types(Self::split_progress(&progress, 0, &[1.0, 3.0, 0.5, 0.5]))?;
- for (name, ty) in self.named_types.iter() {
- self.debug_info.add_type(name, ty.as_ref(), &[]); // TODO : Components
- }
-
- info!(
- "PDB found {} types (before resolving NTRs)",
- self.named_types.len()
- );
-
- if self
- .settings
- .get_bool("pdb.features.parseSymbols", Some(self.bv), None)
- {
- let (symbols, functions) =
- self.parse_symbols(Self::split_progress(&progress, 1, &[1.0, 3.0, 0.5, 0.5]))?;
-
- if self
- .settings
- .get_bool("pdb.features.createMissingNamedTypes", Some(self.bv), None)
- {
- self.resolve_missing_ntrs(
- &symbols,
- Self::split_progress(&progress, 2, &[1.0, 3.0, 0.5, 0.5]),
- )?;
- self.resolve_missing_ntrs(
- &functions,
- Self::split_progress(&progress, 3, &[1.0, 3.0, 0.5, 0.5]),
- )?;
- }
-
- info!("PDB found {} types", self.named_types.len());
- info!("PDB found {} data variables", symbols.len());
- info!("PDB found {} functions", functions.len());
-
- let allow_void =
- self.settings
- .get_bool("pdb.features.allowVoidGlobals", Some(self.bv), None);
-
- let min_confidence_type = Conf::new(Type::void(), min_confidence());
- for sym in symbols.iter() {
- match sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- address,
- name,
- type_,
- ..
- }) => {
- let real_type =
- type_.as_ref().unwrap_or(&min_confidence_type);
-
- if real_type.contents.type_class() == TypeClass::VoidTypeClass {
- if !allow_void {
- self.log(|| {
- format!("Not adding void-typed symbol {:?}@{:x}", name, address)
- });
- continue;
- }
- }
-
- self.log(|| {
- format!(
- "Adding data variable: 0x{:x}: {} {:?}",
- address, &name.raw_name, real_type
- )
- });
- self.debug_info
- .add_data_variable_info(DataVariableAndName::new(
- *address,
- real_type.clone(),
- true,
- name.full_name.as_ref().unwrap_or(&name.raw_name),
- ));
- }
- s => {
- self.log(|| format!("Not adding non-data symbol {:?}", s));
- }
- }
- }
-
- for sym in functions {
- match sym {
- ParsedSymbol::Procedure(ParsedProcedure {
- address,
- name,
- type_,
- locals: _,
- ..
- }) => {
- self.log(|| {
- format!(
- "Adding function: 0x{:x}: {} {:?}",
- address, &name.raw_name, type_
- )
- });
- self.debug_info.add_function(DebugFunctionInfo::new(
- Some(name.short_name.unwrap_or(name.raw_name.clone())),
- Some(name.full_name.unwrap_or(name.raw_name.clone())),
- Some(name.raw_name),
- type_.clone().and_then(|conf| {
- // TODO: When DebugInfo support confidence on function types, remove this
- if conf.confidence == 0 {
- None
- } else {
- Some(conf.contents)
- }
- }),
- Some(address),
- Some(self.platform.clone()),
- vec![], // TODO : Components
- vec![], //TODO: local variables
- ));
- }
- _ => {}
- }
- }
- }
-
- Ok(())
- }
-
- fn collect_name(
- &self,
- name: &NamedTypeReference,
- unknown_names: &mut HashMap<String, NamedTypeReferenceClass>,
- ) {
- let used_name = name.name().to_string();
- if let Some(&found) =
- unknown_names.get(&used_name)
- {
- if found != name.class() {
- // Interesting case, not sure we care
- self.log(|| {
- format!(
- "Mismatch unknown NTR class for {}: {} ?",
- &used_name,
- name.class() as u32
- )
- });
- }
- } else {
- self.log(|| format!("Found new unused name: {}", &used_name));
- unknown_names.insert(used_name, name.class());
- }
- }
-
- fn collect_names(
- &self,
- ty: &Type,
- unknown_names: &mut HashMap<String, NamedTypeReferenceClass>,
- ) {
- match ty.type_class() {
- TypeClass::StructureTypeClass => {
- if let Ok(structure) = ty.get_structure() {
- if let Ok(members) = structure.members() {
- for member in members {
- self.collect_names(member.ty.contents.as_ref(), unknown_names);
- }
- }
- if let Ok(bases) = structure.base_structures() {
- for base in bases {
- self.collect_name(base.ty.as_ref(), unknown_names);
- }
- }
- }
- }
- TypeClass::PointerTypeClass => {
- if let Ok(target) = ty.target() {
- self.collect_names(target.contents.as_ref(), unknown_names);
- }
- }
- TypeClass::ArrayTypeClass => {
- if let Ok(element_type) = ty.element_type() {
- self.collect_names(element_type.contents.as_ref(), unknown_names);
- }
- }
- TypeClass::FunctionTypeClass => {
- if let Ok(return_value) = ty.return_value() {
- self.collect_names(return_value.contents.as_ref(), unknown_names);
- }
- if let Ok(params) = ty.parameters() {
- for param in params {
- self.collect_names(param.t.contents.as_ref(), unknown_names);
- }
- }
- }
- TypeClass::NamedTypeReferenceClass => {
- if let Ok(ntr) = ty.get_named_type_reference() {
- self.collect_name(ntr.as_ref(), unknown_names);
- }
- }
- _ => {}
- }
- }
-
- fn resolve_missing_ntrs(
- &mut self,
- symbols: &Vec<ParsedSymbol>,
- progress: Box<dyn Fn(usize, usize) -> Result<()> + '_>,
- ) -> Result<()> {
- let mut unknown_names = HashMap::new();
- let mut known_names = self
- .bv
- .types()
- .iter()
- .map(|qnat| qnat.name().string())
- .collect::<HashSet<_>>();
-
- for ty in &self.named_types {
- known_names.insert(ty.0.clone());
- }
-
- let count = symbols.len();
- for (i, sym) in symbols.into_iter().enumerate() {
- match sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- type_: Some(type_), ..
- }) => {
- self.collect_names(type_.contents.as_ref(), &mut unknown_names);
- }
- ParsedSymbol::Procedure(ParsedProcedure {
- type_: Some(type_),
- locals,
- ..
- }) => {
- self.collect_names(type_.contents.as_ref(), &mut unknown_names);
- for l in locals {
- if let Some(ltype) = &l.type_ {
- self.collect_names(ltype.contents.as_ref(), &mut unknown_names);
- }
- }
- }
- _ => {}
- }
- (progress)(i, count)?;
- }
-
- for (name, class) in unknown_names.into_iter() {
- if known_names.contains(&name) {
- self.log(|| format!("Found referenced name and ignoring: {}", &name));
- continue;
- }
- self.log(|| format!("Adding referenced but unknown type {} (likely due to demangled name and stripped type)", &name));
- match class {
- NamedTypeReferenceClass::UnknownNamedTypeClass
- | NamedTypeReferenceClass::TypedefNamedTypeClass => {
- self.debug_info.add_type(name, Type::void().as_ref(), &[]); // TODO : Components
- }
- NamedTypeReferenceClass::ClassNamedTypeClass
- | NamedTypeReferenceClass::StructNamedTypeClass
- | NamedTypeReferenceClass::UnionNamedTypeClass => {
- let structure = StructureBuilder::new();
- match class {
- NamedTypeReferenceClass::ClassNamedTypeClass => {
- structure.set_structure_type(StructureType::ClassStructureType);
- }
- NamedTypeReferenceClass::StructNamedTypeClass => {
- structure.set_structure_type(StructureType::StructStructureType);
- }
- NamedTypeReferenceClass::UnionNamedTypeClass => {
- structure.set_structure_type(StructureType::UnionStructureType);
- }
- _ => {}
- }
- structure.set_width(1);
- structure.set_alignment(1);
-
- self.debug_info.add_type(
- name,
- Type::structure(structure.finalize().as_ref()).as_ref(),
- &[], // TODO : Components
- );
- }
- NamedTypeReferenceClass::EnumNamedTypeClass => {
- let enumeration = EnumerationBuilder::new();
- self.debug_info.add_type(
- name,
- Type::enumeration(
- enumeration.finalize().as_ref(),
- self.arch.default_integer_size(),
- false,
- )
- .as_ref(),
- &[], // TODO : Components
- );
- }
- }
- }
-
- Ok(())
- }
-
- /// Lazy logging function that prints like 20MB of messages
- pub(crate) fn log<F: FnOnce() -> D, D: Display>(&self, msg: F) {
- static MEM: OnceLock<bool> = OnceLock::new();
- let debug_pdb = MEM.get_or_init(|| {
- env::var("BN_DEBUG_PDB").is_ok()
- });
- if *debug_pdb {
- let space = "\t".repeat(self.type_stack.len()) + &"\t".repeat(self.symbol_stack.len());
- let msg = format!("{}", msg());
- debug!(
- "{}{}",
- space,
- msg.replace("\n", &*("\n".to_string() + &space))
- );
- }
- }
-
- pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<()> + 'b>(
- original_fn: F,
- subpart: usize,
- subpart_weights: &[f64],
- ) -> Box<dyn Fn(usize, usize) -> Result<()> + 'b> {
- // Normalize weights
- let weight_sum: f64 = subpart_weights.iter().sum();
- if weight_sum < 0.0001 {
- return Box::new(|_, _| Ok(()));
- }
-
- // Keep a running count of weights for the start
- let mut subpart_starts = vec![];
- let mut start = 0f64;
- for w in subpart_weights {
- subpart_starts.push(start);
- start += *w;
- }
-
- let subpart_start = subpart_starts[subpart] / weight_sum;
- let weight = subpart_weights[subpart] / weight_sum;
-
- Box::new(move |cur: usize, max: usize| {
- // Just use a large number for easy divisibility
- let steps = 1000000f64;
- let subpart_size = steps * weight;
- let subpart_progress = ((cur as f64) / (max as f64)) * subpart_size;
-
- original_fn(
- (subpart_start * steps + subpart_progress) as usize,
- steps as usize,
- )
- })
- }
-}
diff --git a/rust/examples/pdb-ng/src/struct_grouper.rs b/rust/examples/pdb-ng/src/struct_grouper.rs
deleted file mode 100644
index 042eb979..00000000
--- a/rust/examples/pdb-ng/src/struct_grouper.rs
+++ /dev/null
@@ -1,1164 +0,0 @@
-// Copyright 2022-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::cmp::Ordering;
-use std::env;
-use std::fmt::{Debug, Display, Formatter};
-
-use anyhow::{anyhow, Result};
-use log::{debug, warn};
-
-use binaryninja::types::{
- max_confidence, Conf, MemberAccess, MemberScope, StructureBuilder, StructureType, Type,
-};
-
-use crate::type_parser::ParsedMember;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-struct MemberSize {
- index: usize,
- offset: u64,
- width: u64,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-enum ResolvedGroup {
- Single(usize),
- Struct(u64, Vec<ResolvedGroup>),
- Union(u64, Vec<ResolvedGroup>),
-}
-
-#[derive(Clone, PartialEq, Eq)]
-struct WorkingStruct {
- index: Option<usize>,
- offset: u64,
- width: u64,
- is_union: bool,
- children: Vec<WorkingStruct>,
-}
-
-impl PartialOrd<Self> for WorkingStruct {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- if self.end() < other.start() {
- Some(Ordering::Less)
- } else if other.end() < self.start() {
- Some(Ordering::Greater)
- } else if self.is_same(other) {
- Some(Ordering::Equal)
- } else {
- None
- }
- }
-}
-
-impl Debug for WorkingStruct {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- if self.children.len() == 0 {
- write!(f, "{:X} -> {:X}", self.start(), self.end())?;
- if let Some(index) = self.index {
- write!(f, " (#{:X})", index)?;
- } else {
- write!(f, " without index???")?;
- }
- Ok(())
- } else if self.is_union {
- write!(f, "union {:X} -> {:X} ", self.start(), self.end())?;
- if let Some(index) = self.index {
- write!(f, "with index {:X} ??? ", index)?;
- }
- f.debug_list().entries(self.children.iter()).finish()
- } else {
- write!(f, "struct {:X} -> {:X} ", self.start(), self.end())?;
- if let Some(index) = self.index {
- write!(f, "with index {:X} ??? ", index)?;
- }
- f.debug_list().entries(self.children.iter()).finish()
- }
- }
-}
-
-impl WorkingStruct {
- pub fn start(&self) -> u64 {
- self.offset
- }
-
- pub fn end(&self) -> u64 {
- self.offset + self.width
- }
-
- pub fn extend_to(&mut self, new_end: u64) {
- if new_end > self.end() {
- self.width = new_end - self.offset;
- }
- }
-
- // pub fn overlaps(&self, other: &WorkingStruct) -> bool {
- // // If A starts after B ends
- // if self.start() >= other.end() {
- // return false;
- // }
- // // Or if B starts after A ends
- // if other.start() >= self.end() {
- // return false;
- // }
- // // Otherwise, one of the items starts before the other ends, so there is overlap
- // return true;
- // }
-
- // pub fn contains(&self, other: &WorkingStruct) -> bool {
- // // If other is fully contained within self
- // self.start() <= other.start() && self.end() >= other.end()
- // }
-
- pub fn is_same(&self, other: &WorkingStruct) -> bool {
- // If self and other have the same range
- self.start() == other.start() && self.end() == other.end()
- }
-
- pub fn insert(&mut self, other: WorkingStruct, recursion: usize) -> Result<()> {
- log(|| {
- format!("{}self: {:#?}", " ".repeat(recursion), self)
- .replace("\n", &*("\n".to_owned() + &" ".repeat(recursion)))
- });
- log(|| {
- format!("{}other: {:#?}", " ".repeat(recursion), other)
- .replace("\n", &*("\n".to_owned() + &" ".repeat(recursion)))
- });
-
- self.extend_to(other.end());
-
- // There are 2 cases we have to deal with here:
- // a. `other` starts after the end of the last group => insert `other` into the last group
- // b. `other` starts before the end of the last group => collect all the children inserted after it starts and put them into a struct
- // start a new struct with `other`
-
- if self.children.len() == 0 {
- self.children.push(other);
- return Ok(());
- }
-
- // This is really gross.
- // But also I need to ship this before I leave for France
- // TODO: Clean this up
-
- if other.start()
- >= self
- .children
- .last()
- .ok_or_else(|| anyhow!("Expected we have children #A"))?
- .end()
- {
- self.children.push(other);
- } else {
- // Create a structure with fields from self.children
- if self
- .children
- .last()
- .ok_or_else(|| anyhow!("Expected we have children #B"))?
- .index
- .is_none()
- && self
- .children
- .last()
- .ok_or_else(|| anyhow!("Expected we have children #C"))?
- .start()
- < other.start()
- {
- self.children
- .last_mut()
- .ok_or_else(|| anyhow!("Expected we have children #D"))?
- .insert(other, recursion + 1)?;
- return Ok(());
- }
-
- // If we're a union, we don't have to bother pushing a struct+union combo
- if self.is_union {
- self.children.push(WorkingStruct {
- index: None,
- offset: self.offset,
- width: self.width,
- is_union: false,
- children: vec![other],
- });
- return Ok(());
- }
-
- let mut start_index = None;
- for (i, child) in self.children.iter().enumerate() {
- if child.start() >= other.start() {
- start_index = Some(i);
- break;
- }
- }
- if start_index.is_none() {
- return Err(anyhow!(
- "Struct has overlapping member that cannot be resolved: {:#?}",
- other
- ));
- }
-
- let struct_start = self.children
- [start_index.ok_or_else(|| anyhow!("Expected we have start index"))?]
- .offset;
- let struct_end = self
- .children
- .last()
- .ok_or_else(|| anyhow!("Expected we have start index"))?
- .end()
- .max(other.end());
-
- let struct_children = self
- .children
- .drain(start_index.ok_or_else(|| anyhow!("Expected we have start index"))?..)
- .collect::<Vec<_>>();
- self.children.push(WorkingStruct {
- index: None,
- offset: struct_start,
- width: struct_end - struct_start,
- is_union: true,
- children: vec![
- WorkingStruct {
- index: None,
- offset: struct_start,
- width: struct_end - struct_start,
- is_union: false,
- children: struct_children,
- },
- WorkingStruct {
- index: None,
- offset: struct_start,
- width: struct_end - struct_start,
- is_union: false,
- children: vec![other],
- },
- ],
- });
-
- // union {
- // struct {
- // int data0;
- // int[2] data4;
- // int dataC;
- // };
- // struct {
- // int newdata0;
- // ...
- // };
- // };
- }
-
- // if other.start() < self.children[-1].end() {
- // take children from other.start() until -1 and put them into a struct
- // }
- // else {
- // add to self.children[-1], extend to fill
- // }
-
- Ok(())
- }
-
- pub fn to_resolved(mut self) -> ResolvedGroup {
- if let Some(index) = self.index {
- ResolvedGroup::Single(index)
- } else if self.is_union {
- if self.children.len() == 1 {
- self.children.remove(0).to_resolved()
- } else {
- // Collapse union of unions
- ResolvedGroup::Union(
- self.offset,
- self.children
- .into_iter()
- .flat_map(|child| match child.to_resolved() {
- ResolvedGroup::Union(offset, children) if offset == self.offset => {
- children
- }
- s => vec![s],
- })
- .collect(),
- )
- }
- } else {
- if self.children.len() == 1 {
- self.children.remove(0).to_resolved()
- } else {
- ResolvedGroup::Struct(
- self.offset,
- self.children
- .into_iter()
- .map(|child| child.to_resolved())
- .collect(),
- )
- }
- }
- }
-}
-
-pub fn group_structure(
- name: &String,
- members: &Vec<ParsedMember>,
- structure: &mut StructureBuilder,
-) -> Result<()> {
- // SO
- // PDBs handle trivial unions inside structures by just slamming all the fields together into
- // one big overlappy happy family. We need to reverse this and create out union structures
- // to properly represent the original source.
-
- // IN VISUAL FORM (if you are a visual person, like me):
- // struct {
- // int foos[2];
- // __offset(0):
- // int foo1;
- // int foo2;
- // int bar;
- // }
- //
- // Into
- //
- // struct {
- // union {
- // int foos[2];
- // struct {
- // int foo1;
- // int foo2;
- // }
- // }
- // int bar;
- // }
-
- // Into internal rep
- let reps = members
- .iter()
- .enumerate()
- .map(|(i, member)| MemberSize {
- index: i,
- offset: member.offset,
- width: member.ty.contents.width(),
- })
- .collect::<Vec<_>>();
-
- log(|| format!("{} {:#x?}", name, members));
- log(|| format!("{} {:#x?}", name, reps));
-
- // Group them
- match resolve_struct_groups(reps) {
- Ok(groups) => {
- log(|| format!("{} {:#x?}", name, groups));
-
- // Apply grouped members
- apply_groups(members, structure, groups, 0);
- }
- Err(e) => {
- warn!("{} Could not resolve structure groups: {}", name, e);
- for member in members {
- structure.insert(
- &member.ty.clone(),
- member.name.clone(),
- member.offset,
- false,
- member.access,
- member.scope,
- );
- }
- }
- }
-
- Ok(())
-}
-
-fn apply_groups(
- members: &Vec<ParsedMember>,
- structure: &mut StructureBuilder,
- groups: Vec<ResolvedGroup>,
- offset: u64,
-) {
- for (i, group) in groups.into_iter().enumerate() {
- match group {
- ResolvedGroup::Single(index) => {
- let member = &members[index];
-
- // TODO : Fix inner-offset being larger than `member.offset`
-
- if offset > member.offset {
- structure.insert(
- &member.ty.clone(),
- member.name.clone(),
- 0,
- false,
- member.access,
- member.scope,
- );
- } else {
- structure.insert(
- &member.ty.clone(),
- member.name.clone(),
- member.offset - offset,
- false,
- member.access,
- member.scope,
- );
- }
- }
- ResolvedGroup::Struct(inner_offset, children) => {
- let mut inner = StructureBuilder::new();
- apply_groups(members, &mut inner, children, inner_offset);
- structure.insert(
- &Conf::new(Type::structure(inner.finalize().as_ref()), max_confidence()),
- format!("__inner{}", i),
- inner_offset - offset,
- false,
- MemberAccess::PublicAccess,
- MemberScope::NoScope,
- );
- }
- ResolvedGroup::Union(inner_offset, children) => {
- let mut inner = StructureBuilder::new();
- inner.set_structure_type(StructureType::UnionStructureType);
- apply_groups(members, &mut inner, children, inner_offset);
- structure.insert(
- &Conf::new(Type::structure(inner.finalize().as_ref()), max_confidence()),
- format!("__inner{}", i),
- inner_offset - offset,
- false,
- MemberAccess::PublicAccess,
- MemberScope::NoScope,
- );
- }
- }
- }
-}
-
-fn resolve_struct_groups(members: Vec<MemberSize>) -> Result<Vec<ResolvedGroup>> {
- // See if we care
- let mut has_overlapping = false;
- let mut last_end = 0;
- let mut max_width = 0;
- for member in &members {
- if member.offset < last_end {
- has_overlapping = true;
- }
- last_end = member.offset + member.width;
- max_width = max_width.max(member.offset + member.width);
- }
-
- if !has_overlapping {
- // Nothing overlaps, just add em directly
- return Ok(members
- .into_iter()
- .map(|member| ResolvedGroup::Single(member.index))
- .collect());
- }
-
- // Yes overlapping
-
- let mut groups = WorkingStruct {
- index: None,
- offset: 0,
- width: max_width,
- is_union: false,
- children: vec![],
- };
- for &member in &members {
- let member_group = WorkingStruct {
- index: Some(member.index),
- offset: member.offset,
- width: member.width,
- is_union: false,
- children: vec![],
- };
- groups.insert(member_group, 0)?;
-
- log(|| format!("GROUPS: {:#x?}", groups));
- }
-
- Ok(groups
- .children
- .into_iter()
- .map(|child| child.to_resolved())
- .collect())
-}
-
-#[test]
-fn test_trivial() {
- assert_eq!(
- resolve_struct_groups(vec![
- MemberSize {
- index: 0,
- offset: 0,
- width: 1,
- },
- MemberSize {
- index: 1,
- offset: 1,
- width: 1,
- },
- MemberSize {
- index: 2,
- offset: 2,
- width: 1,
- },
- MemberSize {
- index: 3,
- offset: 3,
- width: 1,
- },
- ])
- .unwrap(),
- vec![
- ResolvedGroup::Single(0),
- ResolvedGroup::Single(1),
- ResolvedGroup::Single(2),
- ResolvedGroup::Single(3),
- ]
- );
-}
-
-#[test]
-fn test_everything_everywhere() {
- assert_eq!(
- resolve_struct_groups(vec![
- MemberSize {
- index: 0,
- offset: 0,
- width: 1,
- },
- MemberSize {
- index: 1,
- offset: 0,
- width: 1,
- },
- MemberSize {
- index: 2,
- offset: 0,
- width: 1,
- },
- MemberSize {
- index: 3,
- offset: 0,
- width: 1,
- },
- ])
- .unwrap(),
- vec![ResolvedGroup::Union(
- 0,
- vec![
- ResolvedGroup::Single(0),
- ResolvedGroup::Single(1),
- ResolvedGroup::Single(2),
- ResolvedGroup::Single(3),
- ]
- )]
- );
-}
-
-#[test]
-fn test_unalignend() {
- assert_eq!(
- resolve_struct_groups(vec![
- MemberSize {
- index: 0,
- offset: 0,
- width: 4,
- },
- MemberSize {
- index: 1,
- offset: 4,
- width: 8,
- },
- MemberSize {
- index: 2,
- offset: 12,
- width: 4,
- },
- MemberSize {
- index: 3,
- offset: 0,
- width: 8,
- },
- MemberSize {
- index: 4,
- offset: 8,
- width: 8,
- },
- ])
- .unwrap(),
- vec![ResolvedGroup::Union(
- 0,
- vec![
- ResolvedGroup::Struct(
- 0,
- vec![
- ResolvedGroup::Single(0),
- ResolvedGroup::Single(1),
- ResolvedGroup::Single(2),
- ]
- ),
- ResolvedGroup::Struct(0, vec![ResolvedGroup::Single(3), ResolvedGroup::Single(4),]),
- ]
- )]
- );
-}
-
-#[test]
-fn test_heap_vs_chunk_free_header() {
- assert_eq!(
- resolve_struct_groups(vec![
- MemberSize {
- index: 0,
- offset: 0,
- width: 16,
- },
- MemberSize {
- index: 1,
- offset: 0,
- width: 8,
- },
- MemberSize {
- index: 2,
- offset: 8,
- width: 24,
- },
- ])
- .unwrap(),
- vec![ResolvedGroup::Union(
- 0,
- vec![
- ResolvedGroup::Single(0),
- ResolvedGroup::Struct(0, vec![ResolvedGroup::Single(1), ResolvedGroup::Single(2)])
- ]
- )]
- );
-}
-
-#[test]
-fn test_kprcb() {
- assert_eq!(
- resolve_struct_groups(vec![
- MemberSize {
- index: 0,
- offset: 0,
- width: 8,
- },
- MemberSize {
- index: 1,
- offset: 8,
- width: 1,
- },
- MemberSize {
- index: 2,
- offset: 8,
- width: 1,
- },
- MemberSize {
- index: 3,
- offset: 9,
- width: 1,
- },
- MemberSize {
- index: 4,
- offset: 9,
- width: 1,
- },
- MemberSize {
- index: 5,
- offset: 10,
- width: 1,
- },
- MemberSize {
- index: 6,
- offset: 11,
- width: 1,
- },
- MemberSize {
- index: 7,
- offset: 12,
- width: 1,
- },
- MemberSize {
- index: 8,
- offset: 13,
- width: 1,
- },
- MemberSize {
- index: 9,
- offset: 14,
- width: 2,
- },
- MemberSize {
- index: 10,
- offset: 0,
- width: 16,
- },
- MemberSize {
- index: 11,
- offset: 16,
- width: 1,
- },
- MemberSize {
- index: 12,
- offset: 17,
- width: 1,
- },
- MemberSize {
- index: 13,
- offset: 18,
- width: 1,
- },
- MemberSize {
- index: 14,
- offset: 18,
- width: 1,
- },
- MemberSize {
- index: 15,
- offset: 19,
- width: 1,
- },
- MemberSize {
- index: 16,
- offset: 19,
- width: 1,
- },
- MemberSize {
- index: 17,
- offset: 20,
- width: 4,
- },
- MemberSize {
- index: 18,
- offset: 16,
- width: 8,
- },
- ])
- .unwrap(),
- vec![
- ResolvedGroup::Union(
- 0,
- vec![
- ResolvedGroup::Struct(
- 0,
- vec![
- ResolvedGroup::Single(0),
- ResolvedGroup::Union(
- 8,
- vec![ResolvedGroup::Single(1), ResolvedGroup::Single(2),]
- ),
- ResolvedGroup::Union(
- 9,
- vec![ResolvedGroup::Single(3), ResolvedGroup::Single(4),]
- ),
- ResolvedGroup::Single(5),
- ResolvedGroup::Single(6),
- ResolvedGroup::Single(7),
- ResolvedGroup::Single(8),
- ResolvedGroup::Single(9)
- ]
- ),
- ResolvedGroup::Single(10)
- ]
- ),
- ResolvedGroup::Union(
- 16,
- vec![
- ResolvedGroup::Struct(
- 16,
- vec![
- ResolvedGroup::Single(11),
- ResolvedGroup::Single(12),
- ResolvedGroup::Union(
- 18,
- vec![ResolvedGroup::Single(13), ResolvedGroup::Single(14),]
- ),
- ResolvedGroup::Union(
- 19,
- vec![ResolvedGroup::Single(15), ResolvedGroup::Single(16),]
- ),
- ResolvedGroup::Single(17)
- ]
- ),
- ResolvedGroup::Single(18)
- ]
- )
- ]
- );
-}
-
-#[test]
-fn test_dispatcher_header() {
- /*
- XXX: This returns a different grouping which is still valid
- Basically it turns this:
- struct {
- unsigned char data0;
- union {
- unsigned char data1;
- struct {
- unsigned char data1_2;
- unsigned char data2;
- unsigned char data3;
- };
- };
- };
-
- into this:
-
- struct {
- unsigned char data0;
- union {
- unsigned char data1;
- unsigned char data1_2;
- };
- unsigned char data2;
- unsigned char data3;
- };
- */
-
- assert_eq!(
- resolve_struct_groups(vec![
- MemberSize {
- index: 0x0,
- offset: 0x0,
- width: 0x4,
- },
- MemberSize {
- index: 0x1,
- offset: 0x0,
- width: 0x4,
- },
- MemberSize {
- index: 0x2,
- offset: 0x0,
- width: 0x1,
- },
- MemberSize {
- index: 0x3,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0x4,
- offset: 0x2,
- width: 0x1,
- },
- MemberSize {
- index: 0x5,
- offset: 0x3,
- width: 0x1,
- },
- MemberSize {
- index: 0x6,
- offset: 0x0,
- width: 0x1,
- },
- MemberSize {
- index: 0x7,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0x8,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0x9,
- offset: 0x2,
- width: 0x1,
- },
- MemberSize {
- index: 0xa,
- offset: 0x3,
- width: 0x1,
- },
- MemberSize {
- index: 0xb,
- offset: 0x3,
- width: 0x1,
- },
- MemberSize {
- index: 0xc,
- offset: 0x0,
- width: 0x1,
- },
- MemberSize {
- index: 0xd,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0xe,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0xf,
- offset: 0x2,
- width: 0x1,
- },
- MemberSize {
- index: 0x10,
- offset: 0x3,
- width: 0x1,
- },
- MemberSize {
- index: 0x11,
- offset: 0x0,
- width: 0x1,
- },
- MemberSize {
- index: 0x12,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0x13,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0x14,
- offset: 0x2,
- width: 0x1,
- },
- MemberSize {
- index: 0x15,
- offset: 0x3,
- width: 0x1,
- },
- MemberSize {
- index: 0x16,
- offset: 0x0,
- width: 0x1,
- },
- MemberSize {
- index: 0x17,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0x18,
- offset: 0x2,
- width: 0x1,
- },
- MemberSize {
- index: 0x19,
- offset: 0x2,
- width: 0x1,
- },
- MemberSize {
- index: 0x1a,
- offset: 0x3,
- width: 0x1,
- },
- MemberSize {
- index: 0x1b,
- offset: 0x3,
- width: 0x1,
- },
- MemberSize {
- index: 0x1c,
- offset: 0x0,
- width: 0x1,
- },
- MemberSize {
- index: 0x1d,
- offset: 0x1,
- width: 0x1,
- },
- MemberSize {
- index: 0x1e,
- offset: 0x2,
- width: 0x1,
- },
- MemberSize {
- index: 0x1f,
- offset: 0x3,
- width: 0x1,
- },
- MemberSize {
- index: 0x20,
- offset: 0x4,
- width: 0x4,
- },
- MemberSize {
- index: 0x21,
- offset: 0x8,
- width: 0x10,
- },
- ])
- .unwrap(),
- vec![
- ResolvedGroup::Union(
- 0,
- vec![
- ResolvedGroup::Single(0x0),
- ResolvedGroup::Single(0x1),
- ResolvedGroup::Struct(
- 0,
- vec![
- ResolvedGroup::Single(0x2),
- ResolvedGroup::Single(0x3),
- ResolvedGroup::Single(0x4),
- ResolvedGroup::Single(0x5),
- ]
- ),
- ResolvedGroup::Struct(
- 0,
- vec![
- ResolvedGroup::Single(0x6),
- ResolvedGroup::Union(
- 1,
- vec![
- ResolvedGroup::Single(0x7),
- ResolvedGroup::Struct(
- 1,
- vec![
- ResolvedGroup::Single(0x8),
- ResolvedGroup::Single(0x9),
- ResolvedGroup::Union(
- 3,
- vec![
- ResolvedGroup::Single(0xa),
- ResolvedGroup::Single(0xb),
- ]
- ),
- ]
- ),
- ]
- ),
- ]
- ),
- ResolvedGroup::Struct(
- 0,
- vec![
- ResolvedGroup::Single(0xc),
- ResolvedGroup::Union(
- 1,
- vec![
- ResolvedGroup::Single(0xd),
- ResolvedGroup::Struct(
- 1,
- vec![
- ResolvedGroup::Single(0xe),
- ResolvedGroup::Single(0xf),
- ResolvedGroup::Single(0x10),
- ]
- )
- ]
- ),
- ]
- ),
- ResolvedGroup::Struct(
- 0,
- vec![
- ResolvedGroup::Single(0x11),
- ResolvedGroup::Union(
- 1,
- vec![
- ResolvedGroup::Single(0x12),
- ResolvedGroup::Struct(
- 1,
- vec![
- ResolvedGroup::Single(0x13),
- ResolvedGroup::Single(0x14),
- ResolvedGroup::Single(0x15),
- ]
- )
- ]
- ),
- ]
- ),
- ResolvedGroup::Struct(
- 0,
- vec![
- ResolvedGroup::Single(0x16),
- ResolvedGroup::Single(0x17),
- ResolvedGroup::Union(
- 2,
- vec![
- ResolvedGroup::Single(0x18),
- ResolvedGroup::Struct(
- 2,
- vec![
- ResolvedGroup::Single(0x19),
- ResolvedGroup::Union(
- 2,
- vec![
- ResolvedGroup::Single(0x1a),
- ResolvedGroup::Single(0x1b),
- ]
- )
- ]
- )
- ]
- ),
- ]
- ),
- ResolvedGroup::Struct(
- 0,
- vec![
- ResolvedGroup::Single(0x1c),
- ResolvedGroup::Single(0x1d),
- ResolvedGroup::Single(0x1e),
- ResolvedGroup::Single(0x1f),
- ]
- ),
- ]
- ),
- ResolvedGroup::Single(0x20),
- ResolvedGroup::Single(0x21),
- ]
- )
-}
-
-#[test]
-fn test_bool_modifier() {
- assert_eq!(
- resolve_struct_groups(vec![
- MemberSize {
- index: 0,
- offset: 8,
- width: 1,
- },
- MemberSize {
- index: 1,
- offset: 12,
- width: 8,
- },
- MemberSize {
- index: 2,
- offset: 16,
- width: 1,
- },
- ])
- .unwrap_err()
- .to_string(),
- format!(
- "Struct has overlapping member that cannot be resolved: {:#?}",
- MemberSize {
- index: 2,
- offset: 16,
- width: 1,
- }
- )
- );
-}
-
-/// Whoops I'm not in PDBParserInstance
-fn log<F: FnOnce() -> D, D: Display>(msg: F) {
- // println!("{}", msg());
- if env::var("BN_DEBUG_PDB").is_ok() {
- debug!("{}", msg());
- }
-}
diff --git a/rust/examples/pdb-ng/src/symbol_parser.rs b/rust/examples/pdb-ng/src/symbol_parser.rs
deleted file mode 100644
index 7a90c4ab..00000000
--- a/rust/examples/pdb-ng/src/symbol_parser.rs
+++ /dev/null
@@ -1,2061 +0,0 @@
-// Copyright 2022-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::{BTreeMap, HashMap, HashSet};
-use std::mem;
-use std::sync::OnceLock;
-
-use anyhow::{anyhow, Result};
-use itertools::Itertools;
-use pdb::register::Register::{AMD64, X86};
-use pdb::register::{AMD64Register, X86Register};
-use pdb::Error::UnimplementedSymbolKind;
-use pdb::{
- AnnotationReferenceSymbol, BasePointerRelativeSymbol, BlockSymbol, BuildInfoSymbol,
- CallSiteInfoSymbol, CompileFlagsSymbol, ConstantSymbol, DataReferenceSymbol, DataSymbol,
- DefRangeFramePointerRelativeFullScopeSymbol, DefRangeFramePointerRelativeSymbol,
- DefRangeRegisterRelativeSymbol, DefRangeRegisterSymbol, DefRangeSubFieldRegisterSymbol,
- DefRangeSubFieldSymbol, DefRangeSymbol, ExportSymbol, FallibleIterator, FrameProcedureSymbol,
- InlineSiteSymbol, LabelSymbol, LocalSymbol, MultiRegisterVariableSymbol, ObjNameSymbol,
- ProcedureReferenceSymbol, ProcedureSymbol, PublicSymbol, RegisterRelativeSymbol,
- RegisterVariableSymbol, Rva, SeparatedCodeSymbol, Source, Symbol, SymbolData, SymbolIndex,
- SymbolIter, ThreadStorageSymbol, ThunkSymbol, TrampolineSymbol, TypeIndex,
- UserDefinedTypeSymbol, UsingNamespaceSymbol,
-};
-
-use binaryninja::architecture::{Architecture, ArchitectureExt, Register};
-use binaryninja::binaryview::BinaryViewBase;
-use binaryninja::demangle::demangle_ms;
-use binaryninja::rc::Ref;
-use binaryninja::types::{
- max_confidence, min_confidence, Conf, ConfMergable, FunctionParameter, QualifiedName,
- StructureBuilder, Type, TypeClass, Variable, VariableSourceType,
-};
-
-use crate::PDBParserInstance;
-
-const DEMANGLE_CONFIDENCE: u8 = 32;
-
-/// Parsed Data Symbol like globals, etc
-#[derive(Debug, Clone)]
-pub struct SymbolNames {
- pub raw_name: String,
- pub short_name: Option<String>,
- pub full_name: Option<String>,
-}
-
-/// Parsed Data Symbol like globals, etc
-#[derive(Debug, Clone)]
-pub struct ParsedDataSymbol {
- /// If the symbol comes from the public symbol list (lower quality)
- pub is_public: bool,
- /// Absolute address in bv
- pub address: u64,
- /// Symbol name
- pub name: SymbolNames,
- /// Type if known
- pub type_: Option<Conf<Ref<Type>>>,
-}
-
-/// Parsed functions and function-y symbols
-#[derive(Debug, Clone)]
-pub struct ParsedProcedure {
- /// If the symbol comes from the public symbol list (lower quality)
- pub is_public: bool,
- /// Absolute address in bv
- pub address: u64,
- /// Symbol name
- pub name: SymbolNames,
- /// Function type if known
- pub type_: Option<Conf<Ref<Type>>>,
- /// List of local variables (TODO: use these)
- pub locals: Vec<ParsedVariable>,
-}
-
-/// Structure with some information about a procedure
-#[derive(Debug, Clone)]
-pub struct ParsedProcedureInfo {
- /// Known parameters for the procedure
- pub params: Vec<ParsedVariable>,
- /// Known local variables for the procedure
- pub locals: Vec<ParsedVariable>,
-}
-
-/// One parsed variable / parameter
-#[derive(Debug, Clone)]
-pub struct ParsedVariable {
- /// Variable name
- pub name: String,
- /// Variable type if known
- pub type_: Option<Conf<Ref<Type>>>,
- /// Location(s) where the variable is stored. PDB lets you store a variable in multiple locations
- /// despite binja not really understanding that. Length is probably never zero
- pub storage: Vec<ParsedLocation>,
- /// Do we think this is a parameter
- pub is_param: bool,
-}
-
-#[derive(Debug, Copy, Clone)]
-pub struct ParsedLocation {
- /// Location information
- pub location: Variable,
- /// Is the storage location relative to the base pointer? See [ParsedProcedureInfo.frame_offset]
- pub base_relative: bool,
- /// Is the storage location relative to the stack pointer?
- pub stack_relative: bool,
-}
-
-/// Big enum of all the types of symbols we know how to parse
-#[derive(Debug, Clone)]
-pub enum ParsedSymbol {
- /// Parsed Data Symbol like globals, etc
- Data(ParsedDataSymbol),
- /// Parsed functions and function-y symbols
- Procedure(ParsedProcedure),
- /// Structure with some information about a procedure
- ProcedureInfo(ParsedProcedureInfo),
- /// One parsed variable / parameter
- LocalVariable(ParsedVariable),
- /// Location of a local variable
- Location(ParsedLocation),
-}
-
-/// This is all done in the parser instance namespace because the lifetimes are impossible to
-/// wrangle otherwise.
-impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
- pub fn parse_symbols(
- &mut self,
- progress: Box<dyn Fn(usize, usize) -> Result<()> + '_>,
- ) -> Result<(Vec<ParsedSymbol>, Vec<ParsedSymbol>)> {
- let mut module_count = 0usize;
- let dbg = self.pdb.debug_information()?;
- let mut modules = dbg.modules()?;
- while let Some(_module) = modules.next()? {
- module_count += 1;
- }
-
- let global_symbols = self.pdb.global_symbols()?;
- let symbols = global_symbols.iter();
- let parsed = self.parse_mod_symbols(symbols)?;
- for sym in parsed {
- match &sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- name: SymbolNames { raw_name, .. },
- ..
- })
- | ParsedSymbol::Procedure(ParsedProcedure {
- name: SymbolNames { raw_name, .. },
- ..
- }) => {
- self.parsed_symbols_by_name
- .insert(raw_name.clone(), self.parsed_symbols.len());
- }
- _ => {}
- }
- self.parsed_symbols.push(sym);
- }
-
- (progress)(1, module_count + 1)?;
-
- let dbg = self.pdb.debug_information()?;
- let mut modules = dbg.modules()?;
- let mut i = 0;
- while let Some(module) = modules.next()? {
- i += 1;
- (progress)(i + 1, module_count + 1)?;
-
- self.log(|| {
- format!(
- "Module {} {}",
- module.module_name(),
- module.object_file_name()
- )
- });
- if let Some(module_info) = self.pdb.module_info(&module)? {
- let symbols = module_info.symbols()?;
- let parsed = self.parse_mod_symbols(symbols)?;
- for sym in parsed {
- match &sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- name: SymbolNames { raw_name, .. },
- ..
- })
- | ParsedSymbol::Procedure(ParsedProcedure {
- name: SymbolNames { raw_name, .. },
- ..
- }) => {
- self.parsed_symbols_by_name
- .insert(raw_name.clone(), self.parsed_symbols.len());
- }
- _ => {}
- }
- self.parsed_symbols.push(sym);
- }
- }
- }
-
- let use_public =
- self.settings
- .get_bool("pdb.features.loadGlobalSymbols", Some(self.bv), None);
-
- let mut best_symbols = HashMap::<String, &ParsedSymbol>::new();
- for sym in &self.parsed_symbols {
- match sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- is_public,
- address,
- name:
- SymbolNames {
- raw_name,
- full_name,
- ..
- },
- type_,
- ..
- }) => {
- if *is_public && !use_public {
- continue;
- }
-
- let this_confidence = match type_ {
- Some(Conf { confidence, .. }) => *confidence,
- _ => min_confidence(),
- };
- let (new_better, old_exists) = match best_symbols.get(raw_name) {
- Some(ParsedSymbol::Data(ParsedDataSymbol {
- type_:
- Some(Conf {
- confidence: old_conf,
- ..
- }),
- ..
- })) => (this_confidence > *old_conf, true),
- Some(ParsedSymbol::Data(ParsedDataSymbol { type_: None, .. })) => {
- (true, true)
- }
- Some(..) => (false, true),
- _ => (true, false),
- };
- if new_better {
- self.log(|| {
- format!(
- "New best symbol (at 0x{:x}) for `{}` / `{}`: {:?}",
- *address,
- raw_name,
- full_name.as_ref().unwrap_or(raw_name),
- sym
- )
- });
- if old_exists {
- self.log(|| format!("Clobbering old definition"));
- }
- best_symbols.insert(raw_name.clone(), sym);
- }
- }
- _ => {}
- }
- }
-
- let mut best_functions = HashMap::<String, &ParsedSymbol>::new();
- for sym in &self.parsed_symbols {
- match sym {
- ParsedSymbol::Procedure(ParsedProcedure {
- is_public,
- address,
- name:
- SymbolNames {
- raw_name,
- full_name,
- ..
- },
- type_,
- ..
- }) => {
- if *is_public && !use_public {
- continue;
- }
-
- let this_confidence = match type_ {
- Some(Conf { confidence, .. }) => *confidence,
- _ => min_confidence(),
- };
- let (new_better, old_exists) = match best_functions.get(raw_name) {
- Some(ParsedSymbol::Procedure(ParsedProcedure {
- type_:
- Some(Conf {
- confidence: old_conf,
- ..
- }),
- ..
- })) => (this_confidence > *old_conf, true),
- Some(ParsedSymbol::Procedure(ParsedProcedure { type_: None, .. })) => {
- (true, true)
- }
- Some(..) => (false, true),
- _ => (true, false),
- };
- if new_better {
- self.log(|| {
- format!(
- "New best function (at 0x{:x}) for `{}` / `{}`: {:?}",
- *address,
- raw_name,
- full_name.as_ref().unwrap_or(raw_name),
- sym
- )
- });
- if old_exists {
- self.log(|| format!("Clobbering old definition"));
- }
- best_functions.insert(raw_name.clone(), sym);
- }
- }
- _ => {}
- }
- }
-
- Ok((
- best_symbols
- .into_iter()
- .map(|(_, sym)| sym.clone())
- .sorted_by_key(|sym| match sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- type_, is_public, ..
- }) => type_
- .as_ref()
- .map(|ty| {
- if *is_public {
- ty.confidence / 2
- } else {
- ty.confidence
- }
- })
- .unwrap_or(0),
- ParsedSymbol::Procedure(ParsedProcedure {
- type_, is_public, ..
- }) => type_
- .as_ref()
- .map(|ty| {
- if *is_public {
- ty.confidence / 2
- } else {
- ty.confidence
- }
- })
- .unwrap_or(0),
- _ => 0,
- })
- .collect::<Vec<_>>(),
- best_functions
- .into_iter()
- .map(|(_, func)| func.clone())
- .sorted_by_key(|sym| match sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- type_, is_public, ..
- }) => type_
- .as_ref()
- .map(|ty| {
- if *is_public {
- ty.confidence / 2
- } else {
- ty.confidence
- }
- })
- .unwrap_or(0),
- ParsedSymbol::Procedure(ParsedProcedure {
- type_, is_public, ..
- }) => type_
- .as_ref()
- .map(|ty| {
- if *is_public {
- ty.confidence / 2
- } else {
- ty.confidence
- }
- })
- .unwrap_or(0),
- _ => 0,
- })
- .collect::<Vec<_>>(),
- ))
- }
-
- /// Parse all the symbols in a module, via the given SymbolIter
- pub fn parse_mod_symbols(&mut self, mut symbols: SymbolIter) -> Result<Vec<ParsedSymbol>> {
- // Collect tree structure first
- let mut first = None;
- let mut last_local = None;
- let mut top_level_syms = vec![];
- let mut thunk_syms = vec![];
- let mut unparsed_syms = BTreeMap::new();
- while let Some(sym) = symbols.next()? {
- if first.is_none() {
- first = Some(sym.index());
- }
- unparsed_syms.insert(sym.index(), sym);
-
- let p = sym.parse();
- self.log(|| format!("Parsed: {:x?}", p));
-
- // It's some sort of weird tree structure where SOME symbols have "end" indices
- // and anything between them and that index is a child symbol
- // Sometimes there are "end scope" symbols at those end indices but like, sometimes
- // there aren't? Which makes that entire system seem pointless (or I'm just missing
- // something and it makes sense to _someone_)
- if let Some(&(start, _end)) = self.symbol_stack.last() {
- self.add_symbol_child(start, sym.index());
- } else {
- // Place thunk symbols in their own list at the end, so they can reference
- // other symbols parsed in the module
- match &p {
- Ok(SymbolData::Thunk(_)) => {
- thunk_syms.push(sym.index());
- }
- _ => {
- top_level_syms.push(sym.index());
- }
- }
- }
- let mut popped = false;
- while let Some(&(_start, end)) = self.symbol_stack.last() {
- if sym.index().0 >= end.0 {
- let _ = self.symbol_stack.pop();
- popped = true;
- } else {
- break;
- }
- }
-
- // These aren't actually used for parsing (I don't trust them) but we can include a little
- // debug error check here and see if it's ever actually wrong
- match p {
- Ok(SymbolData::ScopeEnd) | Ok(SymbolData::InlineSiteEnd) if popped => {}
- Ok(SymbolData::ScopeEnd) | Ok(SymbolData::InlineSiteEnd) if !popped => {
- self.log(|| format!("Did not pop at a scope end??? WTF??"));
- }
- _ if popped => {
- self.log(|| format!("Popped but not at a scope end??? WTF??"));
- }
- _ => {}
- }
-
- // Push new scopes on the stack to build the tree
- match p {
- Ok(SymbolData::Procedure(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::InlineSite(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::Block(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::Thunk(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::SeparatedCode(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::FrameProcedure(..)) => {
- if let Some(&(_, proc_end)) = self.symbol_stack.last() {
- self.symbol_stack.push((sym.index(), proc_end));
- }
- }
- Ok(SymbolData::Local(..)) => {
- last_local = Some(sym.index());
- }
- Ok(SymbolData::DefRange(..))
- | Ok(SymbolData::DefRangeSubField(..))
- | Ok(SymbolData::DefRangeRegister(..))
- | Ok(SymbolData::DefRangeFramePointerRelative(..))
- | Ok(SymbolData::DefRangeFramePointerRelativeFullScope(..))
- | Ok(SymbolData::DefRangeSubFieldRegister(..))
- | Ok(SymbolData::DefRangeRegisterRelative(..)) => {
- // I'd like to retract my previous statement that someone could possibly
- // understand this:
- // These symbol types impact the previous symbol, if it was a local
- // BUT ALSO!! PART III REVENGE OF THE SYM-TH: You can have more than one of
- // these and they all (?? it's undocumented) apply to the last local, PROBABLY
- if let Some(last) = last_local {
- self.add_symbol_child(last, sym.index());
- } else {
- self.log(|| format!("Found def range with no last local: {:?}", p));
- }
- }
- _ => {}
- }
- }
- assert!(self.symbol_stack.is_empty());
- // Add thunks at the end as per above
- top_level_syms.extend(thunk_syms.into_iter());
-
- // Restart and do the processing for real this time
- if let Some(first) = first {
- symbols.seek(first);
- }
-
- let mut final_symbols = HashSet::new();
-
- for root_idx in top_level_syms {
- for child_idx in self.walk_children(root_idx).into_iter() {
- let &sym = unparsed_syms
- .get(&child_idx)
- .expect("should have parsed this");
-
- self.log(|| format!("Symbol {:?} ", sym.index()));
- let (name, address) =
- if let Some(parsed) = self.handle_symbol_index(sym.index(), &sym)? {
- final_symbols.insert(sym.index());
- match parsed {
- ParsedSymbol::Data(ParsedDataSymbol { name, address, .. }) => {
- (Some(name.clone()), Some(*address))
- }
- ParsedSymbol::Procedure(ParsedProcedure { name, address, .. }) => {
- (Some(name.clone()), Some(*address))
- }
- _ => (None, None),
- }
- } else {
- (None, None)
- };
-
- if let Some(name) = name {
- self.named_symbols.insert(name.raw_name, sym.index());
- }
- if let Some(address) = address {
- if !self.addressed_symbols.contains_key(&address) {
- self.addressed_symbols.insert(address, vec![]);
- }
- self.addressed_symbols
- .get_mut(&address)
- .expect("just created this")
- .push(
- self.indexed_symbols
- .get(&sym.index())
- .ok_or_else(|| anyhow!("Can't find sym {} ?", sym.index()))?
- .clone(),
- );
- }
- }
- }
-
- let filtered_symbols = mem::replace(&mut self.indexed_symbols, BTreeMap::new())
- .into_iter()
- .filter_map(|(idx, sym)| {
- if final_symbols.contains(&idx) {
- Some(sym)
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
-
- // The symbols overlap between modules or something, so we can't keep this info around
- self.symbol_tree.clear();
- self.module_cpu_type = None;
-
- Ok(filtered_symbols)
- }
-
- /// Set a symbol to be the parent of another, building the symbol tree
- fn add_symbol_child(&mut self, parent: SymbolIndex, child: SymbolIndex) {
- if let Some(tree) = self.symbol_tree.get_mut(&parent) {
- tree.push(child);
- } else {
- self.symbol_tree.insert(parent, Vec::from([child]));
- }
-
- self.symbol_parents.insert(child, parent);
- }
-
- /// Postorder traversal of children of symbol index (only during this module parse)
- fn walk_children(&self, sym: SymbolIndex) -> Vec<SymbolIndex> {
- let mut children = vec![];
-
- if let Some(tree) = self.symbol_tree.get(&sym) {
- for &child in tree {
- children.extend(self.walk_children(child).into_iter());
- }
- }
-
- children.push(sym);
- return children;
- }
-
- /// Direct children of symbol index (only during this module parse)
- fn symbol_children(&self, sym: SymbolIndex) -> Vec<SymbolIndex> {
- if let Some(tree) = self.symbol_tree.get(&sym) {
- tree.clone()
- } else {
- vec![]
- }
- }
-
- /// Direct parent of symbol index (only during this module parse)
- #[allow(dead_code)]
- fn symbol_parent(&self, sym: SymbolIndex) -> Option<SymbolIndex> {
- self.symbol_parents.get(&sym).map(|idx| *idx)
- }
-
- /// Find symbol by index (only during this module parse)
- fn lookup_symbol(&self, sym: &SymbolIndex) -> Option<&ParsedSymbol> {
- self.indexed_symbols.get(sym)
- }
-
- /// Parse a new symbol by its index
- fn handle_symbol_index(
- &mut self,
- idx: SymbolIndex,
- sym: &Symbol,
- ) -> Result<Option<&ParsedSymbol>> {
- if let None = self.indexed_symbols.get(&idx) {
- match sym.parse() {
- Ok(data) => match self.handle_symbol(idx, &data) {
- Ok(Some(parsed)) => {
- self.log(|| format!("Symbol {} parsed into: {:?}", idx, parsed));
- self.indexed_symbols.insert(idx, parsed);
- }
- Ok(None) => {}
- e => {
- self.log(|| format!("Error parsing symbol {}: {:?}", idx, e));
- }
- },
- Err(UnimplementedSymbolKind(k)) => {
- self.log(|| format!("Not parsing unimplemented symbol {}: kind {:x?}", idx, k));
- }
- Err(e) => {
- self.log(|| format!("Could not parse symbol: {}: {}", idx, e));
- }
- };
- }
-
- Ok(self.indexed_symbols.get(&idx))
- }
-
- /// Parse a new symbol's data
- fn handle_symbol(
- &mut self,
- index: SymbolIndex,
- data: &SymbolData,
- ) -> Result<Option<ParsedSymbol>> {
- match data {
- SymbolData::ScopeEnd => self.handle_scope_end_symbol(index),
- SymbolData::ObjName(data) => self.handle_obj_name_symbol(index, &data),
- SymbolData::RegisterVariable(data) => {
- self.handle_register_variable_symbol(index, &data)
- }
- SymbolData::Constant(data) => self.handle_constant_symbol(index, &data),
- SymbolData::UserDefinedType(data) => self.handle_user_defined_type_symbol(index, &data),
- SymbolData::MultiRegisterVariable(data) => {
- self.handle_multi_register_variable_symbol(index, &data)
- }
- SymbolData::Data(data) => self.handle_data_symbol(index, &data),
- SymbolData::Public(data) => self.handle_public_symbol(index, &data),
- SymbolData::Procedure(data) => self.handle_procedure_symbol(index, &data),
- SymbolData::ThreadStorage(data) => self.handle_thread_storage_symbol(index, &data),
- SymbolData::CompileFlags(data) => self.handle_compile_flags_symbol(index, &data),
- SymbolData::UsingNamespace(data) => self.handle_using_namespace_symbol(index, &data),
- SymbolData::ProcedureReference(data) => {
- self.handle_procedure_reference_symbol(index, &data)
- }
- SymbolData::DataReference(data) => self.handle_data_reference_symbol(index, &data),
- SymbolData::AnnotationReference(data) => {
- self.handle_annotation_reference_symbol(index, &data)
- }
- SymbolData::Trampoline(data) => self.handle_trampoline_symbol(index, &data),
- SymbolData::Export(data) => self.handle_export_symbol(index, &data),
- SymbolData::Local(data) => self.handle_local_symbol(index, &data),
- SymbolData::BuildInfo(data) => self.handle_build_info_symbol(index, &data),
- SymbolData::InlineSite(data) => self.handle_inline_site_symbol(index, &data),
- SymbolData::InlineSiteEnd => self.handle_inline_site_end_symbol(index),
- SymbolData::ProcedureEnd => self.handle_procedure_end_symbol(index),
- SymbolData::Label(data) => self.handle_label_symbol(index, &data),
- SymbolData::Block(data) => self.handle_block_symbol(index, &data),
- SymbolData::RegisterRelative(data) => {
- self.handle_register_relative_symbol(index, &data)
- }
- SymbolData::Thunk(data) => self.handle_thunk_symbol(index, &data),
- SymbolData::SeparatedCode(data) => self.handle_separated_code_symbol(index, &data),
- SymbolData::DefRange(data) => self.handle_def_range(index, &data),
- SymbolData::DefRangeSubField(data) => self.handle_def_range_sub_field(index, &data),
- SymbolData::DefRangeRegister(data) => self.handle_def_range_register(index, &data),
- SymbolData::DefRangeFramePointerRelative(data) => {
- self.handle_def_range_frame_pointer_relative_symbol(index, &data)
- }
- SymbolData::DefRangeFramePointerRelativeFullScope(data) => {
- self.handle_def_range_frame_pointer_relative_full_scope_symbol(index, &data)
- }
- SymbolData::DefRangeSubFieldRegister(data) => {
- self.handle_def_range_sub_field_register_symbol(index, &data)
- }
- SymbolData::DefRangeRegisterRelative(data) => {
- self.handle_def_range_register_relative_symbol(index, &data)
- }
- SymbolData::BasePointerRelative(data) => {
- self.handle_base_pointer_relative_symbol(index, &data)
- }
- SymbolData::FrameProcedure(data) => self.handle_frame_procedure_symbol(index, &data),
- SymbolData::CallSiteInfo(data) => self.handle_call_site_info(index, &data),
- e => Err(anyhow!("Unhandled symbol type {:?}", e)),
- }
- }
-
- fn handle_scope_end_symbol(&mut self, _index: SymbolIndex) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ScopeEnd symbol"));
- Ok(None)
- }
-
- fn handle_obj_name_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ObjNameSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ObjName symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_register_variable_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &RegisterVariableSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got RegisterVariable symbol: {:?}", data));
-
- let storage = if let Some(reg) = self.convert_register(data.register) {
- vec![ParsedLocation {
- location: Variable {
- t: VariableSourceType::RegisterVariableSourceType,
- index: 0,
- storage: reg,
- },
- base_relative: false,
- stack_relative: false,
- }]
- } else {
- // TODO: What do we do here?
- vec![]
- };
-
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage,
- is_param: data.slot.map_or(true, |slot| slot > 0),
- })))
- }
-
- fn handle_constant_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ConstantSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Constant symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_user_defined_type_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &UserDefinedTypeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got UserDefinedType symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_multi_register_variable_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &MultiRegisterVariableSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got MultiRegisterVariable symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_data_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DataSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Data symbol: {:?}", data));
-
- let rva = data.offset.to_rva(&self.address_map).unwrap_or_default();
- let raw_name = data.name.to_string().to_string();
- let (t, name) = self.demangle_to_type(&raw_name, rva)?;
- let name = name.map(|n| n.string());
-
- // Sometimes the demangler REALLY knows what type this is supposed to be, and the
- // data symbol is actually wrong. So in those cases, let the demangler take precedence
- // Otherwise-- the demangler is usually wrong and clueless
- let data_type = t.merge(self.lookup_type_conf(&data.type_index, false)?);
-
- // Ignore symbols with no name and no type
- if !self
- .settings
- .get_bool("pdb.features.allowUnnamedVoidSymbols", Some(self.bv), None)
- && name.is_none()
- {
- if let Some(ty) = &data_type {
- if ty.contents.type_class() == TypeClass::VoidTypeClass {
- return Ok(None);
- }
- } else {
- return Ok(None);
- }
- }
-
- let name = SymbolNames {
- raw_name,
- short_name: name.clone(),
- full_name: name,
- };
-
- self.log(|| {
- format!(
- "DATA: 0x{:x}: {:?} {:?}",
- self.bv.start() + rva.0 as u64,
- &name,
- &data_type
- )
- });
-
- Ok(Some(ParsedSymbol::Data(ParsedDataSymbol {
- is_public: false,
- address: self.bv.start() + rva.0 as u64,
- name,
- type_: data_type,
- })))
- }
-
- fn handle_public_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &PublicSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Public symbol: {:?}", data));
- let rva = data.offset.to_rva(&self.address_map).unwrap_or_default();
- let raw_name = data.name.to_string().to_string();
- let (t, name) = self.demangle_to_type(&raw_name, rva)?;
- let name = name.map(|n| n.string());
-
- let name = SymbolNames {
- raw_name,
- short_name: name.clone(),
- full_name: name,
- };
-
- // These are generally low confidence because we only have the demangler to inform us of type
-
- if data.function {
- self.log(|| {
- format!(
- "PUBLIC FUNCTION: 0x{:x}: {:?} {:?}",
- self.bv.start() + rva.0 as u64,
- &name,
- t
- )
- });
-
- Ok(Some(ParsedSymbol::Procedure(ParsedProcedure {
- is_public: true,
- address: self.bv.start() + rva.0 as u64,
- name,
- type_: t,
- locals: vec![],
- })))
- } else {
- self.log(|| {
- format!(
- "PUBLIC DATA: 0x{:x}: {:?} {:?}",
- self.bv.start() + rva.0 as u64,
- &name,
- t
- )
- });
-
- Ok(Some(ParsedSymbol::Data(ParsedDataSymbol {
- is_public: true,
- address: self.bv.start() + rva.0 as u64,
- name,
- type_: t,
- })))
- }
- }
-
- /// Given a proc symbol index and guessed type (from demangler or tpi), find all the local variables
- /// and parameters related to that symbol.
- /// Returns Ok(Some((resolved params, locals))))
- fn lookup_locals(
- &self,
- index: SymbolIndex,
- type_index: TypeIndex,
- demangled_type: Option<Conf<Ref<Type>>>,
- ) -> Result<(Option<Conf<Ref<Type>>>, Vec<ParsedVariable>)> {
- // So generally speaking, here's the information we have:
- // - The function type is usually accurate wrt the parameter locations
- // - The parameter symbols have the names we want for the params
- // - The parameter symbols are a big ugly mess
- // We basically want to take the function type from the type, and just fill in the
- // names of all the parameters. Non-param locals don't really matter since binja
- // can't handle them anyway.
-
- // Type parameters order needs to be like this:
- // 1. `this` pointer (if exists)
- // 2. Various stack params
- // 3. Various register params
- // We assume that if a parameter is found in a register, that is where it is passed.
- // Otherwise they are in the default order as per the CC
-
- // Get child objects and search for local variable names
- let mut locals = vec![];
- let mut params = vec![];
- let mut known_frame = false;
- for child in self.symbol_children(index) {
- match self.lookup_symbol(&child) {
- Some(ParsedSymbol::ProcedureInfo(info)) => {
- params = info.params.clone();
- locals = info.locals.clone();
- known_frame = true;
- }
- _ => {}
- }
- }
-
- let raw_type = self.lookup_type_conf(&type_index, false)?;
- let fancy_type = self.lookup_type_conf(&type_index, true)?;
-
- // Best guess so far in case of error handling
- let fancier_type = fancy_type
- .clone()
- .merge(raw_type.clone())
- .merge(demangled_type.clone());
-
- if !known_frame {
- return Ok((fancier_type, vec![]));
- }
-
- // We need both of these to exist (not sure why they wouldn't)
- let (raw_type, fancy_type) = match (raw_type, fancy_type) {
- (Some(raw), Some(fancy)) => (raw, fancy),
- _ => return Ok((fancier_type, vec![])),
- };
-
- let raw_params = raw_type
- .contents
- .parameters()
- .map_err(|_| anyhow!("no params"))?;
- let mut fancy_params = fancy_type
- .contents
- .parameters()
- .map_err(|_| anyhow!("no params"))?;
-
- // Collect all the parameters we are expecting from the symbols
- let mut parsed_params = vec![];
- for p in &params {
- let param = FunctionParameter::new(
- p.type_.clone().merge(Conf::new(
- Type::int(self.arch.address_size(), false),
- min_confidence(),
- )),
- p.name.clone(),
- p.storage.get(0).map(|loc| loc.location.clone()),
- );
- // Ignore thisptr because it's not technically part of the raw type signature
- if p.name != "this" {
- parsed_params.push(param);
- }
- }
- let mut parsed_locals = vec![];
- for p in &locals {
- let param = FunctionParameter::new(
- p.type_.clone().merge(Conf::new(
- Type::int(self.arch.address_size(), false),
- min_confidence(),
- )),
- p.name.clone(),
- p.storage.get(0).map(|loc| loc.location.clone()),
- );
- // Ignore thisptr because it's not technically part of the raw type signature
- if p.name != "this" {
- parsed_locals.push(param);
- }
- }
-
- self.log(|| format!("Raw params: {:#x?}", raw_params));
- self.log(|| format!("Fancy params: {:#x?}", fancy_params));
- self.log(|| format!("Parsed params: {:#x?}", parsed_params));
-
- // We expect one parameter for each unnamed parameter in the marked up type
- let expected_param_count = fancy_params
- .iter()
- .filter(|p| p.name.as_str().is_empty())
- .count();
- // Sanity
- if expected_param_count != raw_params.len() {
- return Err(anyhow!(
- "Mismatched number of formal parameters and interpreted parameters"
- ));
- }
-
- // If we don't have enough parameters to fill the slots, there's a problem here
- // So just fallback to the unnamed params
- if expected_param_count > parsed_params.len() {
- // As per reversing of msdia140.dll (and nowhere else): if a function doesn't have
- // enough parameter variables declared as parameters, the remaining parameters are
- // the first however many locals. If you don't have enough of those, idk??
- if expected_param_count > (parsed_params.len() + parsed_locals.len()) {
- return Ok((fancier_type, vec![]));
- }
- parsed_params.extend(parsed_locals.into_iter());
- }
- let expected_parsed_params = parsed_params
- .drain(0..expected_param_count)
- .collect::<Vec<_>>();
-
- // For all formal parameters, apply names to them in fancy_params
- // These should be all types in fancy_params that are unnamed (named ones we inserted)
-
- let mut i = 0;
- for p in fancy_params.iter_mut() {
- if p.name.as_str().is_empty() {
- if p.t.contents != expected_parsed_params[i].t.contents {
- self.log(|| {
- format!(
- "Suspicious parameter {}: {:?} vs {:?}",
- i, p, expected_parsed_params[i]
- )
- });
- }
- if expected_parsed_params[i].name.as_str() == "__formal" {
- p.name = format!("__formal{}", i);
- } else {
- p.name = expected_parsed_params[i].name.clone();
- }
- i += 1;
- }
- }
-
- // Now apply the default location for the params from the cc
- let cc = fancy_type
- .contents
- .calling_convention()
- .map_or_else(|_| Conf::new(self.default_cc.clone(), 0), |cc| cc);
-
- self.log(|| {
- format!(
- "Type calling convention: {:?}",
- fancy_type.contents.calling_convention()
- )
- });
- self.log(|| format!("Default calling convention: {:?}", self.default_cc));
- self.log(|| format!("Result calling convention: {:?}", cc));
-
- let locations = cc.contents.variables_for_parameters(&fancy_params, None);
- for (p, new_location) in fancy_params.iter_mut().zip(locations.into_iter()) {
- p.location = Some(new_location);
- }
-
- self.log(|| format!("Final params: {:#x?}", fancy_params));
-
- // Use the new locals we've parsed to make the Real Definitely True function type
- let fancy_type = Conf::new(
- Type::function_with_options(
- &fancy_type
- .contents
- .return_value()
- .map_err(|_| anyhow!("no ret"))?,
- fancy_params.as_slice(),
- fancy_type.contents.has_variable_arguments().contents,
- &cc,
- fancy_type.contents.stack_adjustment(),
- ),
- max_confidence(),
- );
-
- let fancier_type = fancy_type
- .clone()
- .merge(raw_type.clone())
- .merge(demangled_type.clone());
-
- self.log(|| format!("Raw type: {:#x?}", raw_type));
- self.log(|| format!("Demangled type: {:#x?}", demangled_type));
- self.log(|| format!("Fancy type: {:#x?}", fancy_type));
- self.log(|| format!("Result type: {:#x?}", fancier_type));
-
- Ok((Some(fancier_type), vec![]))
- }
-
- fn handle_procedure_symbol(
- &mut self,
- index: SymbolIndex,
- data: &ProcedureSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Procedure symbol: {:?}", data));
-
- let rva = data.offset.to_rva(&self.address_map).unwrap_or_default();
- let address = self.bv.start() + rva.0 as u64;
-
- let mut raw_name = data.name.to_string().to_string();
-
- // Generally proc symbols have real types, but use the demangler just in case the microsoft
- // public pdbs have the function type as `void`
- let (t, name) = self.demangle_to_type(&raw_name, rva)?;
- let mut name = name.map(|n| n.string());
-
- // Some proc symbols don't have a mangled name, so try and look up their name
- if name.is_none() || name.as_ref().expect("just failed none") == &raw_name {
- // Lookup public symbol with the same name
- if let Some(others) = self.addressed_symbols.get(&address) {
- for o in others {
- match o {
- ParsedSymbol::Procedure(ParsedProcedure {
- name: proc_name, ..
- }) => {
- if proc_name.full_name.as_ref().unwrap_or(&proc_name.raw_name)
- == &raw_name
- {
- name = Some(raw_name);
- raw_name = proc_name.raw_name.clone();
- break;
- }
- }
- _ => {}
- }
- }
- }
- }
-
- let (fn_type, locals) = self.lookup_locals(index, data.type_index, t)?;
-
- let name = SymbolNames {
- raw_name,
- short_name: name.clone(),
- full_name: name,
- };
-
- self.log(|| format!("PROC: 0x{:x}: {:?} {:?}", address, &name, &fn_type));
-
- Ok(Some(ParsedSymbol::Procedure(ParsedProcedure {
- is_public: false,
- address,
- name,
- type_: fn_type,
- locals,
- })))
- }
-
- fn handle_thread_storage_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ThreadStorageSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ThreadStorage symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_compile_flags_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &CompileFlagsSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got CompileFlags symbol: {:?}", data));
- self.module_cpu_type = Some(data.cpu_type);
- Ok(None)
- }
-
- fn handle_using_namespace_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &UsingNamespaceSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got UsingNamespace symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_procedure_reference_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ProcedureReferenceSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ProcedureReference symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_data_reference_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DataReferenceSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DataReference symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_annotation_reference_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &AnnotationReferenceSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got AnnotationReference symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_trampoline_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &TrampolineSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Trampoline symbol: {:?}", data));
- let rva = data.thunk.to_rva(&self.address_map).unwrap_or_default();
- let target_rva = data.target.to_rva(&self.address_map).unwrap_or_default();
-
- let address = self.bv.start() + rva.0 as u64;
- let target_address = self.bv.start() + target_rva.0 as u64;
-
- let mut target_name = None;
- let mut thunk_name = None;
-
- let mut fn_type: Option<Conf<Ref<Type>>> = None;
-
- // These have the same name as their target, so look that up
- if let Some(syms) = self.addressed_symbols.get(&target_address) {
- // Take name from the public symbol
- for sym in syms {
- match sym {
- ParsedSymbol::Procedure(proc) if proc.is_public => {
- fn_type = proc.type_.clone().merge(fn_type);
- target_name = Some(proc.name.clone());
- }
- _ => {}
- }
- }
- // Take type from the non-public symbol if we have one
- for sym in syms {
- match sym {
- ParsedSymbol::Procedure(proc) if !proc.is_public => {
- fn_type = proc.type_.clone().merge(fn_type);
- if target_name.is_none() {
- target_name = Some(proc.name.clone());
- }
- }
- _ => {}
- }
- }
- }
-
- // And handle the fact that pdb public symbols for trampolines have the name of their target
- // ugh
- if let Some(syms) = self.addressed_symbols.get_mut(&address) {
- if let [ParsedSymbol::Procedure(proc)] = syms.as_mut_slice() {
- if let Some(tn) = &target_name {
- if proc.name.raw_name == tn.raw_name
- || proc.name.full_name.as_ref().unwrap_or(&proc.name.raw_name)
- == tn.full_name.as_ref().unwrap_or(&tn.raw_name)
- {
- // Yeah it's one of these symbols
- let old_name = proc.name.clone();
- let new_name = SymbolNames {
- raw_name: "j_".to_string() + &old_name.raw_name,
- short_name: old_name.short_name.as_ref().map(|n| "j_".to_string() + n),
- full_name: old_name.full_name.as_ref().map(|n| "j_".to_string() + n),
- };
-
- // I'm so sorry about this
- // XXX: Update the parsed public symbol's name to use j_ syntax
- if let Some(idx) = self.named_symbols.remove(&old_name.raw_name) {
- self.named_symbols.insert(new_name.raw_name.clone(), idx);
- }
- if let Some(idx) = self.parsed_symbols_by_name.remove(&old_name.raw_name) {
- self.parsed_symbols_by_name
- .insert(new_name.raw_name.clone(), idx);
- match &mut self.parsed_symbols[idx] {
- ParsedSymbol::Data(ParsedDataSymbol {
- name: parsed_name, ..
- })
- | ParsedSymbol::Procedure(ParsedProcedure {
- name: parsed_name,
- ..
- }) => {
- parsed_name.raw_name = new_name.raw_name.clone();
- parsed_name.short_name = new_name.short_name.clone();
- parsed_name.full_name = new_name.full_name.clone();
- }
- _ => {}
- }
- }
- proc.name = new_name.clone();
- thunk_name = Some(new_name);
- }
- }
- }
- }
-
- if thunk_name.is_none() {
- if let Some(tn) = target_name {
- thunk_name = Some(SymbolNames {
- raw_name: "j_".to_string() + &tn.raw_name,
- short_name: tn.short_name.as_ref().map(|n| "j_".to_string() + n),
- full_name: tn.full_name.as_ref().map(|n| "j_".to_string() + n),
- });
- }
- }
-
- let name = thunk_name.unwrap_or(SymbolNames {
- raw_name: format!("j_sub_{:x}", target_address),
- short_name: None,
- full_name: None,
- });
-
- self.log(|| format!("TRAMPOLINE: 0x{:x}: {:?} {:?}", address, &name, &fn_type));
-
- Ok(Some(ParsedSymbol::Procedure(ParsedProcedure {
- is_public: false,
- address,
- name,
- type_: fn_type,
- locals: vec![],
- })))
- }
-
- fn handle_export_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ExportSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Export symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_local_symbol(
- &mut self,
- index: SymbolIndex,
- data: &LocalSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Local symbol: {:?}", data));
- // Look for definition ranges for this symbol
- let mut locations = vec![];
- for child in self.symbol_children(index) {
- match self.lookup_symbol(&child) {
- Some(ParsedSymbol::Location(loc)) => {
- locations.push(loc.clone());
- }
- _ => {}
- }
- }
-
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage: locations,
- is_param: data.flags.isparam,
- })))
- }
-
- fn handle_build_info_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &BuildInfoSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got BuildInfo symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_inline_site_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &InlineSiteSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got InlineSite symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_inline_site_end_symbol(
- &mut self,
- _index: SymbolIndex,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got InlineSiteEnd symbol"));
- Ok(None)
- }
-
- fn handle_procedure_end_symbol(&mut self, _index: SymbolIndex) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ProcedureEnd symbol"));
- Ok(None)
- }
-
- fn handle_label_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &LabelSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Label symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_block_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &BlockSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Block symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_register_relative_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &RegisterRelativeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got RegisterRelative symbol: {:?}", data));
- match self.lookup_register(data.register) {
- Some(X86(X86Register::EBP)) | Some(AMD64(AMD64Register::RBP)) => {
- // Local is relative to base pointer
- // This is just another way of writing BasePointerRelativeSymbol
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage: vec![ParsedLocation {
- location: Variable {
- t: VariableSourceType::StackVariableSourceType,
- index: 0,
- storage: data.offset as i64,
- },
- base_relative: true, // !!
- stack_relative: false, // !!
- }],
- is_param: data.slot.map_or(false, |slot| slot > 0),
- })))
- }
- Some(X86(X86Register::ESP)) | Some(AMD64(AMD64Register::RSP)) => {
- // Local is relative to stack pointer
- // This is the same as base pointer case except not base relative (ofc)
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage: vec![ParsedLocation {
- location: Variable {
- t: VariableSourceType::StackVariableSourceType,
- index: 0,
- storage: data.offset as i64,
- },
- base_relative: false, // !!
- stack_relative: true, // !!
- }],
- is_param: data.slot.map_or(false, |slot| slot > 0),
- })))
- }
- _ => {
- // Local is relative to some non-bp register.
- // This is, of course, totally possible and normal
- // Binja just can't handle it in the slightest.
- // Soooooooo ????
- // TODO
- Ok(None)
- }
- }
- }
-
- fn handle_thunk_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ThunkSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Thunk symbol: {:?}", data));
- let rva = data.offset.to_rva(&self.address_map).unwrap_or_default();
- let raw_name = data.name.to_string().to_string();
- let address = self.bv.start() + rva.0 as u64;
-
- let (t, name) = self.demangle_to_type(&raw_name, rva)?;
- let name = name.map(|n| n.string());
- let mut fn_type = t;
-
- // These have the same name as their target, so look that up
- if let Some(&idx) = self.named_symbols.get(&raw_name) {
- if let Some(ParsedSymbol::Procedure(proc)) = self.indexed_symbols.get(&idx) {
- fn_type = proc.type_.clone().merge(fn_type);
- }
- }
-
- let mut thunk_name = None;
-
- // And handle the fact that pdb public symbols for thunks have the name of their target
- // ugh
- if let Some(syms) = self.addressed_symbols.get_mut(&address) {
- if let [ParsedSymbol::Procedure(proc)] = syms.as_mut_slice() {
- // Yeah it's one of these symbols
- // Make sure we don't do this twice (does that even happen?)
- if !proc.name.raw_name.starts_with("j_") {
- let old_name = proc.name.clone();
- let new_name = SymbolNames {
- raw_name: "j_".to_string() + &old_name.raw_name,
- short_name: Some(
- "j_".to_string() + old_name.short_name.as_ref().unwrap_or(&raw_name),
- ),
- full_name: Some(
- "j_".to_string() + old_name.full_name.as_ref().unwrap_or(&raw_name),
- ),
- };
-
- // I'm so sorry about this
- // XXX: Update the parsed public symbol's name to use j_ syntax
- if let Some(idx) = self.named_symbols.remove(&old_name.raw_name) {
- self.named_symbols.insert(new_name.raw_name.clone(), idx);
- }
- if let Some(idx) = self.parsed_symbols_by_name.remove(&old_name.raw_name) {
- self.parsed_symbols_by_name
- .insert(new_name.raw_name.clone(), idx);
- match &mut self.parsed_symbols[idx] {
- ParsedSymbol::Data(ParsedDataSymbol {
- name: parsed_name, ..
- })
- | ParsedSymbol::Procedure(ParsedProcedure {
- name: parsed_name, ..
- }) => {
- parsed_name.raw_name = new_name.raw_name.clone();
- parsed_name.short_name = new_name.short_name.clone();
- parsed_name.full_name = new_name.full_name.clone();
- }
- _ => {}
- }
- }
- proc.name = new_name.clone();
- thunk_name = Some(new_name);
- }
- }
- }
-
- let locals = vec![];
- let name = thunk_name.unwrap_or(SymbolNames {
- raw_name,
- short_name: name.clone(),
- full_name: name,
- });
-
- self.log(|| format!("THUNK: 0x{:x}: {:?} {:?}", address, &name, &fn_type));
-
- Ok(Some(ParsedSymbol::Procedure(ParsedProcedure {
- is_public: false,
- address: address,
- name,
- type_: fn_type,
- locals,
- })))
- }
-
- fn handle_separated_code_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &SeparatedCodeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got SeparatedCode symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRange symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range_sub_field(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeSubFieldSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeSubField symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range_register(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeRegisterSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeRegister symbol: {:?}", data));
- if let Some(reg) = self.convert_register(data.register) {
- Ok(Some(ParsedSymbol::Location(ParsedLocation {
- location: Variable {
- t: VariableSourceType::RegisterVariableSourceType,
- index: 0,
- storage: reg,
- },
- base_relative: false,
- stack_relative: false,
- })))
- } else {
- Ok(None)
- }
- }
-
- fn handle_def_range_frame_pointer_relative_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeFramePointerRelativeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeFramePointerRelative symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range_frame_pointer_relative_full_scope_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeFramePointerRelativeFullScopeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| {
- format!(
- "Got DefRangeFramePointerRelativeFullScope symbol: {:?}",
- data
- )
- });
- Ok(None)
- }
-
- fn handle_def_range_sub_field_register_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeSubFieldRegisterSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeSubFieldRegister symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range_register_relative_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeRegisterRelativeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeRegisterRelative symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_base_pointer_relative_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &BasePointerRelativeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got BasePointerRelative symbol: {:?}", data));
-
- // These are usually parameters if offset > 0
-
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage: vec![ParsedLocation {
- location: Variable {
- t: VariableSourceType::StackVariableSourceType,
- index: 0,
- storage: data.offset as i64,
- },
- base_relative: true,
- stack_relative: false,
- }],
- is_param: data.offset as i64 > 0 || data.slot.map_or(false, |slot| slot > 0),
- })))
- }
-
- fn handle_frame_procedure_symbol(
- &mut self,
- index: SymbolIndex,
- data: &FrameProcedureSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got FrameProcedure symbol: {:?}", data));
-
- // This symbol generally comes before a proc and all various parameters
- // It has a lot of information we don't care about, and some information we maybe do?
- // This function also tries to find all the locals and parameters of the procedure
-
- let mut params = vec![];
- let mut locals = vec![];
- let mut seen_offsets = HashSet::new();
-
- for child in self.symbol_children(index) {
- match self.lookup_symbol(&child) {
- Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name,
- type_,
- storage,
- is_param,
- ..
- })) => {
- let new_storage = storage.iter().map(|&var| var.location).collect::<Vec<_>>();
-
- // See if the parameter really is a parameter. Sometimes they don't say they are
- let mut really_is_param = *is_param;
- for loc in &new_storage {
- match loc {
- Variable {
- t: VariableSourceType::RegisterVariableSourceType,
- ..
- } => {
- // Assume register vars are always parameters
- really_is_param = true;
- }
- Variable {
- t: VariableSourceType::StackVariableSourceType,
- storage,
- ..
- } if *storage >= 0 => {
- // Sometimes you can get two locals at the same offset, both rbp+(x > 0)
- // I'm guessing from looking at dumps from dia2dump that only the first
- // one is considered a parameter, although there are times that I see
- // two params at the same offset and both are considered parameters...
- // This doesn't seem possible (or correct) because they would overlap
- // and only one would be useful anyway.
- // Regardless of the mess, Binja can only handle one parameter per slot
- // so we're just going to use the first one.
- really_is_param = seen_offsets.insert(*storage);
- }
- _ => {}
- }
- }
-
- if really_is_param {
- params.push(ParsedVariable {
- name: name.clone(),
- type_: type_.clone(),
- storage: new_storage
- .into_iter()
- .map(|loc| ParsedLocation {
- location: loc,
- // This has been handled now
- base_relative: false,
- stack_relative: false,
- })
- .collect(),
- is_param: really_is_param,
- });
- } else {
- locals.push(ParsedVariable {
- name: name.clone(),
- type_: type_.clone(),
- storage: new_storage
- .into_iter()
- .map(|loc| ParsedLocation {
- location: loc,
- // This has been handled now
- base_relative: false,
- stack_relative: false,
- })
- .collect(),
- is_param: really_is_param,
- });
- }
- }
- Some(ParsedSymbol::Data(_)) => {
- // Apparently you can have static data symbols as parameters
- // Because of course you can
- }
- None => {}
- e => self.log(|| format!("Unexpected symbol type in frame: {:?}", e)),
- }
- }
-
- Ok(Some(ParsedSymbol::ProcedureInfo(ParsedProcedureInfo {
- params,
- locals,
- })))
- }
-
- fn handle_call_site_info(
- &mut self,
- _index: SymbolIndex,
- data: &CallSiteInfoSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got CallSiteInfo symbol: {:?}", data));
- Ok(None)
- }
-
- /// Demangle a name and get a type out
- /// Also fixes void(void) and __s_RTTI_Nonsense
- fn demangle_to_type(
- &self,
- raw_name: &String,
- rva: Rva,
- ) -> Result<(Option<Conf<Ref<Type>>>, Option<QualifiedName>)> {
- let (mut t, mut name) = match demangle_ms(&self.arch, raw_name, true) {
- Ok((Some(t), name)) => (Some(Conf::new(t, DEMANGLE_CONFIDENCE)), name),
- Ok((_, name)) => (None, name),
- _ => (None, vec![raw_name.clone()]),
- };
-
- if let Some(ty) = t.as_ref() {
- if ty.contents.type_class() == TypeClass::FunctionTypeClass {
- // demangler makes (void) into (void arg1) which is wrong
- let parameters = ty
- .contents
- .parameters()
- .map_err(|_| anyhow!("no parameters"))?;
- if let [p] = parameters.as_slice() {
- if p.t.contents.type_class() == TypeClass::VoidTypeClass {
- t = Some(Conf::new(
- Type::function::<_>(
- &ty.contents
- .return_value()
- .map_err(|_| anyhow!("no return value"))?,
- &[],
- ty.contents.has_variable_arguments().contents,
- ),
- ty.confidence,
- ))
- }
- }
- }
- }
-
- // These have types but they aren't actually set anywhere. So it's the demangler's
- // job to take care of them, apparently?
- static MEM: OnceLock<Vec<(String, Vec<String>)>> = OnceLock::new();
- let name_to_type = MEM.get_or_init(|| {
- vec![
- (
- "`RTTI Complete Object Locator'".to_string(),
- vec![
- "_s_RTTICompleteObjectLocator".to_string(),
- "_s__RTTICompleteObjectLocator".to_string(),
- "_s__RTTICompleteObjectLocator2".to_string(),
- ],
- ),
- (
- "`RTTI Class Hierarchy Descriptor'".to_string(),
- vec![
- "_s_RTTIClassHierarchyDescriptor".to_string(),
- "_s__RTTIClassHierarchyDescriptor".to_string(),
- "_s__RTTIClassHierarchyDescriptor2".to_string(),
- ],
- ),
- (
- // TODO: This type is dynamic
- "`RTTI Base Class Array'".to_string(),
- vec![
- "_s_RTTIBaseClassArray".to_string(),
- "_s__RTTIBaseClassArray".to_string(),
- "_s__RTTIBaseClassArray2".to_string(),
- ],
- ),
- (
- "`RTTI Base Class Descriptor at (".to_string(),
- vec![
- "_s_RTTIBaseClassDescriptor".to_string(),
- "_s__RTTIBaseClassDescriptor".to_string(),
- "_s__RTTICBaseClassDescriptor2".to_string(),
- ],
- ),
- (
- "`RTTI Type Descriptor'".to_string(),
- vec!["_TypeDescriptor".to_string()],
- ),
- ]
- });
-
- if let Some(last_name) = name.last() {
- for (search_name, search_types) in name_to_type.iter() {
- if last_name.contains(search_name) {
- for search_type in search_types {
- if let Some(ty) = self.named_types.get(search_type) {
- // Fallback in case we don't find a specific one
- t = Some(Conf::new(
- Type::named_type_from_type(search_type, ty.as_ref()),
- DEMANGLE_CONFIDENCE,
- ));
-
- if self.settings.get_bool(
- "pdb.features.expandRTTIStructures",
- Some(self.bv),
- None,
- ) {
- if let Some((lengthy_type, length)) =
- self.make_lengthy_type(ty, self.bv.start() + rva.0 as u64)?
- {
- // See if we have a type with this length
- let lengthy_name =
- format!("${}$_extraBytes_{}", search_type, length);
-
- if let Some(ty) = self.named_types.get(&lengthy_name) {
- // Wow!
- t = Some(Conf::new(
- Type::named_type_from_type(lengthy_name, ty.as_ref()),
- DEMANGLE_CONFIDENCE,
- ));
- } else {
- t = Some(Conf::new(lengthy_type, DEMANGLE_CONFIDENCE));
- }
- }
- }
- }
- }
- }
- }
- }
-
- // VTables have types on their data symbols,
- if let Some((class_name, last)) = name.join("::").rsplit_once("::") {
- if last.contains("`vftable'") {
- let mut vt_name = class_name.to_string() + "::" + "VTable";
- if last.contains("{for") {
- // DerivedClass::`vftable'{for `BaseClass'}
- let mut base_name = last.to_owned();
- base_name.drain(0..("`vftable'{for `".len()));
- base_name.drain((base_name.len() - "'}".len())..(base_name.len()));
- // Multiply inherited classes have multiple vtable types
- // TODO: Do that
- vt_name = base_name + "::" + "VTable";
- }
-
- vt_name = vt_name
- .replace("class ", "")
- .replace("struct ", "")
- .replace("enum ", "");
-
- if let Some(ty) = self.named_types.get(&vt_name) {
- t = Some(Conf::new(
- Type::named_type_from_type(&vt_name, ty.as_ref()),
- DEMANGLE_CONFIDENCE,
- ));
- } else {
- // Sometimes the demangler has trouble with `class Foo` in templates
- vt_name = vt_name
- .replace("class ", "")
- .replace("struct ", "")
- .replace("enum ", "");
-
- if let Some(ty) = self.named_types.get(&vt_name) {
- t = Some(Conf::new(
- Type::named_type_from_type(&vt_name, ty.as_ref()),
- DEMANGLE_CONFIDENCE,
- ));
- } else {
- t = Some(Conf::new(
- Type::named_type_from_type(
- &vt_name,
- Type::structure(StructureBuilder::new().finalize().as_ref())
- .as_ref(),
- ),
- DEMANGLE_CONFIDENCE,
- ));
- }
- }
- }
- }
-
- if let Some(last_name) = name.last_mut() {
- if last_name.starts_with("__imp_") {
- last_name.drain(0..("__imp_".len()));
- }
- }
-
- let name = if name.len() == 1 && &name[0] == raw_name && raw_name.starts_with('?') {
- None
- } else if name.len() == 1 && name[0] == "" {
- None
- } else if name.len() > 0 && name[0].starts_with("\x7f") {
- // Not sure why these exist but they do Weird Stuff
- name[0].drain(0..1);
- Some(QualifiedName::from(name))
- } else {
- Some(QualifiedName::from(name))
- };
-
- Ok((t, name))
- }
-
- fn make_lengthy_type(
- &self,
- base_type: &Ref<Type>,
- base_address: u64,
- ) -> Result<Option<(Ref<Type>, usize)>> {
- if base_type.type_class() != TypeClass::StructureTypeClass {
- return Ok(None);
- }
- let structure = base_type
- .get_structure()
- .map_err(|_| anyhow!("Expected structure"))?;
- let mut members = structure
- .members()
- .map_err(|_| anyhow!("Expected structure to have members"))?;
- let last_member = members
- .last_mut()
- .ok_or_else(|| anyhow!("Not enough members"))?;
-
- if last_member.ty.contents.type_class() != TypeClass::ArrayTypeClass {
- return Ok(None);
- }
- if last_member.ty.contents.count() != 0 {
- return Ok(None);
- }
-
- let member_element = last_member
- .ty
- .contents
- .element_type()
- .map_err(|_| anyhow!("Last member has no type"))?
- .contents;
- let member_width = member_element.width();
-
- // Read member_width bytes from bv starting at that member, until we read all zeroes
- let member_address = base_address + last_member.offset;
-
- let mut bytes = Vec::<u8>::new();
- bytes.resize(member_width as usize, 0);
-
- let mut element_count = 0;
- while self.bv.read(
- bytes.as_mut_slice(),
- member_address + member_width * element_count,
- ) == member_width as usize
- {
- if bytes.iter().all(|&b| b == 0) {
- break;
- }
- element_count += 1;
- }
-
- // Make a new copy of the type with the correct element count
- last_member.ty.contents = Type::array(member_element.as_ref(), element_count);
-
- Ok(Some((
- Type::structure(StructureBuilder::from(members).finalize().as_ref()),
- element_count as usize,
- )))
- }
-
- /// Sorry about the type names
- /// Given a pdb::Register (u32), get a pdb::register::Register (big enum with names)
- fn lookup_register(&self, reg: pdb::Register) -> Option<pdb::register::Register> {
- if let Some(cpu) = self.module_cpu_type {
- pdb::register::Register::new(reg, cpu).ok()
- } else {
- None
- }
- }
-
- /// Convert a pdb::Register (u32) to a binja register index for the current arch
- fn convert_register(&self, reg: pdb::Register) -> Option<i64> {
- match self.lookup_register(reg) {
- Some(X86(xreg)) => {
- self.log(|| format!("Register {:?} ==> {:?}", reg, xreg));
- self.arch
- .register_by_name(xreg.to_string().to_lowercase())
- .map(|reg| reg.id() as i64)
- }
- Some(AMD64(areg)) => {
- self.log(|| format!("Register {:?} ==> {:?}", reg, areg));
- self.arch
- .register_by_name(areg.to_string().to_lowercase())
- .map(|reg| reg.id() as i64)
- }
- // TODO: Other arches
- _ => None,
- }
- }
-}
diff --git a/rust/examples/pdb-ng/src/type_parser.rs b/rust/examples/pdb-ng/src/type_parser.rs
deleted file mode 100644
index caf732b2..00000000
--- a/rust/examples/pdb-ng/src/type_parser.rs
+++ /dev/null
@@ -1,2477 +0,0 @@
-// Copyright 2022-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::HashMap;
-use std::sync::OnceLock;
-
-use anyhow::{anyhow, Result};
-use binaryninja::architecture::{Architecture, CoreArchitecture};
-use binaryninja::binaryview::BinaryViewExt;
-use binaryninja::callingconvention::CallingConvention;
-use binaryninja::platform::Platform;
-use binaryninja::rc::Ref;
-use binaryninja::types::{
- max_confidence, BaseStructure, Conf, EnumerationBuilder, EnumerationMember, FunctionParameter,
- MemberAccess, MemberScope, NamedTypeReference, NamedTypeReferenceClass, QualifiedName,
- StructureBuilder, StructureMember, StructureType, Type, TypeBuilder, TypeClass,
-};
-use log::warn;
-use pdb::Error::UnimplementedTypeKind;
-use pdb::{
- ArgumentList, ArrayType, BaseClassType, BitfieldType, ClassKind, ClassType, EnumerateType,
- EnumerationType, FallibleIterator, FieldAttributes, FieldList, FunctionAttributes, Indirection,
- ItemFinder, MemberFunctionType, MemberType, MethodList, MethodType, ModifierType, NestedType,
- OverloadedMethodType, PointerMode, PointerType, PrimitiveKind, PrimitiveType, ProcedureType,
- Source, StaticMemberType, TypeData, TypeIndex, UnionType, Variant, VirtualBaseClassType,
- VirtualFunctionTablePointerType, VirtualFunctionTableType, VirtualTableShapeType,
-};
-use regex::Regex;
-
-use crate::struct_grouper::group_structure;
-use crate::PDBParserInstance;
-
-static BUILTIN_NAMES: &[&'static str] = &[
- "size_t",
- "ssize_t",
- "ptrdiff_t",
- "wchar_t",
- "wchar16",
- "wchar32",
- "bool",
-];
-// const VOID_RETURN_CONFIDENCE: u8 = 16;
-
-/// Function types
-#[derive(Debug, Clone)]
-pub struct ParsedProcedureType {
- /// Interpreted type of the method, with thisptr, __return, etc
- pub method_type: Ref<Type>,
- /// Base method type right outta the pdb with no frills
- pub raw_method_type: Ref<Type>,
-}
-
-/// Bitfield member type, if we ever get around to implementing these
-#[derive(Debug, Clone)]
-pub struct ParsedBitfieldType {
- /// Size in bits
- pub size: u64,
- /// Bit offset in the current bitfield set
- pub position: u64,
- /// Underlying type of the whole bitfield set
- pub ty: Ref<Type>,
-}
-
-/// Parsed member of a class/structure, basically just binaryninja::StructureMember but with bitfields :(
-#[derive(Debug, Clone)]
-pub struct ParsedMember {
- /// Member type
- pub ty: Conf<Ref<Type>>,
- /// Member name
- pub name: String,
- /// Offset in structure
- pub offset: u64,
- /// Access flags
- pub access: MemberAccess,
- /// Scope doesn't really mean anything in binja
- pub scope: MemberScope,
- /// Bitfield size, if this is in a bitfield. Mainly you should just be checking for Some()
- pub bitfield_size: Option<u64>,
- /// Bit offset, if this is in a bitfield. Mainly you should just be checking for Some()
- pub bitfield_position: Option<u64>,
-}
-
-/// Parsed named method of a class
-#[derive(Debug, Clone)]
-pub struct ParsedMethod {
- /// Attributes from pdb-rs
- pub attributes: FieldAttributes,
- /// Name of method
- pub name: String,
- /// Type of the method + class info
- pub method_type: ParsedMemberFunction,
- /// Offset in class's virtual table, if virtual
- pub vtable_offset: Option<usize>,
-}
-
-/// One entry in a list of parsed methods? This is just here so overloaded methods have a struct to use
-#[derive(Debug, Clone)]
-pub struct ParsedMethodListEntry {
- /// Attributes from pdb-rs
- pub attributes: FieldAttributes,
- /// Type of the method + class info
- pub method_type: ParsedMemberFunction,
- /// Offset in class's virtual table, if virtual
- pub vtable_offset: Option<usize>,
-}
-
-/// Parsed member function type info
-#[derive(Debug, Clone)]
-pub struct ParsedMemberFunction {
- /// Attributes from pdb-rs
- pub attributes: FunctionAttributes,
- /// Parent class's name
- pub class_name: String,
- /// Interpreted type of the method, with thisptr, __return, etc
- pub method_type: Ref<Type>,
- /// Base method type right outta the pdb with no frills
- pub raw_method_type: Ref<Type>,
- /// Type of thisptr object, if relevant
- pub this_pointer_type: Option<Ref<Type>>,
- /// Adjust to thisptr at start, for virtual bases or something
- pub this_adjustment: usize,
-}
-
-/// Virtual base class, c++ nightmare fuel
-#[derive(Debug, Clone)]
-pub struct VirtualBaseClass {
- /// Base class name
- pub base_name: String,
- /// Base class type
- pub base_type: Ref<Type>,
- /// Offset in this class where the base's fields are located
- pub base_offset: u64,
- /// Type of vbtable, probably
- pub base_table_type: Ref<Type>,
- /// Offset of this base in the vbtable
- pub base_table_offset: u64,
-}
-
-/// Mega enum of all the different types of types we can parse
-#[derive(Debug, Clone)]
-pub enum ParsedType {
- /// No info other than type data
- Bare(Ref<Type>),
- /// Named fully parsed class/enum/union/etc type
- Named(String, Ref<Type>),
- /// Function procedure
- Procedure(ParsedProcedureType),
- /// Bitfield entries
- BitfieldType(ParsedBitfieldType),
- /// A list of members for a structure / union
- FieldList(Vec<ParsedType>),
- /// One member in a structure/union
- Member(ParsedMember),
- /// Base class name and offset details
- BaseClass(String, StructureMember),
- /// One member in an enumeration
- Enumerate(EnumerationMember),
- /// List of arguments to a function
- ArgumentList(Vec<FunctionParameter>),
- /// Parsed member function type info
- MemberFunction(ParsedMemberFunction),
- /// Parsed named method of a class
- Method(ParsedMethod),
- /// List of all the methods in a class
- MethodList(Vec<ParsedMethodListEntry>),
- /// (Name, Overloads) equivalent to ParsedMethod
- OverloadedMethod(String, Vec<ParsedMethodListEntry>),
- /// Virtual table shape straight outta pdb-rs
- VTableShape(Vec<u8>),
- /// Also virtual table shape, but you want a pointer this time
- VTablePointer(Vec<u8>),
- /// Virtual base class, c++ nightmare fuel
- VBaseClass(VirtualBaseClass),
-}
-
-#[allow(non_camel_case_types)]
-#[derive(Debug)]
-pub enum CV_call_t {
- NEAR_C = 1,
- FAR_C = 2,
- NEAR_PASCAL = 3,
- FAR_PASCAL = 4,
- NEAR_FAST = 5,
- FAR_FAST = 6,
- SKIPPED = 7,
- NEAR_STD = 8,
- FAR_STD = 9,
- NEAR_SYS = 10,
- FAR_SYS = 11,
- THISCALL = 12,
- MIPSCALL = 13,
- GENERIC = 14,
- ALPHACALL = 15,
- PPCCALL = 16,
- SHCALL = 17,
- ARMCALL = 18,
- AM33CALL = 19,
- TRICALL = 20,
- SH5CALL = 21,
- M32RCALL = 22,
- ALWAYS_INLINED = 23,
- NEAR_VECTOR = 24,
- RESERVED = 25,
-}
-
-impl TryFrom<u8> for CV_call_t {
- type Error = anyhow::Error;
-
- fn try_from(value: u8) -> Result<Self> {
- match value {
- 0 => Err(anyhow!("Empty calling convention")),
- 1 => Ok(Self::NEAR_C),
- 2 => Ok(Self::FAR_C),
- 3 => Ok(Self::NEAR_PASCAL),
- 4 => Ok(Self::FAR_PASCAL),
- 5 => Ok(Self::NEAR_FAST),
- 6 => Ok(Self::FAR_FAST),
- 7 => Ok(Self::SKIPPED),
- 8 => Ok(Self::NEAR_STD),
- 9 => Ok(Self::FAR_STD),
- 10 => Ok(Self::NEAR_SYS),
- 11 => Ok(Self::FAR_SYS),
- 12 => Ok(Self::THISCALL),
- 13 => Ok(Self::MIPSCALL),
- 14 => Ok(Self::GENERIC),
- 15 => Ok(Self::ALPHACALL),
- 16 => Ok(Self::PPCCALL),
- 17 => Ok(Self::SHCALL),
- 18 => Ok(Self::ARMCALL),
- 19 => Ok(Self::AM33CALL),
- 20 => Ok(Self::TRICALL),
- 21 => Ok(Self::SH5CALL),
- 22 => Ok(Self::M32RCALL),
- 23 => Ok(Self::ALWAYS_INLINED),
- 24 => Ok(Self::NEAR_VECTOR),
- 25 => Ok(Self::RESERVED),
- e => Err(anyhow!("Unknown CV_call_t convention {}", e)),
- }
- }
-}
-
-/// This is all done in the parser instance namespace because the lifetimes are impossible to
-/// wrangle otherwise.
-impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
- /// Parse all the types in a pdb
- pub fn parse_types(
- &mut self,
- progress: Box<dyn Fn(usize, usize) -> Result<()> + '_>,
- ) -> Result<()> {
- // Hack: This is needed for primitive types but it's not defined in the pdb itself
- self.named_types
- .insert("HRESULT".to_string(), Type::int(4, true));
-
- let type_information = self.pdb.type_information()?;
- let mut finder = type_information.finder();
-
- let mut type_count = 0;
-
- // Do an initial pass on the types to find the full indexes for named types
- // In case something like an array needs to reference them before they're fully defined
- let mut prepass_types = type_information.iter();
- while let Some(ty) = prepass_types.next()? {
- type_count += 1;
- finder.update(&prepass_types);
- match ty.parse() {
- Ok(TypeData::Class(data)) => {
- if !data.properties.forward_reference() {
- self.full_type_indices.insert(
- data.unique_name
- .unwrap_or(data.name)
- .to_string()
- .to_string(),
- ty.index(),
- );
- }
- }
- Ok(TypeData::Enumeration(data)) => {
- if !data.properties.forward_reference() {
- self.full_type_indices.insert(
- data.unique_name
- .unwrap_or(data.name)
- .to_string()
- .to_string(),
- ty.index(),
- );
- }
- }
- Ok(TypeData::Union(data)) => {
- if !data.properties.forward_reference() {
- self.full_type_indices.insert(
- data.unique_name
- .unwrap_or(data.name)
- .to_string()
- .to_string(),
- ty.index(),
- );
- }
- }
- _ => {}
- }
- }
-
- self.log(|| format!("Now parsing named types"));
-
- // Parse the types we care about, so that recursion gives us parent relationships for free
- let mut types = type_information.iter();
- let mut i = 0;
- while let Some(ty) = types.next()? {
- i += 1;
- (progress)(i, type_count * 2)?;
-
- match ty.parse() {
- Ok(TypeData::Class(_)) | Ok(TypeData::Enumeration(_)) | Ok(TypeData::Union(_)) => {
- self.handle_type_index(ty.index(), &mut finder)?;
- }
- _ => {}
- }
-
- assert!(self.namespace_stack.is_empty());
- assert!(self.type_stack.is_empty());
- }
-
- self.log(|| format!("Now parsing unused floating types"));
-
- // Parse the rest because symbols often use them
- let mut postpass_types = type_information.iter();
- while let Some(ty) = postpass_types.next()? {
- i += 1;
- (progress)(i, type_count * 2)?;
-
- self.handle_type_index(ty.index(), &mut finder)?;
- }
-
- self.log(|| format!("Now adding all unreferenced named types"));
- // Any referenced named types that are only forward-declared will cause missing type references,
- // so create empty types for those here.
- for (_, parsed) in &self.indexed_types {
- match parsed {
- ParsedType::Bare(ty) if ty.type_class() == TypeClass::NamedTypeReferenceClass => {
- // See if we have this type
- let name = ty
- .get_named_type_reference()
- .map_err(|_| anyhow!("expected ntr"))?
- .name()
- .to_string();
- if Self::is_name_anonymous(&name) {
- continue;
- }
- if self.named_types.contains_key(&name) {
- continue;
- }
- // If the bv has this type, DebugInfo will just update us to reference it
- if let Some(_) = self.bv.get_type_by_name(&name) {
- continue;
- }
-
- self.log(|| format!("Got undefined but referenced named type: {}", &name));
- let type_class = ty
- .get_named_type_reference()
- .map_err(|_| anyhow!("expected ntr"))?
- .class();
-
- let bare_type = match type_class {
- NamedTypeReferenceClass::ClassNamedTypeClass => Type::structure(
- StructureBuilder::new()
- .set_structure_type(StructureType::ClassStructureType)
- .finalize()
- .as_ref(),
- ),
- // Missing typedefs are just going to become structures
- NamedTypeReferenceClass::UnknownNamedTypeClass
- | NamedTypeReferenceClass::TypedefNamedTypeClass
- | NamedTypeReferenceClass::StructNamedTypeClass => {
- Type::structure(StructureBuilder::new().finalize().as_ref())
- }
- NamedTypeReferenceClass::UnionNamedTypeClass => Type::structure(
- StructureBuilder::new()
- .set_structure_type(StructureType::UnionStructureType)
- .finalize()
- .as_ref(),
- ),
- NamedTypeReferenceClass::EnumNamedTypeClass => Type::enumeration(
- EnumerationBuilder::new().finalize().as_ref(),
- self.arch.default_integer_size(),
- false,
- ),
- };
-
- self.log(|| format!("Bare type created: {} {}", &name, &bare_type));
- self.named_types.insert(name, bare_type);
- }
- _ => {}
- }
- }
-
- // Cleanup a couple builtin names
- for &name in BUILTIN_NAMES {
- if self.named_types.contains_key(name) {
- self.named_types.remove(name);
- self.log(|| format!("Remove builtin type {}", name));
- }
- }
-
- static MEM: OnceLock<Regex> = OnceLock::new();
- let uint_regex = MEM.get_or_init(|| {
- Regex::new(r"u?int\d+_t").unwrap()
- });
-
- let float_regex = MEM.get_or_init(|| {
- Regex::new(r"float\d+").unwrap()
- });
-
- let mut remove_names = vec![];
- for (name, _) in &self.named_types {
- if uint_regex.is_match(name) {
- remove_names.push(name.clone());
- }
- if float_regex.is_match(name) {
- remove_names.push(name.clone());
- }
- }
- for name in remove_names {
- self.named_types.remove(&name);
- self.log(|| format!("Remove builtin type {}", &name));
- }
-
- Ok(())
- }
-
- /// Lookup a type in the parsed types by its index (ie for a procedure)
- pub(crate) fn lookup_type(
- &self,
- index: &TypeIndex,
- fancy_procs: bool,
- ) -> Result<Option<Ref<Type>>> {
- match self.indexed_types.get(index) {
- Some(ParsedType::Bare(ty)) => Ok(Some(ty.clone())),
- Some(ParsedType::Named(name, ty)) => Ok(Some(Type::named_type_from_type(name, &ty))),
- Some(ParsedType::Procedure(ParsedProcedureType {
- method_type,
- raw_method_type,
- })) => {
- if fancy_procs {
- Ok(Some(method_type.clone()))
- } else {
- Ok(Some(raw_method_type.clone()))
- }
- }
- Some(ParsedType::MemberFunction(ParsedMemberFunction {
- method_type,
- raw_method_type,
- ..
- })) => {
- if fancy_procs {
- Ok(Some(method_type.clone()))
- } else {
- Ok(Some(raw_method_type.clone()))
- }
- }
- Some(ParsedType::Member(ParsedMember { ty, .. })) => Ok(Some(ty.contents.clone())),
- _ => Ok(None),
- }
- }
-
- /// Lookup a type in the parsed types and get a confidence value for it too
- pub(crate) fn lookup_type_conf(
- &self,
- index: &TypeIndex,
- fancy_procs: bool,
- ) -> Result<Option<Conf<Ref<Type>>>> {
- match self.lookup_type(index, fancy_procs)? {
- Some(ty) if ty.type_class() == TypeClass::VoidTypeClass => Ok(Some(Conf::new(ty, 0))),
- Some(ty) => {
- let mut confidence = max_confidence();
-
- // Extra check here for void(void) functions, they should get minimum confidence since this
- // is the signature PDB uses when it doesn't actually know the signature
- if ty.type_class() == TypeClass::FunctionTypeClass {
- if let Ok(ret) = ty.return_value() {
- if ret.contents.type_class() == TypeClass::VoidTypeClass {
- if let Ok(params) = ty.parameters() {
- if params.len() == 0 {
- confidence = 0;
- }
- }
- }
- }
- }
-
- // Also array of bare function pointers (often seen in vtables)
- // These should not be marked confidently, as they don't actually know
- // the types of their contents
-
- if ty.type_class() == TypeClass::ArrayTypeClass {
- if let Ok(ptr) = ty.element_type() {
- if ptr.contents.type_class() == TypeClass::PointerTypeClass {
- if let Ok(fun) = ptr.contents.target() {
- if fun.contents.type_class() == TypeClass::FunctionTypeClass
- && fun
- .contents
- .parameters()
- .map(|pars| pars.len())
- .unwrap_or(0)
- == 0
- {
- if let Ok(ret) = fun.contents.return_value() {
- if ret.contents.type_class() == TypeClass::VoidTypeClass {
- confidence = 0;
- }
- }
- }
- }
- }
- }
- }
-
- Ok(Some(Conf::new(ty, confidence)))
- }
- None => Ok(None),
- }
- }
-
- /// Parse and return a type by its index, used as lookup-or-parse
- fn handle_type_index(
- &mut self,
- ty: TypeIndex,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<&ParsedType>> {
- if let None = self.indexed_types.get(&ty) {
- self.log(|| format!("Parsing Type {:x?} ", ty));
-
- match finder.find(ty).and_then(|item| item.parse()) {
- Ok(data) => {
- self.type_stack.push(ty);
- let handled = self.handle_type(&data, finder);
- self.type_stack.pop();
-
- match handled {
- Ok(Some(parsed)) => {
- self.log(|| format!("Type {} parsed into: {:?}", ty, parsed));
- match &*parsed {
- ParsedType::Named(name, parsed) => {
- // PDB does this thing where anonymous inner types are represented as
- // some_type::<anonymous-tag>
- if !Self::is_name_anonymous(name) {
- if let Some(_old) =
- self.named_types.insert(name.clone(), parsed.clone())
- {
- warn!("Found two types both named `{}`, only one will be used.", name);
- }
- }
- }
- _ => {}
- }
- self.indexed_types.insert(ty, *parsed);
- }
- e => {
- self.log(|| format!("Error parsing type {}: {:x?}", ty, e));
- }
- }
- }
- Err(UnimplementedTypeKind(k)) if k != 0 => {
- warn!("Not parsing unimplemented type {}: kind {:x?}", ty, k);
- }
- Err(e) => {
- self.log(|| format!("Could not parse type: {}: {}", ty, e));
- }
- };
- }
-
- Ok(self.indexed_types.get(&ty))
- }
-
- /// Parse a new type's data
- fn handle_type(
- &mut self,
- data: &TypeData,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- match data {
- TypeData::Primitive(data) => Ok(self.handle_primitive_type(&data, finder)?),
- TypeData::Class(data) => Ok(self.handle_class_type(&data, finder)?),
- TypeData::Member(data) => Ok(self.handle_member_type(&data, finder)?),
- TypeData::MemberFunction(data) => Ok(self.handle_member_function_type(&data, finder)?),
- TypeData::OverloadedMethod(data) => {
- Ok(self.handle_overloaded_method_type(&data, finder)?)
- }
- TypeData::Method(data) => Ok(self.handle_method_type(&data, finder)?),
- TypeData::StaticMember(data) => Ok(self.handle_static_member_type(&data, finder)?),
- TypeData::Nested(data) => Ok(self.handle_nested_type(&data, finder)?),
- TypeData::BaseClass(data) => Ok(self.handle_base_class_type(&data, finder)?),
- TypeData::VirtualBaseClass(data) => {
- Ok(self.handle_virtual_base_class_type(&data, finder)?)
- }
- TypeData::VirtualFunctionTable(data) => {
- Ok(self.handle_virtual_function_table_type(&data, finder)?)
- }
- TypeData::VirtualTableShape(data) => {
- Ok(self.handle_virtual_table_shape_type(&data, finder)?)
- }
- TypeData::VirtualFunctionTablePointer(data) => {
- Ok(self.handle_virtual_function_table_pointer_type(&data, finder)?)
- }
- TypeData::Procedure(data) => Ok(self.handle_procedure_type(&data, finder)?),
- TypeData::Pointer(data) => Ok(self.handle_pointer_type(&data, finder)?),
- TypeData::Modifier(data) => Ok(self.handle_modifier_type(&data, finder)?),
- TypeData::Enumeration(data) => Ok(self.handle_enumeration_type(&data, finder)?),
- TypeData::Enumerate(data) => Ok(self.handle_enumerate_type(&data, finder)?),
- TypeData::Array(data) => Ok(self.handle_array_type(&data, finder)?),
- TypeData::Union(data) => Ok(self.handle_union_type(&data, finder)?),
- TypeData::Bitfield(data) => Ok(self.handle_bitfield_type(&data, finder)?),
- TypeData::FieldList(data) => Ok(self.handle_field_list_type(&data, finder)?),
- TypeData::ArgumentList(data) => Ok(self.handle_argument_list_type(&data, finder)?),
- TypeData::MethodList(data) => Ok(self.handle_method_list_type(&data, finder)?),
- _ => Err(anyhow!("Unknown typedata")),
- }
- }
-
- /// Get the raw (mangled) name out of a type, if possible
- fn type_data_to_raw_name(data: &TypeData) -> Option<String> {
- match data {
- TypeData::Class(data) => Some(
- data.unique_name
- .unwrap_or(data.name)
- .to_string()
- .to_string(),
- ),
- TypeData::Enumeration(data) => Some(
- data.unique_name
- .unwrap_or(data.name)
- .to_string()
- .to_string(),
- ),
- TypeData::Union(data) => Some(
- data.unique_name
- .unwrap_or(data.name)
- .to_string()
- .to_string(),
- ),
- _ => None,
- }
- }
-
- fn handle_primitive_type(
- &mut self,
- data: &PrimitiveType,
- _finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Primitive type: {:x?}", data));
- let base = match data.kind {
- PrimitiveKind::NoType => Ok(Type::void()),
- PrimitiveKind::Void => Ok(Type::void()),
- PrimitiveKind::Char => Ok(Type::int(1, true)),
- PrimitiveKind::UChar => Ok(Type::int(1, false)),
- PrimitiveKind::RChar => Ok(Type::int(1, true)),
- PrimitiveKind::WChar => Ok(Type::wide_char(2)),
- PrimitiveKind::RChar16 => Ok(Type::wide_char(2)),
- PrimitiveKind::RChar32 => Ok(Type::wide_char(4)),
- PrimitiveKind::I8 => Ok(Type::int(1, true)),
- PrimitiveKind::U8 => Ok(Type::int(1, false)),
- PrimitiveKind::Short => Ok(Type::int(2, true)),
- PrimitiveKind::UShort => Ok(Type::int(2, false)),
- PrimitiveKind::I16 => Ok(Type::int(2, true)),
- PrimitiveKind::U16 => Ok(Type::int(2, false)),
- PrimitiveKind::Long => Ok(Type::int(4, true)),
- PrimitiveKind::ULong => Ok(Type::int(4, false)),
- PrimitiveKind::I32 => Ok(Type::int(4, true)),
- PrimitiveKind::U32 => Ok(Type::int(4, false)),
- PrimitiveKind::Quad => Ok(Type::int(8, true)),
- PrimitiveKind::UQuad => Ok(Type::int(8, false)),
- PrimitiveKind::I64 => Ok(Type::int(8, true)),
- PrimitiveKind::U64 => Ok(Type::int(8, false)),
- PrimitiveKind::Octa => Ok(Type::int(16, true)),
- PrimitiveKind::UOcta => Ok(Type::int(16, false)),
- PrimitiveKind::I128 => Ok(Type::int(16, true)),
- PrimitiveKind::U128 => Ok(Type::int(16, false)),
- PrimitiveKind::F16 => Ok(Type::float(2)),
- PrimitiveKind::F32 => Ok(Type::float(4)),
- PrimitiveKind::F32PP => Ok(Type::float(4)),
- PrimitiveKind::F48 => Ok(Type::float(6)),
- PrimitiveKind::F64 => Ok(Type::float(8)),
- PrimitiveKind::F80 => Ok(Type::float(10)),
- PrimitiveKind::F128 => Ok(Type::float(16)),
- PrimitiveKind::Complex32 => Err(anyhow!("Complex32 unimplmented")),
- PrimitiveKind::Complex64 => Err(anyhow!("Complex64 unimplmented")),
- PrimitiveKind::Complex80 => Err(anyhow!("Complex80 unimplmented")),
- PrimitiveKind::Complex128 => Err(anyhow!("Complex128 unimplmented")),
- PrimitiveKind::Bool8 => Ok(Type::int(1, false)),
- PrimitiveKind::Bool16 => Ok(Type::int(2, false)),
- PrimitiveKind::Bool32 => Ok(Type::int(4, false)),
- PrimitiveKind::Bool64 => Ok(Type::int(8, false)),
- // Hack: this isn't always defined
- PrimitiveKind::HRESULT => Ok(Type::named_type_from_type(
- "HRESULT",
- Type::int(4, true).as_ref(),
- )),
- _ => Err(anyhow!("Unknown type unimplmented")),
- }?;
-
- // TODO: Pointer suffix is not exposed
- match data.indirection {
- Some(Indirection::Near16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer(
- &self.arch,
- base.as_ref(),
- ))))),
- Some(Indirection::Far16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer(
- &self.arch,
- base.as_ref(),
- ))))),
- Some(Indirection::Huge16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer(
- &self.arch,
- base.as_ref(),
- ))))),
- Some(Indirection::Near32) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer(
- &self.arch,
- base.as_ref(),
- ))))),
- Some(Indirection::Far32) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer(
- &self.arch,
- base.as_ref(),
- ))))),
- Some(Indirection::Near64) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer(
- &self.arch,
- base.as_ref(),
- ))))),
- Some(Indirection::Near128) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer(
- &self.arch,
- base.as_ref(),
- ))))),
- None => Ok(Some(Box::new(ParsedType::Bare(base)))),
- }
- }
-
- fn handle_class_type(
- &mut self,
- data: &ClassType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Class type: {:x?}", data));
-
- let raw_class_name = &data.name.to_string();
- let class_name = raw_class_name.to_string();
-
- self.log(|| format!("Named: {}", class_name));
-
- if data.properties.forward_reference() {
- // Try and find it first
- if let Some(existing) = self.named_types.get(&class_name) {
- return Ok(Some(Box::new(ParsedType::Bare(
- Type::named_type_from_type(&class_name, existing),
- ))));
- }
-
- let ntr_class = match data.kind {
- ClassKind::Class => NamedTypeReferenceClass::ClassNamedTypeClass,
- ClassKind::Struct => NamedTypeReferenceClass::StructNamedTypeClass,
- ClassKind::Interface => NamedTypeReferenceClass::StructNamedTypeClass,
- };
- return Ok(Some(Box::new(ParsedType::Bare(Type::named_type(
- &*NamedTypeReference::new(ntr_class, QualifiedName::from(class_name)),
- )))));
- }
-
- let struct_kind = match &data.kind {
- ClassKind::Class => StructureType::ClassStructureType,
- ClassKind::Struct => StructureType::StructStructureType,
- ClassKind::Interface => StructureType::StructStructureType,
- };
-
- let mut structure = StructureBuilder::new();
- structure.set_structure_type(struct_kind);
- structure.set_width(data.size);
- structure.set_packed(data.properties.packed());
-
- if let Some(fields) = data.fields {
- self.namespace_stack.push(class_name.to_string());
- let success = self.parse_structure_fields(&mut structure, fields, finder);
- self.namespace_stack.pop();
- let _ = success?;
- }
-
- let new_type = Type::structure(structure.finalize().as_ref());
- Ok(Some(Box::new(ParsedType::Named(class_name, new_type))))
- }
-
- /// Handle all the structure field parsing for a given field list, putting the fields into a struct
- fn parse_structure_fields(
- &mut self,
- structure: &mut StructureBuilder,
- fields: TypeIndex,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<()> {
- let mut base_classes = vec![];
- let mut virt_methods = HashMap::new();
- let mut non_virt_methods = Vec::new();
-
- let mut members = vec![];
-
- match self.handle_type_index(fields, finder)? {
- Some(ParsedType::FieldList(fields)) => {
- for field in fields {
- match field {
- ParsedType::Member(member) => {
- members.push(member.clone());
- }
- b @ ParsedType::BaseClass(..) => {
- base_classes.push(b.clone());
- }
- b @ ParsedType::VBaseClass(..) => {
- base_classes.push(b.clone());
- }
- ParsedType::Named(..) => {}
- ParsedType::VTablePointer(_vt) => {}
- ParsedType::Method(method) => {
- if let Some(offset) = method.vtable_offset {
- virt_methods.insert(
- offset,
- (method.name.clone(), method.method_type.clone()),
- );
- } else {
- non_virt_methods
- .push((method.name.clone(), method.method_type.clone()));
- }
- }
- ParsedType::OverloadedMethod(name, methods) => {
- for method in methods {
- if let Some(offset) = method.vtable_offset {
- virt_methods
- .insert(offset, (name.clone(), method.method_type.clone()));
- }
- }
- }
- f => {
- return Err(anyhow!("Unexpected field type {:?}", f));
- }
- }
- }
- }
- Some(_) => {
- return Err(anyhow!(
- "Structure fields list did not parse into member list?"
- ));
- }
- // No fields?
- None => {}
- }
-
- // Combine bitfields into structures
- let mut combined_bitfield_members = vec![];
- let mut last_bitfield_offset = u64::MAX;
- let mut last_bitfield_pos = u64::MAX;
- let mut last_bitfield_idx = 0;
- let mut bitfield_builder: Option<StructureBuilder> = None;
-
- fn bitfield_name(offset: u64, idx: u64) -> String {
- if idx > 0 {
- format!("__bitfield{:x}_{}", offset, idx)
- } else {
- format!("__bitfield{:x}", offset)
- }
- }
-
- for m in members {
- match (m.bitfield_position, m.bitfield_size) {
- (Some(pos), Some(_size)) => {
- if last_bitfield_offset != m.offset || last_bitfield_pos >= pos {
- if let Some(builder) = bitfield_builder.take() {
- combined_bitfield_members.push(ParsedMember {
- ty: Conf::new(
- Type::structure(builder.finalize().as_ref()),
- max_confidence(),
- ),
- name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
- offset: last_bitfield_offset,
- access: MemberAccess::PublicAccess,
- scope: MemberScope::NoScope,
- bitfield_size: None,
- bitfield_position: None,
- });
- }
- let new_builder = StructureBuilder::new();
- new_builder.set_structure_type(StructureType::UnionStructureType);
- new_builder.set_width(m.ty.contents.width());
- bitfield_builder = Some(new_builder);
-
- if last_bitfield_offset != m.offset {
- last_bitfield_idx = 0;
- } else {
- last_bitfield_idx += 1;
- }
- }
-
- last_bitfield_pos = pos;
- last_bitfield_offset = m.offset;
- bitfield_builder
- .as_mut()
- .expect("Invariant")
- .insert(&m.ty, m.name, 0, false, m.access, m.scope);
- }
- (None, None) => {
- if let Some(builder) = bitfield_builder.take() {
- combined_bitfield_members.push(ParsedMember {
- ty: Conf::new(
- Type::structure(builder.finalize().as_ref()),
- max_confidence(),
- ),
- name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
- offset: last_bitfield_offset,
- access: MemberAccess::PublicAccess,
- scope: MemberScope::NoScope,
- bitfield_size: None,
- bitfield_position: None,
- });
- }
- last_bitfield_offset = u64::MAX;
- last_bitfield_pos = u64::MAX;
- combined_bitfield_members.push(m);
- }
- e => return Err(anyhow!("Unexpected bitfield parameters {:?}", e)),
- }
- }
- if let Some(builder) = bitfield_builder.take() {
- combined_bitfield_members.push(ParsedMember {
- ty: Conf::new(
- Type::structure(builder.finalize().as_ref()),
- max_confidence(),
- ),
- name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
- offset: last_bitfield_offset,
- access: MemberAccess::PublicAccess,
- scope: MemberScope::NoScope,
- bitfield_size: None,
- bitfield_position: None,
- });
- }
- members = combined_bitfield_members;
- group_structure(
- &format!(
- "`{}`",
- self.namespace_stack
- .last()
- .ok_or_else(|| anyhow!("Expected class in ns stack"))?
- ),
- &members,
- structure,
- )?;
-
- let mut bases = vec![];
-
- for base_class in &base_classes {
- match base_class {
- ParsedType::BaseClass(name, base) => {
- let ntr_class = match self.named_types.get(name) {
- Some(ty) if ty.type_class() == TypeClass::StructureTypeClass => {
- match ty.get_structure() {
- Ok(str)
- if str.structure_type()
- == StructureType::StructStructureType =>
- {
- NamedTypeReferenceClass::StructNamedTypeClass
- }
- Ok(str)
- if str.structure_type()
- == StructureType::ClassStructureType =>
- {
- NamedTypeReferenceClass::ClassNamedTypeClass
- }
- _ => NamedTypeReferenceClass::StructNamedTypeClass,
- }
- }
- _ => NamedTypeReferenceClass::StructNamedTypeClass,
- };
- bases.push(BaseStructure::new(
- NamedTypeReference::new(ntr_class, name.into()),
- base.offset,
- base.ty.contents.width(),
- ));
- }
- ParsedType::VBaseClass(VirtualBaseClass {
- base_name,
- base_type,
- base_offset,
- ..
- }) => {
- let ntr_class = match self.named_types.get(base_name) {
- Some(ty) if ty.type_class() == TypeClass::StructureTypeClass => {
- match ty.get_structure() {
- Ok(str)
- if str.structure_type()
- == StructureType::StructStructureType =>
- {
- NamedTypeReferenceClass::StructNamedTypeClass
- }
- Ok(str)
- if str.structure_type()
- == StructureType::ClassStructureType =>
- {
- NamedTypeReferenceClass::ClassNamedTypeClass
- }
- _ => NamedTypeReferenceClass::StructNamedTypeClass,
- }
- }
- _ => NamedTypeReferenceClass::StructNamedTypeClass,
- };
- bases.push(BaseStructure::new(
- NamedTypeReference::new(ntr_class, base_name.into()),
- *base_offset,
- base_type.width(),
- ));
- warn!(
- "Class `{}` uses virtual inheritance. Type information may be inaccurate.",
- self.namespace_stack
- .last()
- .ok_or_else(|| anyhow!("Expected class in ns stack"))?
- );
- }
- e => return Err(anyhow!("Unexpected base class type: {:x?}", e)),
- }
- }
-
- if bases.len() > 1 {
- warn!(
- "Class `{}` has multiple base classes. Type information may be inaccurate.",
- self.namespace_stack
- .last()
- .ok_or_else(|| anyhow!("Expected class in ns stack"))?
- );
- }
- structure.set_base_structures(bases);
-
- if self
- .settings
- .get_bool("pdb.features.generateVTables", Some(self.bv), None)
- && !virt_methods.is_empty()
- {
- let vt = StructureBuilder::new();
-
- let mut vt_bases = vec![];
-
- for base_class in &base_classes {
- match base_class {
- ParsedType::BaseClass(base_name, _base_type) => {
- let mut vt_base_name = base_name
- .split("::")
- .into_iter()
- .map(|s| s.to_string())
- .collect::<Vec<_>>();
- vt_base_name.push("VTable".to_string());
- let vt_base_name = vt_base_name.join("::");
-
- match self.named_types.get(&vt_base_name) {
- Some(vt_base_type)
- if vt_base_type.type_class() == TypeClass::StructureTypeClass =>
- {
- let ntr_class =
- if vt_base_type.type_class() == TypeClass::StructureTypeClass {
- match vt_base_type.get_structure() {
- Ok(str)
- if str.structure_type()
- == StructureType::StructStructureType =>
- {
- NamedTypeReferenceClass::StructNamedTypeClass
- }
- Ok(str)
- if str.structure_type()
- == StructureType::ClassStructureType =>
- {
- NamedTypeReferenceClass::ClassNamedTypeClass
- }
- _ => NamedTypeReferenceClass::StructNamedTypeClass,
- }
- } else {
- NamedTypeReferenceClass::StructNamedTypeClass
- };
- vt_bases.push(BaseStructure::new(
- NamedTypeReference::new(ntr_class, vt_base_name.into()),
- 0,
- vt_base_type.width(),
- ));
- }
- e @ Some(_) => {
- return Err(anyhow!("Unexpected vtable base class: {:?}", e))
- }
- None => {
- // Parent might just not have a vtable
- }
- }
- }
- ParsedType::VBaseClass(_vbase) => {}
- e => return Err(anyhow!("Unexpected base class type: {:x?}", e)),
- }
- }
-
- let mut min_width = 0;
- for base in &vt_bases {
- min_width = min_width.max(base.width);
- }
-
- vt.set_base_structures(vt_bases);
- vt.set_propagates_data_var_refs(true);
-
- for (offset, (name, method)) in virt_methods {
- vt.insert(
- &Conf::new(
- Type::pointer(&self.arch, &Conf::new(method.method_type, max_confidence())),
- max_confidence(),
- ),
- &name,
- offset as u64,
- true,
- MemberAccess::PublicAccess,
- MemberScope::NoScope,
- );
- min_width = min_width.max((offset + self.arch.address_size()) as u64);
- }
-
- vt.set_width(min_width);
-
- let vt_type = Type::structure(vt.finalize().as_ref());
- // Need to insert a new named type for the vtable
- let mut vt_name = self
- .namespace_stack
- .last()
- .ok_or_else(|| anyhow!("Expected class in ns stack"))?
- .clone();
- vt_name += "::VTable";
- self.named_types.insert(vt_name.clone(), vt_type.clone());
-
- let vt_pointer = Type::pointer(
- &self.arch,
- &Conf::new(
- Type::named_type_from_type(&QualifiedName::from(vt_name), vt_type.as_ref()),
- max_confidence(),
- ),
- );
-
- structure.insert(
- &Conf::new(vt_pointer, max_confidence()),
- "vtable",
- 0,
- true,
- MemberAccess::PublicAccess,
- MemberScope::NoScope,
- );
- }
-
- Ok(())
- }
-
- fn handle_member_type(
- &mut self,
- data: &MemberType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Member type: {:x?}", data));
-
- let member_name = data.name.to_string();
- let member_offset = data.offset;
- let member_attrs = data.attributes;
-
- let access = match member_attrs.access() {
- 1 /* CV_private */ => MemberAccess::PrivateAccess,
- 2 /* CV_protected */ => MemberAccess::ProtectedAccess,
- 3 /* CV_public */ => MemberAccess::PublicAccess,
- _ => return Err(anyhow!("Unknown access"))
- };
-
- let scope = MemberScope::NoScope;
-
- match self.try_type_index_to_bare(data.field_type, finder, true)? {
- Some(ty) => Ok(Some(Box::new(ParsedType::Member(ParsedMember {
- ty: Conf::new(ty, max_confidence()),
- name: member_name.into_owned(),
- offset: member_offset,
- access,
- scope,
- bitfield_position: None,
- bitfield_size: None,
- })))),
- None => match self.handle_type_index(data.field_type, finder)? {
- Some(ParsedType::BitfieldType(bitfield)) => {
- Ok(Some(Box::new(ParsedType::Member(ParsedMember {
- ty: Conf::new(bitfield.ty.clone(), max_confidence()),
- name: member_name.into_owned(),
- offset: member_offset,
- access,
- scope,
- bitfield_position: Some(bitfield.position),
- bitfield_size: Some(bitfield.size),
- }))))
- }
- e => Err(anyhow!("Unexpected member type: {:x?}", e)),
- },
- }
- }
-
- fn handle_member_function_type(
- &mut self,
- data: &MemberFunctionType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got MemberFunction type: {:x?}", data));
- let return_type = self.type_index_to_bare(data.return_type, finder, false)?;
-
- let class_name = match self.handle_type_index(data.class_type, finder)? {
- Some(ParsedType::Bare(ty)) if ty.type_class() == TypeClass::NamedTypeReferenceClass => {
- ty.get_named_type_reference()
- .map_err(|_| anyhow!("Expected NTR to have NTR"))?
- .name()
- .to_string()
- }
- e => return Err(anyhow!("Unexpected class type: {:x?}", e)),
- };
-
- let this_pointer_type = if let Some(this_pointer_type) = data.this_pointer_type {
- match self.handle_type_index(this_pointer_type, finder)? {
- Some(ParsedType::Bare(ty)) => Some(ty.clone()),
- e => return Err(anyhow!("Unexpected this pointer type: {:x?}", e)),
- }
- } else {
- None
- };
-
- let mut arguments = match self.handle_type_index(data.argument_list, finder)? {
- Some(ParsedType::ArgumentList(args)) => args.clone(),
- e => return Err(anyhow!("Unexpected argument list type: {:x?}", e)),
- };
-
- // It looks like pdb stores varargs by having the final argument be void
- let mut is_varargs = false;
- if let Some(last) = arguments.pop() {
- if last.t.contents.as_ref().type_class() == TypeClass::VoidTypeClass {
- is_varargs = true;
- } else {
- arguments.push(last);
- }
- }
-
- let mut fancy_return_type = return_type.clone();
- let mut fancy_arguments = arguments.clone();
-
- if data.attributes.cxx_return_udt()
- || !self.can_fit_in_register(data.return_type, finder, true)
- {
- // Return UDT??
- // This probably means the return value got pushed to the stack
- fancy_return_type = Type::pointer(
- &self.arch,
- &Conf::new(return_type.clone(), max_confidence()),
- );
- fancy_arguments.insert(
- 0,
- FunctionParameter::new(
- Conf::new(fancy_return_type.clone(), max_confidence()),
- "__return".to_string(),
- None,
- ),
- );
- }
-
- if let Some(this_ptr) = &this_pointer_type {
- self.insert_this_pointer(&mut fancy_arguments, this_ptr.clone())?;
- }
-
- let convention = self
- .cv_call_t_to_calling_convention(data.attributes.calling_convention())
- .map(|cc| Conf::new(cc, max_confidence()))
- .unwrap_or({
- if is_varargs {
- Conf::new(self.cdecl_cc.clone(), max_confidence())
- } else if this_pointer_type.is_some() {
- Conf::new(self.thiscall_cc.clone(), max_confidence())
- } else {
- Conf::new(self.default_cc.clone(), 16)
- }
- });
-
- let func = Type::function_with_options(
- &Conf::new(return_type, max_confidence()),
- arguments.as_slice(),
- is_varargs,
- &convention,
- Conf::new(0, 0),
- );
-
- let fancy_func = Type::function_with_options(
- &Conf::new(fancy_return_type, max_confidence()),
- fancy_arguments.as_slice(),
- is_varargs,
- &convention,
- Conf::new(0, 0),
- );
-
- Ok(Some(Box::new(ParsedType::MemberFunction(
- ParsedMemberFunction {
- attributes: data.attributes,
- class_name,
- method_type: fancy_func,
- raw_method_type: func,
- this_pointer_type,
- this_adjustment: data.this_adjustment as usize,
- },
- ))))
- }
-
- fn handle_overloaded_method_type(
- &mut self,
- data: &OverloadedMethodType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got OverloadedMethod type: {:x?}", data));
- // This is just a MethodList in disguise
- let method_list = match self.handle_type_index(data.method_list, finder)? {
- Some(ParsedType::MethodList(list)) => list.clone(),
- e => return Err(anyhow!("Unexpected method list type: {:x?}", e)),
- };
-
- Ok(Some(Box::new(ParsedType::OverloadedMethod(
- data.name.to_string().to_string(),
- method_list,
- ))))
- }
-
- fn handle_method_type(
- &mut self,
- data: &MethodType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Method type: {:x?}", data));
-
- let member_function = match self.handle_type_index(data.method_type, finder)? {
- Some(ParsedType::MemberFunction(func)) => func.clone(),
- e => return Err(anyhow!("Unexpected method type {:?}", e)),
- };
-
- Ok(Some(Box::new(ParsedType::Method(ParsedMethod {
- attributes: data.attributes,
- name: data.name.to_string().to_string(),
- method_type: member_function,
- vtable_offset: data.vtable_offset.map(|o| o as usize),
- }))))
- }
-
- fn handle_static_member_type(
- &mut self,
- data: &StaticMemberType,
- _finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got StaticMember type: {:x?}", data));
- // TODO: Not handling these
- Ok(None)
- }
-
- fn handle_nested_type(
- &mut self,
- data: &NestedType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Nested type: {:x?}", data));
- let mut class_name_ns = self.namespace_stack.clone();
- class_name_ns.push(data.name.to_string().to_string());
- let ty = self.type_index_to_bare(data.nested_type, finder, false)?;
- Ok(Some(Box::new(ParsedType::Named(
- class_name_ns.join("::"),
- ty,
- ))))
- }
-
- fn handle_base_class_type(
- &mut self,
- data: &BaseClassType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got BaseClass type: {:x?}", data));
-
- let base_offset = data.offset;
- let base_attrs = data.attributes;
-
- let (member_name, t) = match self.handle_type_index(data.base_class, finder)? {
- Some(ParsedType::Named(n, t)) => (n.clone(), t.clone()),
- Some(ParsedType::Bare(t)) if t.type_class() == TypeClass::NamedTypeReferenceClass => {
- let name = t
- .get_named_type_reference()
- .map_err(|_| anyhow!("Expected NTR to have NTR"))?
- .name()
- .to_string();
- (name, t.clone())
- }
- e => return Err(anyhow!("Unexpected base class type: {:x?}", e)),
- };
-
- // Try to resolve the full base type
- let resolved_type = match self.try_type_index_to_bare(data.base_class, finder, true)? {
- Some(ty) => Type::named_type_from_type(&member_name, ty.as_ref()),
- None => t.clone(),
- };
-
- let access = match base_attrs.access() {
- 1 /* CV_private */ => MemberAccess::PrivateAccess,
- 2 /* CV_protected */ => MemberAccess::ProtectedAccess,
- 3 /* CV_public */ => MemberAccess::PublicAccess,
- _ => return Err(anyhow!("Unknown access"))
- };
-
- let scope = MemberScope::NoScope;
- Ok(Some(Box::new(ParsedType::BaseClass(
- member_name.clone(),
- StructureMember::new(
- Conf::new(resolved_type, max_confidence()),
- member_name,
- base_offset as u64,
- access,
- scope,
- ),
- ))))
- }
-
- fn handle_virtual_base_class_type(
- &mut self,
- data: &VirtualBaseClassType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got VirtualBaseClass type: {:x?}", data));
-
- let (n, ty) = match self.handle_type_index(data.base_class, finder)? {
- Some(ParsedType::Named(n, t)) => (n.clone(), t.clone()),
- Some(ParsedType::Bare(t)) if t.type_class() == TypeClass::NamedTypeReferenceClass => {
- let name = t
- .get_named_type_reference()
- .map_err(|_| anyhow!("Expected NTR to have NTR"))?
- .name()
- .to_string();
- (name, t.clone())
- }
- e => return Err(anyhow!("Unexpected base class type: {:x?}", e)),
- };
-
- // In addition to the base class, we also have a vbtable
- let vbptr_type = match self.handle_type_index(data.base_pointer, finder)? {
- Some(ParsedType::Bare(t)) => t.clone(),
- e => return Err(anyhow!("Unexpected virtual base pointer type: {:x?}", e)),
- };
-
- Ok(Some(Box::new(ParsedType::VBaseClass(VirtualBaseClass {
- base_name: n,
- base_type: ty,
- base_offset: data.base_pointer_offset as u64,
- base_table_type: vbptr_type,
- base_table_offset: data.virtual_base_offset as u64,
- }))))
- }
-
- fn handle_virtual_function_table_type(
- &mut self,
- data: &VirtualFunctionTableType,
- _finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got VirtualFunctionTableType type: {:x?}", data));
- Err(anyhow!("VirtualFunctionTableType unimplemented"))
- }
-
- fn handle_virtual_table_shape_type(
- &mut self,
- data: &VirtualTableShapeType,
- _finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got VirtualTableShapeType type: {:x?}", data));
- Ok(Some(Box::new(ParsedType::VTableShape(
- data.descriptors.clone(),
- ))))
- }
-
- fn handle_virtual_function_table_pointer_type(
- &mut self,
- data: &VirtualFunctionTablePointerType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got VirtualFunctionTablePointer type: {:x?}", data));
- let shape = match self.handle_type_index(data.table, finder)? {
- Some(ParsedType::VTablePointer(shape)) => shape.clone(),
- e => {
- return Err(anyhow!(
- "Could not parse virtual function table pointer type: {:x?}",
- e
- ))
- }
- };
-
- Ok(Some(Box::new(ParsedType::VTablePointer(shape))))
- }
-
- fn handle_procedure_type(
- &mut self,
- data: &ProcedureType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Procedure type: {:x?}", data));
- let return_type = if let Some(return_type_index) = data.return_type {
- self.try_type_index_to_bare(return_type_index, finder, false)?
- } else {
- None
- }
- .map(|r| Conf::new(r, max_confidence()))
- .unwrap_or(Conf::new(Type::void(), 0));
-
- let mut arguments = match self.handle_type_index(data.argument_list, finder)? {
- Some(ParsedType::ArgumentList(args)) => args.clone(),
- e => return Err(anyhow!("Unexpected argument list type: {:x?}", e)),
- };
-
- // It looks like pdb stores varargs by having the final argument be void
- let mut is_varargs = false;
- if let Some(last) = arguments.pop() {
- if last.t.contents.as_ref().type_class() == TypeClass::VoidTypeClass {
- is_varargs = true;
- } else {
- arguments.push(last);
- }
- }
-
- let mut fancy_return_type = return_type.clone();
- let mut fancy_arguments = arguments.clone();
-
- let mut return_stacky = data.attributes.cxx_return_udt();
- if let Some(return_type_index) = data.return_type {
- return_stacky |= !self.can_fit_in_register(return_type_index, finder, true);
- }
- if return_stacky {
- // Stack return via a pointer in the first parameter
- fancy_return_type =
- Conf::new(Type::pointer(&self.arch, &return_type), max_confidence());
- fancy_arguments.insert(
- 0,
- FunctionParameter::new(fancy_return_type.clone(), "__return".to_string(), None),
- );
- }
-
- let convention = self
- .cv_call_t_to_calling_convention(data.attributes.calling_convention())
- .map(|cc| Conf::new(cc, max_confidence()))
- .unwrap_or(Conf::new(self.default_cc.clone(), 0));
- self.log(|| format!("Convention: {:?}", convention));
-
- let func = Type::function_with_options(
- &return_type,
- arguments.as_slice(),
- is_varargs,
- &convention,
- Conf::new(0, 0),
- );
-
- let fancy_func = Type::function_with_options(
- &fancy_return_type,
- fancy_arguments.as_slice(),
- is_varargs,
- &convention,
- Conf::new(0, 0),
- );
-
- Ok(Some(Box::new(ParsedType::Procedure(ParsedProcedureType {
- method_type: fancy_func,
- raw_method_type: func,
- }))))
- }
-
- fn handle_pointer_type(
- &mut self,
- data: &PointerType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Pointer type: {:x?}", data));
- let base = match self.try_type_index_to_bare(data.underlying_type, finder, false)? {
- Some(ty) => Some(ty.clone()),
- None => match self.handle_type_index(data.underlying_type, finder)? {
- Some(ParsedType::VTableShape(descriptors)) => {
- return Ok(Some(Box::new(ParsedType::VTablePointer(
- descriptors.clone(),
- ))));
- }
- _ => None,
- },
- };
-
- if let Some(base) = base {
- Ok(Some(Box::new(ParsedType::Bare(Type::pointer(
- &self.arch,
- base.as_ref(),
- )))))
- } else {
- Ok(None)
- }
- }
-
- fn handle_modifier_type(
- &mut self,
- data: &ModifierType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Modifier type: {:x?}", data));
- let base = self.try_type_index_to_bare(data.underlying_type, finder, false)?;
-
- if let Some(base) = base {
- let builder = TypeBuilder::new(base.as_ref());
- builder.set_const(data.constant);
- builder.set_volatile(data.volatile);
- Ok(Some(Box::new(ParsedType::Bare(builder.finalize()))))
- } else {
- Ok(None)
- }
- }
-
- fn handle_enumeration_type(
- &mut self,
- data: &EnumerationType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Enumeration type: {:x?}", data));
-
- let raw_enum_name = &data.name.to_string();
- let enum_name = raw_enum_name.to_string();
- self.log(|| format!("Named: {}", enum_name));
-
- if data.properties.forward_reference() {
- // Try and find it first
- if let Some(existing) = self.named_types.get(&enum_name) {
- return Ok(Some(Box::new(ParsedType::Bare(
- Type::named_type_from_type(&enum_name, existing),
- ))));
- }
-
- let ntr_class = NamedTypeReferenceClass::EnumNamedTypeClass;
- return Ok(Some(Box::new(ParsedType::Bare(Type::named_type(
- &*NamedTypeReference::new(ntr_class, QualifiedName::from(enum_name)),
- )))));
- }
-
- let enumeration = EnumerationBuilder::new();
-
- match self.handle_type_index(data.fields, finder)? {
- Some(ParsedType::FieldList(fields)) => {
- for field in fields {
- match field {
- ParsedType::Enumerate(member) => {
- enumeration.insert(member.name.clone(), member.value);
- }
- e => return Err(anyhow!("Unexpected enumerate member: {:?}", e)),
- }
- }
- }
- // No fields?
- None => {}
- e => return Err(anyhow!("Unexpected enumeration field list: {:?}", e)),
- }
-
- let underlying = match self.handle_type_index(data.underlying_type, finder)? {
- Some(ParsedType::Bare(ty)) => ty.clone(),
- e => return Err(anyhow!("Making enumeration from unexpected type: {:x?}", e)),
- };
-
- let new_type = Type::enumeration(
- enumeration.finalize().as_ref(),
- underlying.width() as usize,
- underlying.is_signed().contents,
- );
-
- Ok(Some(Box::new(ParsedType::Named(enum_name, new_type))))
- }
-
- fn handle_enumerate_type(
- &mut self,
- data: &EnumerateType,
- _finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Enumerate type: {:x?}", data));
- Ok(Some(Box::new(ParsedType::Enumerate(EnumerationMember {
- name: data.name.to_string().to_string(),
- value: match data.value {
- Variant::U8(v) => v as u64,
- Variant::U16(v) => v as u64,
- Variant::U32(v) => v as u64,
- Variant::U64(v) => v as u64,
- Variant::I8(v) => (v as i64) as u64,
- Variant::I16(v) => (v as i64) as u64,
- Variant::I32(v) => (v as i64) as u64,
- Variant::I64(v) => (v as i64) as u64,
- },
- is_default: false,
- }))))
- }
-
- fn handle_array_type(
- &mut self,
- data: &ArrayType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Array type: {:x?}", data));
- // PDB stores array sizes as TOTAL bytes not element count
- // So we need to look up the original type's size to know how many there are
- let base = self.try_type_index_to_bare(data.element_type, finder, true)?;
-
- if let Some(base) = base {
- let mut new_type = base;
- if new_type.width() == 0 {
- if new_type.width() == 0 {
- return Err(anyhow!(
- "Cannot calculate array of 0-size elements: {}",
- new_type
- ));
- }
- }
-
- let mut counts = data
- .dimensions
- .iter()
- .map(|t| *t as u64)
- .collect::<Vec<_>>();
- for i in 0..counts.len() {
- for j in i..counts.len() {
- if counts[j] % new_type.width() != 0 {
- return Err(anyhow!(
- "Array stride {} is not a multiple of element {} size {}",
- counts[j],
- new_type,
- new_type.width()
- ));
- }
- counts[j] /= new_type.width();
- }
-
- new_type = Type::array(new_type.as_ref(), counts[i] as u64);
- }
-
- Ok(Some(Box::new(ParsedType::Bare(new_type))))
- } else {
- Ok(None)
- }
- }
-
- fn handle_union_type(
- &mut self,
- data: &UnionType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Union type: {:x?}", data));
-
- let raw_union_name = &data.name.to_string();
- let union_name = raw_union_name.to_string();
- self.log(|| format!("Named: {}", union_name));
-
- if data.properties.forward_reference() {
- // Try and find it first
- if let Some(existing) = self.named_types.get(&union_name) {
- return Ok(Some(Box::new(ParsedType::Bare(
- Type::named_type_from_type(&union_name, existing),
- ))));
- }
-
- let ntr_class = NamedTypeReferenceClass::UnionNamedTypeClass;
- return Ok(Some(Box::new(ParsedType::Bare(Type::named_type(
- &*NamedTypeReference::new(ntr_class, QualifiedName::from(union_name)),
- )))));
- }
-
- let mut structure = StructureBuilder::new();
- structure.set_structure_type(StructureType::UnionStructureType);
- structure.set_width(data.size);
-
- self.namespace_stack.push(union_name.to_string());
- let success = self.parse_union_fields(&mut structure, data.fields, finder);
- self.namespace_stack.pop();
- let _ = success?;
-
- let new_type = Type::structure(structure.finalize().as_ref());
- Ok(Some(Box::new(ParsedType::Named(union_name, new_type))))
- }
-
- /// Parse the fields in a union's field list
- fn parse_union_fields(
- &mut self,
- structure: &mut StructureBuilder,
- fields: TypeIndex,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<()> {
- let mut union_groups = vec![];
- let mut last_union_group = u64::MAX;
-
- match self.handle_type_index(fields, finder) {
- Ok(Some(ParsedType::FieldList(fields))) => {
- for field in fields {
- match field {
- ParsedType::Member(member) => {
- if member.offset <= last_union_group {
- union_groups.push(vec![]);
- }
- last_union_group = member.offset;
- union_groups
- .last_mut()
- .expect("invariant")
- .push(member.clone());
- }
- ParsedType::Method(..) => {}
- ParsedType::Named(..) => {}
- e => return Err(anyhow!("Unexpected union member type {:?}", e)),
- }
- }
- }
- e => return Err(anyhow!("Unexpected union field list type {:?}", e)),
- }
-
- for (i, group) in union_groups.into_iter().enumerate() {
- if group.len() == 1 {
- structure.insert(
- &group[0].ty,
- group[0].name.clone(),
- group[0].offset,
- false,
- group[0].access,
- group[0].scope,
- );
- } else {
- let inner_struct = StructureBuilder::new();
- for member in group {
- inner_struct.insert(
- &member.ty,
- member.name.clone(),
- member.offset,
- false,
- member.access,
- member.scope,
- );
- }
- structure.insert(
- &Conf::new(
- Type::structure(inner_struct.finalize().as_ref()),
- max_confidence(),
- ),
- format!("__inner{:x}", i),
- 0,
- false,
- MemberAccess::PublicAccess,
- MemberScope::NoScope,
- );
- }
- }
-
- Ok(())
- }
-
- fn handle_bitfield_type(
- &mut self,
- data: &BitfieldType,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got Bitfield type: {:x?}", data));
- Ok(self
- .try_type_index_to_bare(data.underlying_type, finder, true)?
- .map(|ty| {
- Box::new(ParsedType::BitfieldType(ParsedBitfieldType {
- size: data.length as u64,
- position: data.position as u64,
- ty,
- }))
- }))
- }
-
- fn handle_field_list_type(
- &mut self,
- data: &FieldList,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got FieldList type: {:x?}", data));
-
- let mut fields = vec![];
- for (i, field) in data.fields.iter().enumerate() {
- match self.handle_type(field, finder)? {
- Some(f) => {
- self.log(|| format!("Inner field {} parsed into {:?}", i, f));
- fields.push(*f);
- }
- None => {
- self.log(|| format!("Inner field {} parsed into None", i));
- }
- }
- }
-
- if let Some(cont) = data.continuation {
- match self.handle_type_index(cont, finder)? {
- Some(ParsedType::FieldList(cont_fields)) => {
- fields.extend(cont_fields.clone());
- }
- None => {}
- f => {
- return Err(anyhow!("Unexpected field list continuation {:?}", f));
- }
- }
- }
- Ok(Some(Box::new(ParsedType::FieldList(fields))))
- }
-
- fn handle_argument_list_type(
- &mut self,
- data: &ArgumentList,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got ArgumentList type: {:x?}", data));
- let mut args = vec![];
- for &arg in data.arguments.iter() {
- match self.try_type_index_to_bare(arg, finder, false)? {
- Some(ty) => {
- // On x86_32, structures are stored on the stack directly
- // On x64, they are put into pointers if they are not a int size
- // TODO: Ugly hack
- if self.arch.address_size() == 4 || Self::size_can_fit_in_register(ty.width()) {
- args.push(FunctionParameter::new(
- Conf::new(ty.clone(), max_confidence()),
- "".to_string(),
- None,
- ));
- } else {
- args.push(FunctionParameter::new(
- Conf::new(
- Type::pointer(self.arch.as_ref(), ty.as_ref()),
- max_confidence(),
- ),
- "".to_string(),
- None,
- ));
- }
- }
- e => {
- return Err(anyhow!("Unexpected argument type {:?}", e));
- }
- }
- }
- Ok(Some(Box::new(ParsedType::ArgumentList(args))))
- }
-
- fn handle_method_list_type(
- &mut self,
- data: &MethodList,
- finder: &mut ItemFinder<TypeIndex>,
- ) -> Result<Option<Box<ParsedType>>> {
- self.log(|| format!("Got MethodList type: {:x?}", data));
-
- let mut list = vec![];
- for method in &data.methods {
- match self.handle_type_index(method.method_type, finder)? {
- Some(ParsedType::MemberFunction(func)) => {
- list.push(ParsedMethodListEntry {
- attributes: method.attributes,
- method_type: func.clone(),
- vtable_offset: method.vtable_offset.map(|o| o as usize),
- });
- }
- e => return Err(anyhow!("Unexpected method list entry: {:?}", e)),
- }
- }
-
- Ok(Some(Box::new(ParsedType::MethodList(list))))
- }
-
- /// Given a type index, get a bare binja type (or fail if not found)
- /// Optionally, set fully_resolve to true to parse and get the real type back in the case of NTRs
- fn type_index_to_bare(
- &mut self,
- index: TypeIndex,
- finder: &mut ItemFinder<TypeIndex>,
- fully_resolve: bool,
- ) -> Result<Ref<Type>> {
- match self.try_type_index_to_bare(index, finder, fully_resolve)? {
- Some(ty) => Ok(ty),
- None => Err(anyhow!("Unresolved expected type {:?}", index)),
- }
- }
-
- /// Given a type index, try to get a bare binja type
- /// Optionally, set fully_resolve to true to parse and get the real type back in the case of NTRs
- fn try_type_index_to_bare(
- &mut self,
- index: TypeIndex,
- finder: &mut ItemFinder<TypeIndex>,
- fully_resolve: bool,
- ) -> Result<Option<Ref<Type>>> {
- let (mut type_, inner) = match self.handle_type_index(index, finder)? {
- Some(ParsedType::Bare(ty)) => (ty.clone(), None),
- Some(ParsedType::Named(name, ty)) => {
- (Type::named_type_from_type(name, &ty), Some(ty.clone()))
- }
- Some(ParsedType::Procedure(ParsedProcedureType { method_type, .. })) => {
- (method_type.clone(), Some(method_type.clone()))
- }
- Some(ParsedType::MemberFunction(ParsedMemberFunction { method_type, .. })) => {
- (method_type.clone(), Some(method_type.clone()))
- }
- Some(ParsedType::Member(ParsedMember { ty, .. })) => {
- (ty.contents.clone(), Some(ty.contents.clone()))
- }
- _ => return Ok(None),
- };
-
- if type_.type_class() == TypeClass::NamedTypeReferenceClass {
- if type_.width() == 0 {
- // Replace empty NTR with fully parsed NTR, if we can
- let name = type_
- .get_named_type_reference()
- .map_err(|_| anyhow!("expected ntr"))?
- .name()
- .to_string();
- if let Some(full_ntr) = self.named_types.get(&name) {
- type_ = Type::named_type_from_type(&name, full_ntr.as_ref());
- }
- }
- }
-
- if !fully_resolve {
- return Ok(Some(type_));
- }
-
- if type_.type_class() == TypeClass::NamedTypeReferenceClass {
- if type_.width() == 0 {
- // Look up raw name of this type
- if let Ok(raw) = finder.find(index) {
- if let Ok(parsed) = raw.parse() {
- // Have to use raw name here because self.full_type_indices uses raw name
- // for some reason
- if let Some(raw_name) = Self::type_data_to_raw_name(&parsed) {
- if let Some(&full_index) = self.full_type_indices.get(&raw_name) {
- if let None = self.type_stack.iter().find(|&&idx| idx == full_index)
- {
- if full_index != index {
- return self.try_type_index_to_bare(
- full_index,
- finder,
- fully_resolve,
- );
- }
- }
- }
- }
- }
- }
- }
- }
-
- if type_.type_class() == TypeClass::NamedTypeReferenceClass {
- // PDB does this thing where anonymous inner types are represented as
- // some_type::<anonymous-tag>
- let name = type_
- .get_named_type_reference()
- .map_err(|_| anyhow!("expected ntr"))?
- .name()
- .to_string();
- if Self::is_name_anonymous(&name) {
- if let Some(inner) = inner.as_ref() {
- type_ = inner.clone();
- } else {
- // Look up raw name of this type
- if let Ok(raw) = finder.find(index) {
- if let Ok(parsed) = raw.parse() {
- // Have to use raw name here because self.full_type_indices uses raw name
- // for some reason
- if let Some(raw_name) = Self::type_data_to_raw_name(&parsed) {
- if let Some(&full_index) = self.full_type_indices.get(&raw_name) {
- if let None =
- self.type_stack.iter().find(|&&idx| idx == full_index)
- {
- if full_index != index {
- return self.try_type_index_to_bare(
- full_index,
- finder,
- fully_resolve,
- );
- }
- }
- }
- }
- }
- }
- }
- }
- }
- Ok(Some(type_))
- }
-
- /// Is this name one of the stupid microsoft unnamed type names
- fn is_name_anonymous(name: &String) -> bool {
- let name_string = name.split("::").last().unwrap_or("").to_string();
- return name_string == "<anonymous-tag>" || name_string.starts_with("<unnamed-");
- }
-
- /// Find a calling convention in the platform
- pub(crate) fn find_calling_convention(
- platform: &Platform,
- name: &str,
- ) -> Option<Ref<CallingConvention<CoreArchitecture>>> {
- platform
- .calling_conventions()
- .iter()
- .find(|c| c.name().as_str() == name)
- .map(|g| g.clone())
- }
-
- /// Convert pdb calling convention enum to binja
- fn cv_call_t_to_calling_convention(
- &self,
- cv: u8,
- ) -> Option<Ref<CallingConvention<CoreArchitecture>>> {
- match CV_call_t::try_from(cv) {
- Ok(CV_call_t::NEAR_FAST) | Ok(CV_call_t::FAR_FAST) => {
- self.platform.get_fastcall_calling_convention()
- }
- Ok(CV_call_t::NEAR_STD) | Ok(CV_call_t::FAR_STD) => {
- self.platform.get_stdcall_calling_convention()
- }
- Ok(CV_call_t::NEAR_C) | Ok(CV_call_t::FAR_C) => {
- self.platform.get_cdecl_calling_convention()
- }
- Ok(CV_call_t::THISCALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "thiscall")
- }
- Ok(CV_call_t::NEAR_PASCAL) | Ok(CV_call_t::FAR_PASCAL) => {
- Self::find_calling_convention(self.platform.as_ref(), "pascal")
- }
- Ok(CV_call_t::NEAR_SYS) | Ok(CV_call_t::FAR_SYS) => {
- Self::find_calling_convention(self.platform.as_ref(), "sys")
- }
- Ok(CV_call_t::MIPSCALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "mipscall")
- }
- Ok(CV_call_t::ALPHACALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "alphacall")
- }
- Ok(CV_call_t::PPCCALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "ppccall")
- }
- Ok(CV_call_t::SHCALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "shcall")
- }
- Ok(CV_call_t::ARMCALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "armcall")
- }
- Ok(CV_call_t::AM33CALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "am33call")
- }
- Ok(CV_call_t::TRICALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "tricall")
- }
- Ok(CV_call_t::SH5CALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "sh5call")
- }
- Ok(CV_call_t::M32RCALL) => {
- Self::find_calling_convention(self.platform.as_ref(), "m32rcall")
- }
- Ok(CV_call_t::NEAR_VECTOR) => {
- Self::find_calling_convention(self.platform.as_ref(), "vectorcall")
- }
- _ => None,
- }
- }
-
- /// Insert an argument for the thisptr in a function param list
- fn insert_this_pointer(
- &self,
- parameters: &mut Vec<FunctionParameter>,
- this_type: Ref<Type>,
- ) -> Result<()> {
- parameters.insert(
- 0,
- FunctionParameter::new(
- Conf::new(this_type, max_confidence()),
- "this".to_string(),
- None,
- ),
- );
-
- Ok(())
- }
-
- /// Does this type get returned in rax? Or should we put it on the stack?
- pub fn can_fit_in_register(
- &mut self,
- index: TypeIndex,
- finder: &mut ItemFinder<TypeIndex>,
- treat_references_like_pointers: bool,
- ) -> bool {
- // TLDR "This is impossible so we're making a best-guess"
- // GET READY OKAY
-
- // "A scalar return value that can fit into 64 bits, including the __m64 type, is returned
- // through RAX. Non-scalar types including floats, doubles, and vector types such as __m128,
- // __m128i, __m128d are returned in XMM0. The state of unused bits in the value returned
- // in RAX or XMM0 is undefined.
-
- // "User-defined types can be returned by value from global functions and static member
- // functions. To return a user-defined type by value in RAX, it must have a length of
- // 1, 2, 4, 8, 16, 32, or 64 bits. It must also have no user-defined constructor, destructor,
- // or copy assignment operator. It can have no private or protected non-static data members,
- // and no non-static data members of reference type. It can't have base classes or virtual
- // functions. And, it can only have data members that also meet these requirements.
- // (This definition is essentially the same as a C++03 POD type. Because the definition has
- // changed in the C++11 standard, we don't recommend using std::is_pod for this test.)
- // Otherwise, the caller must allocate memory for the return value and pass a pointer to it
- // as the first argument. The remaining arguments are then shifted one argument to the right.
- // The same pointer must be returned by the callee in RAX."
-
- // - length of 1, 2, 4, 8, 16, 32, or 64 bits
- // - no user-defined constructor
- // - no user-defined destructor
- // - no user-defined copy assignment operator
- // - no private data members
- // - no protected data members
- // - no reference data members
- // - no base classes
- // - no virtual functions
-
- // This one is incorrect, so we're not including it:
- // - all members meet these requirements
- // https://godbolt.org/z/hsTxrxq9c extremely cool
-
- // Are we going to implement all of this?
- // No? We're just going to do something close and leave it to the users to figure out the rest
- // There's no way I'm digging through all nonsense
-
- // After a quick GitHub discussion (https://github.com/MicrosoftDocs/cpp-docs/issues/4152)
- // I've determined this is unknowable.
- // Microsoft does it again!!!!
-
- if let Some(&returnable) = self.type_default_returnable.get(&index) {
- returnable
- } else {
- let returnable =
- self.can_fit_in_register_impl(index, finder, treat_references_like_pointers);
- self.log(|| format!("Type {} is default returnable: {}", index, returnable));
- self.type_default_returnable.insert(index, returnable);
- returnable
- }
- }
-
- fn size_can_fit_in_register(size: u64) -> bool {
- match size {
- 0 | 1 | 2 | 4 | 8 => true,
- _ => false,
- }
- }
-
- // Memoized... because this has gotta be real slow
- fn can_fit_in_register_impl(
- &mut self,
- index: TypeIndex,
- finder: &mut ItemFinder<TypeIndex>,
- treat_references_like_pointers: bool,
- ) -> bool {
- let ty = match finder.find(index) {
- Ok(item) => match item.parse() {
- Ok(ty) => ty,
- Err(_) => return false,
- },
- Err(_) => return false,
- };
-
- fn get_fields<'a>(
- index: TypeIndex,
- finder: &mut ItemFinder<'a, TypeIndex>,
- ) -> Result<Vec<TypeData<'a>>> {
- match finder.find(index).and_then(|fields| fields.parse()) {
- Ok(TypeData::FieldList(fields)) => {
- if let Some(cont) = fields.continuation {
- Ok(fields
- .fields
- .into_iter()
- .chain(get_fields(cont, finder)?.into_iter())
- .collect::<Vec<_>>())
- } else {
- Ok(fields.fields)
- }
- }
- _ => Err(anyhow!("can't lookup fields")),
- }
- }
-
- match ty {
- TypeData::Primitive(_) => true,
- TypeData::Pointer(p) => match p.attributes.pointer_mode() {
- PointerMode::Pointer => true,
- PointerMode::Member => true,
- PointerMode::MemberFunction => true,
- // - no reference data members
- PointerMode::LValueReference => treat_references_like_pointers,
- PointerMode::RValueReference => treat_references_like_pointers,
- },
- TypeData::Array(a) => {
- Self::size_can_fit_in_register(*a.dimensions.last().unwrap_or(&0) as u64)
- && self.can_fit_in_register(a.element_type, finder, false)
- }
- TypeData::Modifier(m) => {
- self.can_fit_in_register(m.underlying_type, finder, treat_references_like_pointers)
- }
- TypeData::Enumeration(e) => self.can_fit_in_register(e.underlying_type, finder, false),
- TypeData::Class(c) => {
- if c.properties.forward_reference() {
- if let Some(raw_name) = c.unique_name {
- if let Some(&full) = self
- .full_type_indices
- .get(&raw_name.to_string().to_string())
- {
- return self.can_fit_in_register(
- full,
- finder,
- treat_references_like_pointers,
- );
- }
- }
- // Can't look up, assume not
- return false;
- }
-
- // - length of 1, 2, 4, 8, 16, 32, or 64 bits
- if !Self::size_can_fit_in_register(c.size) {
- return false;
- }
-
- // - no user-defined constructor
- // - no user-defined destructor
- // - no user-defined copy assignment operator
- if c.properties.constructors() || c.properties.overloaded_assignment() {
- return false;
- }
-
- // - no base classes
- if let Some(_) = c.derived_from {
- return false;
- }
- // - no virtual functions
- if let Some(_) = c.vtable_shape {
- return false;
- }
-
- let fields = if let Some(fields_idx) = c.fields {
- if let Ok(fields) = get_fields(fields_idx, finder) {
- fields
- } else {
- return false;
- }
- } else {
- // No fields?
- return true;
- };
-
- for field in fields {
- match field {
- TypeData::Member(m) => {
- // - no private data members
- // - no protected data members
- if m.attributes.access() == 1 || m.attributes.access() == 2 {
- return false;
- }
- }
- TypeData::OverloadedMethod(m) => {
- match finder.find(m.method_list).and_then(|l| l.parse()) {
- Ok(TypeData::MethodList(list)) => {
- for m in list.methods {
- // - no virtual functions
- if m.attributes.is_virtual() {
- return false;
- }
- }
- }
- _ => return false,
- }
- }
- TypeData::Method(m) => {
- // - no virtual functions
- if m.attributes.is_virtual() {
- return false;
- }
- }
- // - no base classes
- TypeData::BaseClass(_) => return false,
- TypeData::VirtualBaseClass(_) => return false,
- TypeData::VirtualFunctionTable(_) => return false,
- TypeData::VirtualTableShape(_) => return false,
- TypeData::VirtualFunctionTablePointer(_) => return false,
- _ => {}
- }
- }
- return true;
- }
- TypeData::Union(u) => {
- if u.properties.forward_reference() {
- if let Some(raw_name) = u.unique_name {
- if let Some(&full) = self
- .full_type_indices
- .get(&raw_name.to_string().to_string())
- {
- return self.can_fit_in_register(
- full,
- finder,
- treat_references_like_pointers,
- );
- }
- }
- // Can't look up, assume not
- return false;
- }
-
- // - length of 1, 2, 4, 8, 16, 32, or 64 bits
- if !Self::size_can_fit_in_register(u.size) {
- return false;
- }
-
- // - no user-defined constructor
- // - no user-defined destructor
- // - no user-defined copy assignment operator
- if u.properties.constructors() || u.properties.overloaded_assignment() {
- return false;
- }
-
- let fields = if let Ok(fields) = get_fields(u.fields, finder) {
- fields
- } else {
- return false;
- };
-
- for field in fields {
- match field {
- TypeData::Member(m) => {
- // - no private data members
- // - no protected data members
- if m.attributes.access() == 1 || m.attributes.access() == 2 {
- return false;
- }
- }
- TypeData::OverloadedMethod(m) => {
- match finder.find(m.method_list).and_then(|l| l.parse()) {
- Ok(TypeData::MethodList(list)) => {
- for m in list.methods {
- // - no virtual functions
- if m.attributes.is_virtual() {
- return false;
- }
- }
- }
- _ => return false,
- }
- }
- TypeData::Method(m) => {
- // - no virtual functions
- if m.attributes.is_virtual() {
- return false;
- }
- }
- // - no base classes
- TypeData::BaseClass(_) => return false,
- TypeData::VirtualBaseClass(_) => return false,
- TypeData::VirtualFunctionTable(_) => return false,
- TypeData::VirtualTableShape(_) => return false,
- TypeData::VirtualFunctionTablePointer(_) => return false,
- _ => {}
- }
- }
- return true;
- }
- _ => false,
- }
- }
-}
diff --git a/rust/examples/basic_script/src/main.rs b/rust/examples/simple.rs
index c5c1414a..f41cbf34 100644
--- a/rust/examples/basic_script/src/main.rs
+++ b/rust/examples/simple.rs
@@ -1,9 +1,11 @@
use binaryninja::architecture::Architecture;
-use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt};
+use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt};
fn main() {
- println!("Loading plugins..."); // This loads all the core architecture, platform, etc plugins
- let headless_session = binaryninja::headless::Session::new();
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
println!("Loading binary...");
let bv = headless_session
@@ -15,19 +17,21 @@ fn main() {
println!("Function count: {}", bv.functions().len());
for func in &bv.functions() {
- println!(" `{}`:", func.symbol().full_name());
+ println!("{}:", func.symbol().full_name());
for basic_block in &func.basic_blocks() {
// TODO : This is intended to be refactored to be more nice to work with soon(TM)
for addr in basic_block.as_ref() {
- print!(" {} ", addr);
if let Some((_, tokens)) = func.arch().instruction_text(
bv.read_buffer(addr, func.arch().max_instr_len())
.unwrap()
.get_data(),
addr,
) {
- tokens.iter().for_each(|token| print!("{}", token.text()));
- println!();
+ let line = tokens
+ .iter()
+ .map(|token| token.to_string())
+ .collect::<String>();
+ println!("{addr} {line}");
}
}
}
diff --git a/rust/examples/template/Cargo.toml b/rust/examples/template/Cargo.toml
deleted file mode 100644
index f571c46d..00000000
--- a/rust/examples/template/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "template"
-version = "0.1.0"
-edition = "2021"
-
-# Uncomment this if you're writing a plugin (plugins are shared objects loaded by the core):
-# [lib]
-# crate-type = ["cdylib"]
-
-# You can point at the BinaryNinja dependency in one of two ways, via path:
-[dependencies]
-binaryninja = {path="../../"}
-
-# Or directly at the git repo:
-# [dependencies]
-# binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
diff --git a/rust/examples/template/README.md b/rust/examples/template/README.md
deleted file mode 100644
index 1466ef24..00000000
--- a/rust/examples/template/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Template
-
-[The only official method of providing linker arguments to a crate is through that crate's `build.rs`](https://github.com/rust-lang/cargo/issues/9554), thus this template.
-
-Please see `Cargo.toml` for further configuration options.
-
-## Plugins
-
-Enable
-```
-[lib]
-crate-type = ["cdylib"]
-```
-in `Cargo.toml`.
-
-## Standalone executables
-
-All standalone executables should call both `binaryninja::headless::init()` and `binaryninja::headless::shutdown()` (see [`src/main.rs`](src/main.rs)).
-Standalone executables will fail to link if you do not provide a `build.rs`. The one provided here should work.
diff --git a/rust/examples/template/build.rs b/rust/examples/template/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/template/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/template/src/main.rs b/rust/examples/template/src/main.rs
deleted file mode 100644
index 4a1bab2c..00000000
--- a/rust/examples/template/src/main.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use binaryninja::architecture::Architecture;
-use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt};
-
-// Standalone executables need to provide a main function for rustc
-// Plugins should refer to `binaryninja::command::*` for the various registration callbacks.
-fn main() {
- // This loads all the core architecture, platform, etc plugins
- // Standalone executables probably need to call this, but plugins do not
- println!("Loading plugins...");
- let headless_session = binaryninja::headless::Session::new();
-
- // Your code here...
- println!("Loading binary...");
- let bv = headless_session
- .load("/bin/cat")
- .expect("Couldn't open `/bin/cat`");
-
- println!("Filename: `{}`", bv.file().filename());
- println!("File size: `{:#x}`", bv.len());
- println!("Function count: {}", bv.functions().len());
-
- for func in &bv.functions() {
- println!(" `{}`:", func.symbol().full_name());
- for basic_block in &func.basic_blocks() {
- // TODO : This is intended to be refactored to be more nice to work with soon(TM)
- for addr in basic_block.as_ref() {
- print!(" {} ", addr);
- if let Some((_, tokens)) = func.arch().instruction_text(
- bv.read_buffer(addr, func.arch().max_instr_len())
- .unwrap()
- .get_data(),
- addr,
- ) {
- tokens.iter().for_each(|token| print!("{}", token.text()));
- println!();
- }
- }
- }
- }
-}
diff --git a/rust/examples/test_demangler/Cargo.toml b/rust/examples/test_demangler/Cargo.toml
deleted file mode 100644
index eb4daa34..00000000
--- a/rust/examples/test_demangler/Cargo.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[package]
-name = "test_demangler"
-version = "0.1.0"
-edition = "2021"
-
-# Uncomment this if you're writing a plugin (plugins are shared objects loaded by the core):
-[lib]
-crate-type = ["cdylib"]
-
-# You can point at the BinaryNinja dependency in one of two ways, via path:
-[dependencies]
-binaryninja = {path="../../"}
-log = "0.4.21"
-
-# Or directly at the git repo:
-# [dependencies]
-# binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
diff --git a/rust/examples/test_demangler/src/lib.rs b/rust/examples/test_demangler/src/lib.rs
deleted file mode 100644
index fc94ede9..00000000
--- a/rust/examples/test_demangler/src/lib.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use log::{info, LevelFilter};
-use binaryninja::architecture::CoreArchitecture;
-use binaryninja::binaryview::BinaryView;
-use binaryninja::command;
-use binaryninja::command::Command;
-use binaryninja::demangle::{Demangler, CustomDemangler};
-use binaryninja::logger::Logger;
-use binaryninja::rc::Ref;
-use binaryninja::types::{QualifiedName, Type};
-
-struct TestDemangler;
-
-impl CustomDemangler for TestDemangler {
- fn is_mangled_string(&self, name: &str) -> bool {
- name == "test_name" || name == "test_name2"
- }
-
- fn demangle(&self, _arch: &CoreArchitecture, name: &str, _view: Option<Ref<BinaryView>>) -> Result<(Option<Ref<Type>>, QualifiedName), ()> {
- match name {
- "test_name" => Ok((Some(Type::bool()), QualifiedName::from(vec!["test_name"]))),
- "test_name2" => Ok((None, QualifiedName::from(vec!["test_name2", "aaa"]))),
- _ => Err(()),
- }
-
- }
-}
-
-struct DemangleCommand;
-
-impl Command for DemangleCommand {
- fn action(&self, view: &BinaryView) {
- for d in Demangler::list().iter() {
- info!("{}", d.name());
-
- info!("{}", d.is_mangled_string("__ZN1AC2Ei"));
- info!("{:?}", d.demangle(
- &CoreArchitecture::by_name("x86_64").expect("x86 exists"),
- "__ZN1AC2Ei",
- Some(view)
- ));
- info!("{:?}", d.demangle(
- &CoreArchitecture::by_name("x86_64").expect("x86 exists"),
- "test_name",
- None
- ));
- info!("{:?}", d.demangle(
- &CoreArchitecture::by_name("x86_64").expect("x86 exists"),
- "test_name2",
- None
- ));
- }
- }
-
- fn valid(&self, _view: &BinaryView) -> bool {
- true
- }
-}
-
-#[no_mangle]
-pub extern "C" fn CorePluginInit() -> bool {
- Logger::new("Demangle Test").with_level(LevelFilter::Info).init();
- Demangler::register("Test", TestDemangler {});
- command::register("Demangle Test", "Test", DemangleCommand {});
- true
-}
diff --git a/rust/examples/type_printer.rs b/rust/examples/type_printer.rs
new file mode 100644
index 00000000..ea9c6a4d
--- /dev/null
+++ b/rust/examples/type_printer.rs
@@ -0,0 +1,39 @@
+use binaryninja::type_printer::{CoreTypePrinter, TokenEscapingType};
+use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureMember, Type};
+
+fn main() {
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
+
+ println!("Loading binary...");
+ let bv = headless_session
+ .load("/bin/cat")
+ .expect("Couldn't open `/bin/cat`");
+
+ let type_printer = CoreTypePrinter::default();
+ let my_structure = Type::structure(
+ &Structure::builder()
+ .insert_member(
+ StructureMember::new(
+ Type::int(4, false).into(),
+ "my_field".to_string(),
+ 0,
+ MemberAccess::PublicAccess,
+ MemberScope::NoScope,
+ ),
+ false,
+ )
+ .finalize(),
+ );
+
+ let printed_types = type_printer.print_all_types(
+ [("my_struct", my_structure)],
+ &bv,
+ 4,
+ TokenEscapingType::NoTokenEscapingType,
+ );
+
+ println!("{}", printed_types.unwrap());
+}
diff --git a/rust/examples/workflow.rs b/rust/examples/workflow.rs
new file mode 100644
index 00000000..59678cad
--- /dev/null
+++ b/rust/examples/workflow.rs
@@ -0,0 +1,89 @@
+use binaryninja::binary_view::BinaryViewExt;
+use binaryninja::low_level_il::expression::{ExpressionHandler, LowLevelILExpressionKind};
+use binaryninja::low_level_il::instruction::InstructionHandler;
+use binaryninja::low_level_il::VisitorAction;
+use binaryninja::workflow::{Activity, AnalysisContext, Workflow};
+
+const RUST_ACTIVITY_NAME: &str = "analysis.plugins.rustexample";
+const RUST_ACTIVITY_CONFIG: &str = r#"{
+ "name": "analysis.plugins.rustexample",
+ "title" : "Rust Example",
+ "description": "This analysis step logs out some information about the function...",
+ "eligibility": {
+ "auto": { "default": true },
+ "runOnce": false
+ }
+}"#;
+
+fn example_activity(analysis_context: &AnalysisContext) {
+ let func = analysis_context.function();
+ println!(
+ "Activity `{}` called in function {} with workflow {:?}!",
+ RUST_ACTIVITY_NAME,
+ func.start(),
+ func.workflow().map(|wf| wf.name())
+ );
+ // If we have llil available, replace that as well.
+ if let Some(llil) = unsafe { analysis_context.llil_function() } {
+ for basic_block in &func.basic_blocks() {
+ for instr in basic_block.iter() {
+ if let Some(llil_instr) = llil.instruction_at(instr) {
+ llil_instr.visit_tree(&mut |expr| {
+ if let LowLevelILExpressionKind::Const(_op) = expr.kind() {
+ // Replace all consts with 0x1337.
+ println!("Replacing llil expression @ 0x{:x} : {}", instr, expr.index);
+ unsafe {
+ llil.replace_expression(expr.index, llil.const_int(4, 0x1337))
+ };
+ }
+ VisitorAction::Descend
+ });
+ }
+ }
+ }
+ analysis_context.set_lifted_il_function(&llil);
+ }
+}
+
+pub fn main() {
+ println!("Starting session...");
+ // This loads all the core architecture, platform, etc plugins
+ let headless_session =
+ binaryninja::headless::Session::new().expect("Failed to initialize session");
+
+ println!("Registering workflow...");
+ let old_meta_workflow = Workflow::instance("core.function.metaAnalysis");
+ let meta_workflow = old_meta_workflow.clone("core.function.metaAnalysis");
+ let activity = Activity::new_with_action(RUST_ACTIVITY_CONFIG, example_activity);
+ meta_workflow.register_activity(&activity).unwrap();
+ meta_workflow.insert("core.function.runFunctionRecognizers", [RUST_ACTIVITY_NAME]);
+ // Re-register the meta workflow with our changes.
+ meta_workflow.register().unwrap();
+
+ println!("Loading binary...");
+ let bv = headless_session
+ .load("/bin/cat")
+ .expect("Couldn't open `/bin/cat`");
+
+ // traverse all llil expressions and look for the constant 0x1337
+ for func in &bv.functions() {
+ if let Ok(llil) = func.low_level_il() {
+ for block in &llil.basic_blocks() {
+ for instr in block.iter() {
+ instr.visit_tree(&mut |expr| {
+ if let LowLevelILExpressionKind::Const(value) = expr.kind() {
+ if value.value() == 0x1337 {
+ println!(
+ "Found constant 0x1337 at instruction 0x{:x} in function {}",
+ instr.address(),
+ func.start()
+ );
+ }
+ }
+ VisitorAction::Descend
+ });
+ }
+ }
+ }
+ }
+}
diff --git a/rust/examples/workflow/Cargo.toml b/rust/examples/workflow/Cargo.toml
deleted file mode 100644
index 4c2309ca..00000000
--- a/rust/examples/workflow/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "workflow"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-binaryninja = { path="../../" }
-log = "0.4" \ No newline at end of file
diff --git a/rust/examples/workflow/build.rs b/rust/examples/workflow/build.rs
deleted file mode 100644
index 5ba9bcde..00000000
--- a/rust/examples/workflow/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/workflow/src/lib.rs b/rust/examples/workflow/src/lib.rs
deleted file mode 100644
index 6f415941..00000000
--- a/rust/examples/workflow/src/lib.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use binaryninja::llil::{
- ExprInfo, LiftedNonSSA, NonSSA, VisitorAction,
-};
-use binaryninja::workflow::{Activity, AnalysisContext, Workflow};
-use log::LevelFilter;
-use binaryninja::logger::Logger;
-
-const RUST_ACTIVITY_NAME: &'static str = "analysis.plugins.rustexample";
-const RUST_ACTIVITY_CONFIG: &'static str = r#"{
- "name": "analysis.plugins.rustexample",
- "title" : "Rust Example",
- "description": "This analysis step logs out some information about the function...",
- "eligibility": {
- "auto": { "default": true },
- "runOnce": false
- }
-}"#;
-
-fn example_activity(analysis_context: &AnalysisContext) {
- let func = analysis_context.function();
- log::info!(
- "Activity `{}` called in function {} with workflow {:?}!",
- RUST_ACTIVITY_NAME,
- func.start(),
- func.workflow().map(|wf| wf.name())
- );
- // If we have llil available, replace that as well.
- if let Some(llil) = unsafe { analysis_context.llil_function::<NonSSA<LiftedNonSSA>>() } {
- for basic_block in &func.basic_blocks() {
- for instr in basic_block.iter() {
- if let Some(llil_instr) = llil.instruction_at(instr) {
- llil_instr.visit_tree(&mut |expr, info| {
- match info {
- ExprInfo::Const(_op) => {
- // Replace all consts with 0x1337.
- log::info!(
- "Replacing llil expression @ 0x{:x} : {}",
- instr,
- expr.index()
- );
- unsafe {
- llil.replace_expression(expr.index(), llil.const_int(4, 0x1337))
- };
- }
- _ => {}
- }
- VisitorAction::Descend
- });
- }
- }
- }
- analysis_context.set_lifted_il_function(&llil);
- }
-}
-
-#[no_mangle]
-#[allow(non_snake_case)]
-pub extern "C" fn CorePluginInit() -> bool {
- Logger::new("Workflow Example").with_level(LevelFilter::Info).init();
-
- log::info!("Initialized the plugin");
-
- let meta_workflow = Workflow::new_from_copy("core.function.metaAnalysis");
- let activity = Activity::new_with_action(RUST_ACTIVITY_CONFIG, example_activity);
- meta_workflow.register_activity(&activity).unwrap();
- meta_workflow.insert("core.function.runFunctionRecognizers", [RUST_ACTIVITY_NAME]);
- // Re-register the meta workflow with our changes.
- meta_workflow.register().unwrap();
- true
-}