summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyleMiles <krm504@nyu.edu>2021-05-19 20:40:24 -0400
committerKyleMiles <krm504@nyu.edu>2021-07-07 23:27:29 -0400
commitd19f5e85ee26c3d6604798d30ab821d15731cada (patch)
tree9851b7f6bf5589df69508d4489c11148515cb550
parent0f58b0742e3010eee6bbd2e15f4dd4930ec0d822 (diff)
DebugInfo - plugable debug information importers - C++, Rust, and Python APIs
-rw-r--r--binaryninjaapi.h90
-rw-r--r--binaryninjacore.h56
-rw-r--r--debuginfo.cpp249
-rw-r--r--python/__init__.py1
-rw-r--r--python/binaryview.py29
-rw-r--r--python/debuginfo.py426
-rwxr-xr-xpython/examples/debug_info.py269
-rwxr-xr-xpython/examples/test_debug_infobin0 -> 14336 bytes
-rw-r--r--rust/src/binaryview.rs13
-rw-r--r--rust/src/debuginfo.rs657
-rw-r--r--rust/src/lib.rs1
-rw-r--r--rust/src/string.rs8
-rw-r--r--rust/src/types.rs157
13 files changed, 1939 insertions, 17 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 5c3e0cde..6d4119ca 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -1301,6 +1301,17 @@ __attribute__ ((format (printf, 1, 2)))
bool autoDiscovered;
};
+ struct DataVariableAndName
+ {
+ DataVariableAndName() { }
+ DataVariableAndName(uint64_t a, Type* t, bool d, const std::string& n) : address(a), type(t), autoDiscovered(d), name(n) { }
+
+ uint64_t address;
+ Confidence<Ref<Type>> type;
+ bool autoDiscovered;
+ std::string name;
+ };
+
class TagType: public CoreRefCountObject<BNTagType, BNNewTagTypeReference, BNFreeTagType>
{
public:
@@ -5623,8 +5634,85 @@ __attribute__ ((format (printf, 1, 2)))
uint64_t findConstant;
DataBuffer findBuffer;
-
+
std::vector<BNAddressRange> ranges;
uint64_t totalLength;
};
+
+ struct DebugFunctionInfo
+ {
+ std::string shortName;
+ std::string fullName;
+ std::string rawName;
+ uint64_t address;
+ Ref<Type> returnType;
+ std::vector<std::tuple<std::string, Ref<Type>>> parameters;
+ bool variableParameters;
+ Ref<CallingConvention> callingConvention;
+ Ref<Platform> platform;
+
+ DebugFunctionInfo(std::string shortName,
+ std::string fullName,
+ std::string rawName,
+ uint64_t address,
+ Ref<Type> returnType,
+ std::vector<std::tuple<std::string, Ref<Type>>> parameters,
+ bool variableParameters,
+ Ref<CallingConvention> callingConvention,
+ Ref<Platform> platform) :
+ shortName(shortName),
+ fullName(fullName),
+ rawName(rawName),
+ address(address),
+ returnType(returnType),
+ parameters(parameters),
+ variableParameters(variableParameters),
+ callingConvention(callingConvention),
+ platform(platform)
+ {}
+ };
+
+ class DebugInfo : public CoreRefCountObject<BNDebugInfo, BNNewDebugInfoReference, BNFreeDebugInfoReference>
+ {
+ public:
+ DebugInfo(BNDebugInfo* debugInfo);
+
+ std::vector<NameAndType> GetTypes(const std::string& parserName = "");
+ std::vector<DebugFunctionInfo> GetFunctions(const std::string& parserName = "");
+ std::vector<DataVariableAndName> GetDataVariables(const std::string& parserName = "");
+
+ bool AddType(const std::string& name, Ref<Type> type);
+ bool AddFunction(const DebugFunctionInfo& function);
+ bool AddDataVariable(uint64_t address, Ref<Type> type, const std::string& name = "");
+ };
+
+ class DebugInfoParser : public CoreRefCountObject<BNDebugInfoParser, BNNewDebugInfoParserReference, BNFreeDebugInfoParserReference>
+ {
+ public:
+ DebugInfoParser(BNDebugInfoParser* parser);
+
+ static Ref<DebugInfoParser> GetByName(const std::string& name);
+ static std::vector<Ref<DebugInfoParser>> GetList();
+ static std::vector<Ref<DebugInfoParser>> GetListForView(const Ref<BinaryView> data);
+
+ std::string GetName() const;
+ Ref<DebugInfo> Parse(Ref<BinaryView> view, Ref<DebugInfo> existingDebugInfo = nullptr) const;
+
+ bool IsValidForView(const Ref<BinaryView> view) const;
+ };
+
+ class CustomDebugInfoParser : public DebugInfoParser
+ {
+ static bool IsValidCallback(void* ctxt, BNBinaryView* view);
+ static void ParseCallback(void* ctxt, BNDebugInfo* debugInfo, BNBinaryView* view);
+ BNDebugInfoParser* Register(const std::string& name);
+
+ public:
+ CustomDebugInfoParser(const std::string& name);
+ virtual ~CustomDebugInfoParser() {}
+
+ virtual bool IsValid(Ref<BinaryView>) = 0;
+ virtual void ParseInfo(Ref<DebugInfo>, Ref<BinaryView>) = 0;
+ };
}
+
diff --git a/binaryninjacore.h b/binaryninjacore.h
index def57659..0f6ac60c 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -106,7 +106,9 @@
#define BN_FULL_CONFIDENCE 255
#define BN_MINIMUM_CONFIDENCE 1
+#define BN_DEFAULT_CONFIDENCE 96
#define BN_HEURISTIC_CONFIDENCE 192
+#define BN_DEBUGINFO_CONFIDENCE 200
#define DEFAULT_INTERNAL_NAMESPACE "BNINTERNALNAMESPACE"
#define DEFAULT_EXTERNAL_NAMESPACE "BNEXTERNALNAMESPACE"
@@ -208,6 +210,8 @@ extern "C"
struct BNDisassemblyTextRenderer;
struct BNLinearViewObject;
struct BNLinearViewCursor;
+ struct BNDebugInfo;
+ struct BNDebugInfoParser;
//! Console log levels
@@ -940,6 +944,15 @@ extern "C"
uint8_t typeConfidence;
};
+ struct BNDataVariableAndName
+ {
+ uint64_t address;
+ BNType* type;
+ char* name;
+ bool autoDiscovered;
+ uint8_t typeConfidence;
+ };
+
enum BNMediumLevelILOperation
{
MLIL_NOP,
@@ -2570,6 +2583,21 @@ extern "C"
AllowDeadStoreElimination
};
+ struct BNDebugFunctionInfo
+ {
+ char* shortName;
+ char* fullName;
+ char* rawName;
+ uint64_t address;
+ BNType* returnType;
+ char** parameterNames;
+ BNType** parameterTypes;
+ size_t parameterCount;
+ bool variableParameters;
+ BNCallingConvention* callingConvention;
+ BNPlatform* platform;
+ };
+
BINARYNINJACOREAPI char* BNAllocString(const char* contents);
BINARYNINJACOREAPI void BNFreeString(char* str);
BINARYNINJACOREAPI char** BNAllocStringList(const char** contents, size_t size);
@@ -3673,6 +3701,7 @@ __attribute__ ((format (printf, 1, 2)))
BINARYNINJACOREAPI void BNUndefineUserDataVariable(BNBinaryView* view, uint64_t addr);
BINARYNINJACOREAPI BNDataVariable* BNGetDataVariables(BNBinaryView* view, size_t* count);
BINARYNINJACOREAPI void BNFreeDataVariables(BNDataVariable* vars, size_t count);
+ BINARYNINJACOREAPI void BNFreeDataVariablesAndName(BNDataVariableAndName* vars, size_t count);
BINARYNINJACOREAPI bool BNGetDataVariableAtAddress(BNBinaryView* view, uint64_t addr, BNDataVariable* var);
BINARYNINJACOREAPI bool BNParseTypeString(BNBinaryView* view, const char* text,
@@ -3956,6 +3985,10 @@ __attribute__ ((format (printf, 1, 2)))
BINARYNINJACOREAPI void BNDefineImportedFunction(BNBinaryView* view, BNSymbol* importAddressSym, BNFunction* func, BNType* type);
BINARYNINJACOREAPI BNSymbol* BNDefineAutoSymbolAndVariableOrFunction(BNBinaryView* view, BNPlatform* platform, BNSymbol* sym, BNType* type);
+ BINARYNINJACOREAPI BNDebugInfo* BNGetDebugInfo(BNBinaryView* view);
+ BINARYNINJACOREAPI void BNApplyDebugInfo(BNBinaryView* view, BNDebugInfo* newDebugInfo);
+ BINARYNINJACOREAPI void BNSetDebugInfo(BNBinaryView* view, BNDebugInfo* newDebugInfo);
+
BINARYNINJACOREAPI BNSymbol* BNImportedFunctionFromImportAddressSymbol(BNSymbol* sym, uint64_t addr);
// Low-level IL
@@ -5189,6 +5222,29 @@ __attribute__ ((format (printf, 1, 2)))
BINARYNINJACOREAPI char** BNRustSimplifyStrToFQN(const char* const, bool);
BINARYNINJACOREAPI char* BNRustSimplifyStrToStr(const char* const);
+ BINARYNINJACOREAPI BNDebugInfoParser* BNRegisterDebugInfoParser(const char* name, bool (*isValid)(void*, BNBinaryView*), void (*parseInfo)(void*, BNDebugInfo*, BNBinaryView*), void* context);
+ BINARYNINJACOREAPI void BNUnregisterDebugInfoParser(const char* rawName);
+ BINARYNINJACOREAPI BNDebugInfoParser* BNGetDebugInfoParserByName(const char* name);
+ BINARYNINJACOREAPI BNDebugInfoParser** BNGetDebugInfoParsers(size_t* count);
+ BINARYNINJACOREAPI BNDebugInfoParser** BNGetDebugInfoParsersForView(BNBinaryView* view, size_t* count);
+ BINARYNINJACOREAPI char* BNGetDebugInfoParserName(BNDebugInfoParser* parser);
+ BINARYNINJACOREAPI bool BNIsDebugInfoParserValidForView(BNDebugInfoParser* parser, BNBinaryView* view);
+ BINARYNINJACOREAPI BNDebugInfo* BNParseDebugInfo(BNDebugInfoParser* parser, BNBinaryView* view, BNDebugInfo* existingDebugInfo);
+ BINARYNINJACOREAPI BNDebugInfoParser* BNNewDebugInfoParserReference(BNDebugInfoParser* parser);
+ BINARYNINJACOREAPI void BNFreeDebugInfoParserReference(BNDebugInfoParser* parser);
+ BINARYNINJACOREAPI void BNFreeDebugInfoParserList(BNDebugInfoParser** parsers, size_t count);
+
+ BINARYNINJACOREAPI BNDebugInfo* BNNewDebugInfoReference(BNDebugInfo* debugInfo);
+ BINARYNINJACOREAPI void BNFreeDebugInfoReference(BNDebugInfo* debugInfo);
+ BINARYNINJACOREAPI bool BNAddDebugType(BNDebugInfo* const debugInfo, const char* const name, const BNType* const type);
+ BINARYNINJACOREAPI BNNameAndType* BNGetDebugTypes(BNDebugInfo* const debugInfo, const char* const name, size_t* count);
+ BINARYNINJACOREAPI void BNFreeDebugTypes(BNNameAndType* types, size_t count);
+ BINARYNINJACOREAPI bool BNAddDebugFunction(BNDebugInfo* const debugInfo, BNDebugFunctionInfo* func);
+ BINARYNINJACOREAPI BNDebugFunctionInfo* BNGetDebugFunctions(BNDebugInfo* const debugInfo, const char* const name, size_t* count);
+ BINARYNINJACOREAPI void BNFreeDebugFunctions(BNDebugFunctionInfo* functions, size_t count);
+ BINARYNINJACOREAPI bool BNAddDebugDataVariable(BNDebugInfo* const debugInfo, uint64_t address, const BNType* const type, const char* name);
+ BINARYNINJACOREAPI BNDataVariableAndName* BNGetDebugDataVariables(BNDebugInfo* const debugInfo, const char* const name, size_t* count);
+
#ifdef __cplusplus
}
#endif
diff --git a/debuginfo.cpp b/debuginfo.cpp
new file mode 100644
index 00000000..2bdc2e47
--- /dev/null
+++ b/debuginfo.cpp
@@ -0,0 +1,249 @@
+// Copyright (c) 2015-2021 Vector 35 Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+
+// TODO : Documentation
+
+
+#include "binaryninjaapi.h"
+using namespace BinaryNinja;
+using namespace std;
+
+
+///////////////
+// DebugInfo //
+///////////////
+
+
+DebugInfo::DebugInfo(BNDebugInfo* debugInfo)
+{
+ m_object = debugInfo;
+}
+
+
+vector<NameAndType> DebugInfo::GetTypes(const string& parserName)
+{
+ size_t count;
+ BNNameAndType* nameAndTypes = BNGetDebugTypes(m_object, parserName.size() == 0 ? nullptr : parserName.c_str(), &count);
+
+ vector<NameAndType> result;
+ for (size_t i = 0; i < count; ++i)
+ {
+ result.emplace_back(nameAndTypes[i].name,
+ Confidence<Ref<Type>>(
+ new Type(BNNewTypeReference(nameAndTypes[i].type)),
+ nameAndTypes[i].typeConfidence));
+ }
+
+ BNFreeDebugTypes(nameAndTypes, count);
+ return result;
+}
+
+
+vector<DebugFunctionInfo> DebugInfo::GetFunctions(const string& parserName)
+{
+ size_t count;
+ BNDebugFunctionInfo* functions = BNGetDebugFunctions(m_object, parserName.size() == 0 ? nullptr : parserName.c_str(), &count);
+
+ vector<DebugFunctionInfo> result;
+ for (size_t i = 0; i < count; ++i)
+ {
+ vector<tuple<string, Ref<Type>>> parameters;
+ for (size_t j = 0; j < functions[i].parameterCount; ++j)
+ parameters.emplace_back(functions[i].parameterNames[j], new Type(BNNewTypeReference(functions[i].parameterTypes[j])));
+
+ result.emplace_back(functions[i].shortName ? functions[i].shortName : "",
+ functions[i].fullName ? functions[i].fullName : "",
+ functions[i].rawName ? functions[i].rawName : "",
+ functions[i].address,
+ functions[i].returnType ? new Type(BNNewTypeReference(functions[i].returnType)) : nullptr,
+ parameters,
+ functions[i].variableParameters,
+ functions[i].callingConvention ? new CoreCallingConvention(BNNewCallingConventionReference(functions[i].callingConvention)) : nullptr,
+ functions[i].platform ? new Platform(BNNewPlatformReference(functions[i].platform)) : nullptr);
+ }
+
+ BNFreeDebugFunctions(functions, count);
+ return result;
+}
+
+
+vector<DataVariableAndName> DebugInfo::GetDataVariables(const string& parserName)
+{
+ size_t count;
+ BNDataVariableAndName* variableAndNames = BNGetDebugDataVariables(m_object, parserName.size() == 0 ? nullptr : parserName.c_str(), &count);
+
+ vector<DataVariableAndName> result;
+ for (size_t i = 0; i < count; ++i)
+ {
+ result.emplace_back(variableAndNames[i].address,
+ Confidence(new Type(BNNewTypeReference(variableAndNames[i].type)), variableAndNames[i].typeConfidence),
+ variableAndNames[i].autoDiscovered,
+ variableAndNames[i].name);
+ }
+
+ BNFreeDataVariablesAndName(variableAndNames, count);
+ return result;
+}
+
+
+bool DebugInfo::AddType(const string& name, Ref<Type> type)
+{
+ return BNAddDebugType(m_object, name.c_str(), type->GetObject());
+}
+
+
+bool DebugInfo::AddFunction(const DebugFunctionInfo& function)
+{
+ BNDebugFunctionInfo* input = new BNDebugFunctionInfo();
+
+ input->shortName = function.shortName.size() ? BNAllocString(function.shortName.c_str()) : nullptr;
+ input->fullName = function.fullName.size() ? BNAllocString(function.fullName.c_str()) : nullptr;
+ input->rawName = function.rawName.size() ? BNAllocString(function.rawName.c_str()) : nullptr;
+ input->address = function.address;
+ input->returnType = function.returnType ? function.returnType->GetObject() : nullptr;
+ input->variableParameters = function.variableParameters;
+ input->callingConvention = function.callingConvention ? function.callingConvention->GetObject() : nullptr;
+ input->platform = function.platform ? function.platform->GetObject() : nullptr;
+
+ size_t parameterCount = function.parameters.size();
+ input->parameterCount = parameterCount;
+ input->parameterNames = new char*[parameterCount];
+ input->parameterTypes = new BNType*[parameterCount];
+ for (size_t i = 0; i < parameterCount; ++i)
+ {
+ input->parameterNames[i] = BNAllocString(std::get<0>(function.parameters[i]).c_str());
+ input->parameterTypes[i] = std::get<1>(function.parameters[i])->GetObject();
+ }
+
+ bool result = BNAddDebugFunction(m_object, input);
+
+ BNFreeString(input->shortName);
+ BNFreeString(input->fullName);
+ BNFreeString(input->rawName);
+
+ for (size_t i = 0; i < parameterCount; ++i)
+ BNFreeString(input->parameterNames[i]);
+
+ return result;
+}
+
+
+bool DebugInfo::AddDataVariable(uint64_t address, Ref<Type> type, const string& name)
+{
+ if (name.size() == 0)
+ return BNAddDebugDataVariable(m_object, address, type->GetObject(), nullptr);
+ return BNAddDebugDataVariable(m_object, address, type->GetObject(), name.c_str());
+}
+
+
+/////////////////////
+// DebugInfoParser //
+/////////////////////
+
+
+DebugInfoParser::DebugInfoParser(BNDebugInfoParser* parser)
+{
+ m_object = parser;
+}
+
+
+Ref<DebugInfoParser> DebugInfoParser::GetByName(const string& name)
+{
+ BNDebugInfoParser* parser = BNGetDebugInfoParserByName(name.c_str());
+ if (parser)
+ return new DebugInfoParser(BNNewDebugInfoParserReference(parser));
+ return nullptr;
+}
+
+
+vector<Ref<DebugInfoParser>> DebugInfoParser::GetList()
+{
+ size_t count = 0;
+ BNDebugInfoParser** parsers = BNGetDebugInfoParsers(&count);
+
+ vector<Ref<DebugInfoParser>> result;
+ for (size_t i = 0; i < count; ++i)
+ {
+ result.emplace_back(new DebugInfoParser(BNNewDebugInfoParserReference(parsers[i])));
+ }
+
+ BNFreeDebugInfoParserList(parsers, count);
+ return result;
+}
+
+
+vector<Ref<DebugInfoParser>> DebugInfoParser::GetListForView(const Ref<BinaryView> data)
+{
+ size_t count = 0;
+ BNDebugInfoParser** parsers = BNGetDebugInfoParsersForView(data->GetObject(), &count);
+
+ vector<Ref<DebugInfoParser>> result;
+ for (size_t i = 0; i < count; ++i)
+ {
+ result.emplace_back(new DebugInfoParser(BNNewDebugInfoParserReference(parsers[i])));
+ }
+
+ BNFreeDebugInfoParserList(parsers, count);
+ return result;
+}
+
+
+string DebugInfoParser::GetName() const
+{
+ return BNGetDebugInfoParserName(m_object);
+}
+
+
+Ref<DebugInfo> DebugInfoParser::Parse(Ref<BinaryView> view, Ref<DebugInfo> existingDebugInfo) const
+{
+ if (existingDebugInfo)
+ return new DebugInfo(BNNewDebugInfoReference(BNParseDebugInfo(m_object, view->GetObject(), existingDebugInfo->GetObject())));
+ return new DebugInfo(BNParseDebugInfo(m_object, view->GetObject(), nullptr));
+}
+
+
+bool DebugInfoParser::IsValidForView(const Ref<BinaryView> view) const
+{
+ return BNIsDebugInfoParserValidForView(m_object, view->GetObject());
+}
+
+
+//////////////////////////////
+// Plugin registration APIs //
+//////////////////////////////
+
+
+bool CustomDebugInfoParser::IsValidCallback(void* ctxt, BNBinaryView* view)
+{
+ CustomDebugInfoParser* parser = (CustomDebugInfoParser*)ctxt;
+ return parser->IsValid(new BinaryView(view));
+}
+
+
+void CustomDebugInfoParser::ParseCallback(void* ctxt, BNDebugInfo* debugInfo, BNBinaryView* view)
+{
+ CustomDebugInfoParser* parser = (CustomDebugInfoParser*)ctxt;
+ parser->ParseInfo(new DebugInfo(debugInfo), new BinaryView(view));
+}
+
+
+CustomDebugInfoParser::CustomDebugInfoParser(const string& name) :
+ DebugInfoParser(BNNewDebugInfoParserReference(BNRegisterDebugInfoParser(name.c_str(), IsValidCallback, ParseCallback, this))) {}
diff --git a/python/__init__.py b/python/__init__.py
index 1b199c9a..4f8a373f 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -36,6 +36,7 @@ from binaryninja.databuffer import *
from binaryninja.filemetadata import *
from binaryninja.fileaccessor import *
from binaryninja.binaryview import *
+from binaryninja.debuginfo import *
from binaryninja.transform import *
from binaryninja.architecture import *
from binaryninja.basicblock import *
diff --git a/python/binaryview.py b/python/binaryview.py
index 30b763a0..ae88b4c0 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -28,6 +28,7 @@ import abc
import numbers
import json
import inspect
+from typing import Union
from collections import defaultdict, OrderedDict
@@ -500,6 +501,15 @@ class DataVariable(object):
self._view = value
+class DataVariableAndName(DataVariable):
+ def __init__(self, addr: int, var_type: types.Type, var_name: str, auto_discovered: bool, view: "BinaryView" = None) -> None:
+ super(DataVariableAndName, self).__init__(addr, var_type, auto_discovered, view)
+ self.name = var_name
+
+ def __repr__(self) -> str:
+ return "<var 0x%x: %s %s>" % (self.address, str(self.type), self.name)
+
+
class BinaryDataNotificationCallbacks(object):
def __init__(self, view, notify):
self._view = view
@@ -1483,7 +1493,6 @@ class BinaryView(object):
finally:
core.BNFreeFunctionList(funcs, count.value)
-
def __getitem__(self, i):
if isinstance(i, tuple):
result = bytes()
@@ -6251,6 +6260,24 @@ class BinaryView(object):
"""
core.BNSetGlobalCommentForAddress(self.handle, addr, comment)
+ @property
+ def debug_info(self) -> "binaryninja.debuginfo.DebugInfo":
+ """The current debug info object for this binary view"""
+ return binaryninja.debuginfo.DebugInfo(core.BNNewDebugInfoReference(core.BNGetDebugInfo(self.handle)))
+
+ @debug_info.setter
+ def debug_info(self, value: "binaryninja.debuginfo.DebugInfo") -> Union[None, 'NotImplemented']:
+ """Sets the debug info for the current binary view"""
+ if not isinstance(value, binaryninja.debuginfo.DebugInfo):
+ return NotImplemented
+ core.BNSetDebugInfo(self.handle, value.handle)
+
+ def apply_debug_info(self, value: "binaryninja.debuginfo.DebugInfo") -> Union[None, 'NotImplemented']:
+ """Sets the debug info and applies its contents to the current binary view"""
+ if not isinstance(value, binaryninja.debuginfo.DebugInfo):
+ return NotImplemented
+ core.BNApplyDebugInfo(self.handle, value.handle)
+
def query_metadata(self, key):
"""
`query_metadata` retrieves a metadata associated with the given key stored in the current BinaryView.
diff --git a/python/debuginfo.py b/python/debuginfo.py
new file mode 100644
index 00000000..15ac4efb
--- /dev/null
+++ b/python/debuginfo.py
@@ -0,0 +1,426 @@
+# Copyright (c) 2015-2021 Vector 35 Inc
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import ctypes
+from typing import Optional, List, Iterator, Callable, Tuple
+import traceback
+
+# Binary Ninja components
+import binaryninja
+from binaryninja import _binaryninjacore as core
+from binaryninja import callingconvention
+from binaryninja import platform
+from binaryninja import types
+from binaryninja import log
+
+
+_debug_info_parsers = {}
+
+
+class _DebugInfoParserMetaClass(type):
+ @property
+ def list(self) -> List["DebugInfoParser"]:
+ """List all debug-info parsers (read-only)"""
+ binaryninja._init_plugins()
+ count = ctypes.c_ulonglong()
+ parsers = core.BNGetDebugInfoParsers(count)
+ result = []
+ for i in range(0, count.value):
+ result.append(DebugInfoParser(core.BNNewDebugInfoParserReference(parsers[i])))
+ core.BNFreeDebugInfoParserList(parsers, count.value)
+ return result
+
+ def __iter__(self) -> Iterator["DebugInfoParser"]:
+ """Generator of all debug-info parsers"""
+ binaryninja._init_plugins()
+ count = ctypes.c_ulonglong()
+ parsers = core.BNGetDebugInfoParsers(count)
+ try:
+ for i in range(0, count.value):
+ yield DebugInfoParser(core.BNNewDebugInfoParserReference(parsers[i]))
+ finally:
+ core.BNFreeDebugInfoParserList(parsers, count.value)
+
+ def __getitem__(cls, value: str) -> "DebugInfoParser":
+ """Returns debug info parser of the given name, if it exists"""
+ binaryninja._init_plugins()
+ parser = core.BNGetDebugInfoParserByName(str(value))
+ if parser is None:
+ raise KeyError(f"'{str(value)}' is not a valid debug-info parser")
+ return DebugInfoParser(core.BNNewDebugInfoParserReference(parser))
+
+ @staticmethod
+ def get_parsers_for_view(view: binaryninja.binaryview.BinaryView) -> List["DebugInfoParser"]:
+ """Returns a list of debug-info parsers that are valid for the provided binary view"""
+ binaryninja._init_plugins()
+
+ count = ctypes.c_ulonglong()
+ parsers = core.BNGetDebugInfoParsersForView(view.handle, count)
+ result = []
+ for i in range(0, count.value):
+ result.append(DebugInfoParser(core.BNNewDebugInfoParserReference(parsers[i])))
+ core.BNFreeDebugInfoParserList(parsers, count.value)
+ return result
+
+ @staticmethod
+ def _is_valid(view: core.BNBinaryView, callback: Callable[[binaryninja.binaryview.BinaryView], bool]) -> bool:
+ try:
+ file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ return callback(view_obj)
+ except:
+ log.log_error(traceback.format_exc())
+
+ @staticmethod
+ def _parse_info(debug_info: core.BNDebugInfo, view: core.BNBinaryView, callback: Callable[["DebugInfo", binaryninja.binaryview.BinaryView], None]) -> None:
+ try:
+ file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ callback(DebugInfo(core.BNNewDebugInfoReference(debug_info)), view_obj)
+ except:
+ log.log_error(traceback.format_exc())
+
+ @classmethod
+ def register(cls, name: str, is_valid: Callable[[binaryninja.binaryview.BinaryView], bool], parse_info: Callable[["DebugInfo", binaryninja.binaryview.BinaryView], None]) -> "DebugInfoParser":
+ """Registers a DebugInfoParser. See ``binaryninja.debuginfo.DebugInfoParser`` for more details."""
+ binaryninja._init_plugins()
+
+ is_valid_cb = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._is_valid(view, is_valid))
+ parse_info_cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNDebugInfo), ctypes.POINTER(core.BNBinaryView))(lambda ctxt, debug_info, view: cls._parse_info(debug_info, view, parse_info))
+
+ # Don't let our callbacks get garbage collected
+ global _debug_info_parsers
+ _debug_info_parsers[len(_debug_info_parsers)] = (is_valid_cb, parse_info_cb)
+
+ return DebugInfoParser(core.BNNewDebugInfoParserReference(core.BNRegisterDebugInfoParser(name, is_valid_cb, parse_info_cb, None)))
+
+
+class DebugInfoParser(object, metaclass=_DebugInfoParserMetaClass):
+ """
+ ``DebugInfoParser``s represent the registered parsers and providers of debug information to Binary Ninja.
+
+ The debug information is used by Binary Ninja as ground-truth information about the attributes of functions,
+ types, and variables that Binary Ninja's analysis pipeline would otherwise work to deduce. By providing
+ debug info, Binary Ninja's output can be generated quicker, more accurately, and more completely.
+
+ A DebugInfoParser consists of:
+ 1. A name
+ 2. An ``is_valid`` function which takes a BV and returns a bool
+ 3. A ``parse`` function which takes a ``DebugInfo`` object and uses the member functions ``add_type``, ``add_function``, and ``add_data_variable`` to populate all the info it can.
+ And finally calling ``bn.debuginfo.DebugInfoParser.register`` to register it with the core.
+
+ Here's a minimal, complete example boilerplate-plugin:
+ ```
+ import binaryninja as bn
+
+ def is_valid(bv: bn.binaryview.BinaryView) -> bool:
+ return bv.view_type == "Raw"
+
+ def parse_info(debug_info: bn.debuginfo.DebugInfo, bv: bn.binaryview.BinaryView) -> None:
+ debug_info.add_type("name", bn.types.Type.int(4, True))
+ debug_info.add_data_variable(0x1234, bn.types.Type.int(4, True), "name")
+
+ function_info = bn.debuginfo.DebugFunctionInfo(0xdead1337, "short_name", "full_name", "raw_name", bn.types.Type.int(4, False), [])
+ debug_info.add_function(function_info)
+
+ bn.debuginfo.DebugInfoParser.register("debug info parser", is_valid, parse_info)
+ ```
+
+ ``DebugInfo`` can then be automatically applied to valid binary views (via the "Parse and Apply Debug Info" setting), or manually fetched/applied as bellow:
+ ```
+ valid_parsers = bn.debuginfo.DebugInfoParser.get_parsers_for_view(bv)
+ parser = valid_parsers[0]
+ debug_info = parser.parse_debug_info(bv)
+ bv.apply_debug_info(debug_info)
+ ```
+
+ Multiple debug-info parsers can manually contribute debug info for a binary view by simply calling ``parse_debug_info`` with the
+ ``DebugInfo`` object just returned. This is automatic when opening a binary view with multiple valid debug info parsers. If you
+ wish to set the debug info for a binary view without applying it as well, you can call ``binaryninja.binaryview.BinaryView.set_debug_info``.
+ """
+ def __init__(self, handle: core.BNDebugInfoParser) -> None:
+ self.handle = core.handle_of_type(handle, core.BNDebugInfoParser)
+
+ def __del__(self) -> None:
+ core.BNFreeDebugInfoParserReference(self.handle)
+
+ def __repr__(self) -> str:
+ return f"<debug-info parser: '{self.name}'>"
+
+ def __eq__(self, other: "DebugInfoParser") -> bool:
+ if not isinstance(other, self.__class__):
+ return NotImplemented
+ return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents)
+
+ def __ne__(self, other: "DebugInfoParser") -> bool:
+ if not isinstance(other, self.__class__):
+ return NotImplemented
+ return not (self == other)
+
+ def __hash__(self) -> int:
+ return hash(ctypes.addressof(self.handle.contents))
+
+ @property
+ def name(self) -> str:
+ """Debug-info parser's name (read-only)"""
+ return core.BNGetDebugInfoParserName(self.handle)
+
+ def is_valid_for_view(self, view: binaryninja.binaryview.BinaryView) -> bool:
+ """Returns whether this debug-info parser is valid for the provided binary view"""
+ return core.BNIsDebugInfoParserValidForView(self.handle, view.handle)
+
+ def parse_debug_info(self, view: binaryninja.binaryview.BinaryView, debug_info: Optional["DebugInfo"] = None) -> "DebugInfo":
+ """Returns a ``DebugInfo`` object populated with debug info by this debug-info parser. Only provide a ``DebugInfo`` object if you wish to append to the existing debug info"""
+ if isinstance(debug_info, DebugInfo):
+ return DebugInfo(core.BNNewDebugInfoReference(core.BNParseDebugInfo(self.handle, view.handle, debug_info.handle)))
+ else:
+ return DebugInfo(core.BNParseDebugInfo(self.handle, view.handle, None))
+
+
+class DebugFunctionInfo(object):
+ """
+ ``DebugFunctionInfo`` collates ground-truth function-external attributes for use in BinaryNinja's internal analysis.
+
+ When contributing function info, provide only what you know - BinaryNinja will figure out everything else that it can, as it usually does.
+
+ Functions will not be created if an address is not provided, but will be able to be queried from debug info for later user analysis.
+ """
+ def __init__(self, address: Optional[int] = 0, short_name: Optional[str] = None, full_name: Optional[str] = None, raw_name: Optional[str] = None, return_type: Optional[types.Type] = None, parameters: Optional[List[Tuple[str, types.Type]]] = None, variable_parameters: Optional[bool] = False, calling_convention: Optional[callingconvention.CallingConvention] = None, platform: Optional[platform.Platform] = None) -> None:
+ self._short_name = short_name
+ self._full_name = full_name
+ self._raw_name = raw_name
+ self._address = address
+ self._return_type = return_type
+ self._parameters = parameters
+ self._variable_parameters = variable_parameters
+ self._calling_convention = calling_convention
+ self._platform = platform
+
+ def __repr__(self) -> str:
+ suffix = f"@{hex(self._address)}>" if self._address != 0 else ">"
+ if self._short_name is not None:
+ return f"<debug-info function: {self._short_name}{suffix}"
+ elif self._full_name is not None:
+ return f"<debug-info function: {self._full_name}{suffix}"
+ elif self._raw_name is not None:
+ return f"<debug-info function: {self._raw_name}{suffix}"
+ else:
+ return f"<debug-info function{suffix}"
+
+ @property
+ def short_name(self) -> Optional[str]:
+ return self._short_name
+
+ @property
+ def full_name(self) -> Optional[str]:
+ return self._full_name
+
+ @property
+ def raw_name(self) -> Optional[str]:
+ return self._raw_name
+
+ @property
+ def address(self) -> Optional[int]:
+ return self._address
+
+ @property
+ def return_type(self) -> Optional[int]:
+ return self._return_type
+
+ @property
+ def parameters(self) -> Optional[List[Tuple[str, types.Type]]]:
+ return self._parameters
+
+ @property
+ def variable_parameters(self) -> Optional[bool]:
+ return self._variable_parameters
+
+ @property
+ def calling_convention(self) -> Optional[callingconvention.CallingConvention]:
+ return self._calling_convention
+
+ @property
+ def platform(self) -> Optional[platform.Platform]:
+ return self._platform
+
+
+class DebugInfo(object):
+ """
+ ``class DebugInfo`` provides an interface to both provide and query debug info. The DebugInfo object is used
+ internally by the binary view to which it is applied to determine the attributes of functions, types, and variables
+ that would otherwise be costly to deduce.
+
+ DebugInfo objects themselves are independent of binary views; their data can be sourced from any arbitrary binary
+ views and be applied to any other arbitrary binary view. A DebugInfo object can also contain debug info from multiple
+ DebugInfoParsers. This makes it possible to gather debug info that may be distributed across several different
+ formats and files.
+
+ DebugInfo cannot be instantiated by the user, instead get it from either the binary view (see ``binaryninja.binaryview.BinaryView.debug_info``)
+ or a debug-info parser (see ``binaryninja.debuginfo.DebugInfoParser.parse_debug_info``).
+
+ .. note:: Please note that calling one of ``add_*`` functions will not work outside of a debuginfo plugin.
+ """
+ def __init__(self, handle: core.BNDebugInfo) -> None:
+ self.handle = core.handle_of_type(handle, core.BNDebugInfo)
+
+ def __del__(self) -> None:
+ core.BNFreeDebugInfoReference(self.handle)
+
+ def types_from_parser(self, name: Optional[str] = None) -> Iterator[Tuple[str, types.Type]]:
+ """Returns a generator of all types provided by a named DebugInfoParser"""
+ count = ctypes.c_ulonglong(0)
+ name_and_types = core.BNGetDebugTypes(self.handle, name, count)
+ try:
+ for i in range(0, count.value):
+ yield (name_and_types[i].name, types.Type(core.BNNewTypeReference(name_and_types[i].type)))
+ finally:
+ core.BNFreeDebugTypes(name_and_types, count.value)
+
+ @property
+ def types(self) -> Iterator[Tuple[str, types.Type]]:
+ """A generator of all types provided by DebugInfoParsers"""
+ return self.types_from_parser()
+
+ def functions_from_parser(self, name: Optional[str] = None) -> Iterator[DebugFunctionInfo]:
+ """Returns a generator of all functions provided by a named DebugInfoParser"""
+ count = ctypes.c_ulonglong(0)
+ functions = core.BNGetDebugFunctions(self.handle, name, count)
+ try:
+ for i in range(0, count.value):
+
+ parameters: List[Tuple[str, binaryninja.types.Type]] = []
+ for j in range(functions[i].parameterCount):
+ parameters.append((functions[i].parameterNames[j], binaryninja.types.Type(core.BNNewTypeReference(functions[i].parameterTypes[j]))))
+
+ if functions[i].returnType:
+ return_type = binaryninja.types.Type(core.BNNewTypeReference(functions[i].returnType))
+ else:
+ return_type = None
+
+ if functions[i].callingConvention:
+ calling_convention = callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(functions[i].callingConvention))
+ else:
+ calling_convention = None
+
+ if functions[i].platform:
+ func_platform = platform.Platform(handle=core.BNNewPlatformReference(functions[i].platform))
+ else:
+ func_platform = None
+
+ yield DebugFunctionInfo(
+ functions[i].address,
+ functions[i].shortName,
+ functions[i].fullName,
+ functions[i].rawName,
+ return_type,
+ parameters,
+ functions[i].variableParameters,
+ calling_convention,
+ func_platform
+ )
+ finally:
+ core.BNFreeDebugFunctions(functions, count.value)
+
+ @property
+ def functions(self) -> Iterator[DebugFunctionInfo]:
+ """A generator of all functions provided by DebugInfoParsers"""
+ return self.functions_from_parser()
+
+ def data_variables_from_parser(self, name: Optional[str] = None) -> Iterator[binaryninja.binaryview.DataVariableAndName]:
+ """Returns a generator of all data variables provided by a named DebugInfoParser"""
+ count = ctypes.c_ulonglong(0)
+ data_variables = core.BNGetDebugDataVariables(self.handle, name, count)
+ try:
+ for i in range(0, count.value):
+ yield binaryninja.binaryview.DataVariableAndName(
+ data_variables[i].address,
+ binaryninja.types.Type(core.BNNewTypeReference(data_variables[i].type)),
+ data_variables[i].name,
+ data_variables[i].autoDiscovered,
+ data_variables[i].typeConfidence
+ )
+ finally:
+ core.BNFreeDataVariablesAndName(data_variables, count.value)
+
+ @property
+ def data_variables(self) -> Iterator[binaryninja.binaryview.DataVariableAndName]:
+ """A generator of all data variables provided by DebugInfoParsers"""
+ return self.data_variables_from_parser()
+
+ def add_type(self, name: str, new_type: binaryninja.types.Type) -> bool:
+ """Adds a type scoped under the current parser's name to the debug info"""
+ if isinstance(new_type, binaryninja.types.Type):
+ return core.BNAddDebugType(self.handle, name, new_type.handle)
+ return NotImplemented
+
+ def add_function(self, new_func: DebugFunctionInfo) -> bool:
+ """Adds a function scoped under the current parser's name to the debug info"""
+ if not isinstance(new_func, DebugFunctionInfo):
+ return NotImplemented
+
+ parameter_count = 0
+ if new_func.parameters is not None:
+ parameter_count = len(new_func.parameters)
+
+ func_info = core.BNDebugFunctionInfo()
+
+ if new_func.return_type is None:
+ func_info.returnType = None
+ elif isinstance(new_func.return_type, binaryninja.types.Type):
+ func_info.returnType = new_func.return_type.handle
+ else:
+ return NotImplemented
+
+ if new_func.calling_convention is None:
+ func_info.callingConvention = None
+ elif isinstance(new_func.calling_convention, callingconvention.CallingConvention):
+ func_info.callingConvention = new_func.calling_convention.handle
+ else:
+ return NotImplemented
+
+ if new_func.platform is None:
+ func_info.platform = None
+ elif isinstance(new_func.platform, platform.Platform):
+ func_info.platform = new_func.platform.handle
+ else:
+ return NotImplemented
+
+ func_info.shortName = new_func.short_name
+ func_info.fullName = new_func.full_name
+ func_info.rawName = new_func.raw_name
+ func_info.address = new_func.address
+ func_info.variableParameters = new_func.variable_parameters
+
+ if parameter_count == 0:
+ func_info.parameterNames = None
+ func_info.parameterTypes = None
+ func_info.parameterCount = parameter_count
+ else:
+ func_info.parameterNames = (ctypes.c_char_p * parameter_count)(*map(lambda pair: binaryninja.cstr(pair[0]), new_func.parameters))
+ func_info.parameterTypes = (ctypes.POINTER(core.BNType) * parameter_count)(*map(lambda pair: pair[1].handle, new_func.parameters))
+ func_info.parameterCount = parameter_count
+
+ return core.BNAddDebugFunction(self.handle, func_info)
+
+ def add_data_variable(self, address: int, new_type: binaryninja.types.Type, name: Optional[str] = None) -> bool:
+ """Adds a data variable scoped under the current parser's name to the debug info"""
+ if isinstance(address, int) and isinstance(new_type, binaryninja.types.Type):
+ return core.BNAddDebugDataVariable(self.handle, address, new_type.handle, name)
+ return NotImplemented
diff --git a/python/examples/debug_info.py b/python/examples/debug_info.py
new file mode 100755
index 00000000..8f379544
--- /dev/null
+++ b/python/examples/debug_info.py
@@ -0,0 +1,269 @@
+#!/usr/bin/env python3
+
+# If you're here, you're likely looking for boilerplate code. Here it is:
+# ```
+# import binaryninja as bn
+#
+# def is_valid(bv: bn.binaryview.BinaryView):
+# return bv.view_type == "Raw"
+#
+# def parse_info(debug_info: bn.debuginfo.DebugInfo, bv: bn.binaryview.BinaryView):
+# debug_info.add_type("name", bn.types.Type.int(4, True))
+#
+# debug_info.add_data_variable(0x1234, bn.types.Type.int(4, True), "name")
+# debug_info.add_data_variable(0x4321, bn.types.Type.int(4, True)) # Names are optional
+#
+# # Just provide the information you can; we can't create the function without an address, but we'll
+# # figure out what we can and you can query this info later when you have a better idea of things
+# function_info = bn.debuginfo.DebugFunctionInfo(0xdead1337, "short_name", "full_name", "raw_name", bn.types.Type.int(4, False), [])
+# debug_info.add_function(function_info)
+#
+# bn.debuginfo.DebugInfoParser.register("debug info parser", is_valid, parse_info)
+# ```
+
+# If you're interesting in applying debug info to existing BNDBs or otherwise manipulating debug info more directally, consider:
+# ```
+# valid_parsers = bn.debuginfo.DebugInfoParser.get_parsers_for_view(bv)
+# parser = bn.debuginfo.DebugInfoParser[name]
+# debug_info = parser.parse_debug_info(bv)
+# bv.apply_debug_info(debug_info)
+# ```
+
+
+# The rest of this file serves as a test and example of implementing debug info parsers, and the resultant debug info.
+#
+# All that is required is to provide functions similar to "is_valid" and "parse_info" below, and call
+# `binaryninja.debuginfo.DebugInfoParser.register` with a name for your parser; your parser will be made
+# available for all valid binary views, with the ability to parse and apply debug info to existing BNDBs.
+#
+# For the purposes of this example, the following test program was compiled and the symbol `__elf_interp`
+# overwritten to provide some magic for us to key on. This example should prove sufficient to
+# demonstraight the capabilities of a debug info parser; providing function prototypes, local variables,
+# data variables, and new types. It also highlights some limitations of BN at time of writing which
+# should be fixed (see github.com/Vector35/binaryninja-api/issues/2399).
+# ```
+# #include <stdint.h>
+# #include <stdbool.h>
+#
+# struct test_type_1 {
+# int a;
+# char b[4];
+# uint64_t c;
+# bool d;
+# } test_var_1;
+#
+# struct test_type_2 {
+# struct test_type_1 a;
+# struct test_type_1* b;
+# struct test_type_2* c;
+# };
+#
+# int test_var_2 = 0x1232;
+# const int test_var_3 = 0x1233;
+# static int test_var_4 = 0x1234;
+#
+# void no_return_type_no_parameters() { }
+#
+# bool used_parameter(bool value)
+# {
+# return !value;
+# }
+#
+# int unused_parameters(bool value_1, int value_2, char* value_3)
+# {
+# return 8*16-12/32+7|13;
+# }
+#
+# int used_and_unused_parameters_1(int value_1, int value_2, char* value_3, bool value_4)
+# {
+# return value_1 + value_2;
+# }
+#
+# uint8_t used_and_unused_parameters_2(bool value_1, uint8_t value_2, char* value_3, uint8_t value_4, char value_5)
+# {
+# return value_2 + value_4;
+# }
+#
+# void local_parameters(bool value_1, uint8_t value_2, char* value_3, uint8_t value_4, char value_5)
+# {
+# char local_var_1 = value_1 ? value_3[15] : value_5;
+# uint8_t local_var_2 = value_2 + 25;
+# }
+#
+# int main()
+# {
+# int a = 0b01010101;
+# int b = 0b10101010;
+# return ~(a | b | test_var_2);
+# }
+# ```
+
+
+import binaryninja as bn
+import os
+
+
+filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_debug_info")
+
+
+# Some setup code not just for informative printing
+
+
+print = print
+if __name__ != "__main__":
+ print = bn.log_error
+
+
+def pretty_print_add_data_variable(debug_info: bn.debuginfo.DebugInfo, address: int, t: bn.types.Type, name: str = None) -> None:
+ print(f" Adding data variable of type `{t}` at {hex(address)} : {debug_info.add_data_variable(address, t, name)}")
+
+
+def pretty_print_add_function(debug_info: bn.debuginfo.DebugInfo, address: int, short_name: str = None, full_name: str = None, raw_name: str = None, return_type = None, parameters = None) -> None:
+ function_info = bn.debuginfo.DebugFunctionInfo(address, short_name, full_name, raw_name, return_type, parameters)
+ if parameters is not None:
+ print(f" Adding function `{return_type} {short_name}({', '.join(f'{t} {name}' for name, t in parameters)})` at {hex(address)} : {debug_info.add_function(function_info)}")
+ else:
+ print(f" Adding function `{return_type} {short_name}()` at {hex(address)} : {debug_info.add_function(function_info)}")
+
+
+# The beginning of the actual debug info plugin
+
+
+def is_valid(bv: bn.binaryview.BinaryView):
+ sym = bv.get_symbol_by_raw_name("__elf_interp")
+ if sym is None:
+ return False
+ else:
+ var = bv.get_data_var_at(sym.address)
+ return b"test_debug_info_parsing" == bv.read(sym.address, var.type.width-1)
+
+
+def parse_info(debug_info: bn.debuginfo.DebugInfo, bv: bn.binaryview.BinaryView):
+ print("Adding types")
+ types = []
+ for name, t in bv.parse_types_from_string("""
+ struct test_type_1 {
+ int a;
+ char b[4];
+ uint64_t c;
+ bool d;
+ };
+
+ struct test_type_2 {
+ struct test_type_1 a;
+ struct test_type_1* b;
+ struct test_type_2* c;
+ };""").types.items():
+ print(f" Adding type \"{name}\" `{t}` : {debug_info.add_type(str(name), t)}")
+ types.append(t)
+
+ print("Adding data variables")
+ pretty_print_add_data_variable(debug_info, 0x4030, types[0], "test_var_1")
+ pretty_print_add_data_variable(debug_info, 0x4010, bn.types.Type.int(4, True), "test_var_2")
+ # Names are optional
+ pretty_print_add_data_variable(debug_info, 0x4014, bn.types.Type.int(4, True))
+
+ t = bn.types.Type.int(4, True)
+ t.const = True
+ pretty_print_add_data_variable(debug_info, 0x2004, t, "test_var_3")
+
+ print("Adding functions")
+ char_star = bv.parse_type_string("char*")[0]
+ pretty_print_add_function(debug_info, 0x1129, "no_return_type_no_parameters", None, None, bn.types.Type.void(), None)
+ pretty_print_add_function(debug_info, 0x1134, "used_parameter", None, None, bn.types.Type.bool(), [("value", bn.types.Type.bool())])
+ pretty_print_add_function(debug_info, 0x1155, "unused_parameters", None, None, bn.types.Type.int(4, True), [("value_1", bn.types.Type.bool()), ("value_2", bn.types.Type.int(4, True)), ("value_3", char_star)])
+ pretty_print_add_function(debug_info, 0x1170, "used_and_unused_parameters_1", None, None, bn.types.Type.int(4, True), [("value_1", bn.types.Type.int(4, True)), ("value_2", bn.types.Type.int(4, True)), ("value_3", char_star), ("value_4", bn.types.Type.bool())])
+ pretty_print_add_function(debug_info, 0x1191, "used_and_unused_parameters_2", None, None, bn.types.Type.int(1, False), [("value_1", bn.types.Type.bool()), ("value_2", bn.types.Type.int(1, False)), ("value_3", char_star), ("value_4", bn.types.Type.int(1, False)), ("value_5", bn.types.Type.char())])
+ pretty_print_add_function(debug_info, 0x11c0, "local_parameters", None, None, bn.types.Type.void(), [("value_1", bn.types.Type.bool()), ("value_2", bn.types.Type.int(1, False)), ("value_3", char_star), ("value_4", bn.types.Type.int(1, False)), ("value_5", bn.types.Type.char())])
+
+
+parser = bn.debuginfo.DebugInfoParser.register("test debug info parser", is_valid, parse_info)
+print(f"Registered parser: {parser.name}")
+
+
+# The above is all that is needed for a DebugInfo plugin
+# The below serves to test the correctness of (the Python bindings' implementation of) debug info parsers' functionality.
+
+
+bn.debuginfo.DebugInfoParser.register("dummy extra debug parser 1", lambda bv: False, lambda di, bv: None)
+bn.debuginfo.DebugInfoParser.register("dummy extra debug parser 2", lambda bv: bv.view_type != "Raw", lambda di, bv: None)
+
+# Test fetching parser list and fetching by name
+print(f"Availible parsers: {len(list(bn.debuginfo.DebugInfoParser))}")
+for p in bn.debuginfo.DebugInfoParser:
+ if p == parser:
+ print(f" {bn.debuginfo.DebugInfoParser[p.name].name} (the one we just registered)")
+ else:
+ print(f" {bn.debuginfo.DebugInfoParser[p.name].name}")
+
+# Test calling our `is_valid` callback
+bv = bn.open_view(filename, options={"analysis.experimental.parseDebugInfo": False})
+if parser.is_valid_for_view(bv):
+ print("Parser is valid")
+else:
+ print("Parser is NOT valid!")
+ quit()
+
+
+# Test getting list of valid parsers, and DebugInfoParser's repr
+print("")
+for p in bn.debuginfo.DebugInfoParser.get_parsers_for_view(bv):
+ print(f"`{p.name}` is valid for `{bv}`")
+print("")
+
+# Test calling our `parse_info` callback
+debug_info = parser.parse_debug_info(bv)
+# debug_info = bv.debug_info
+
+print("\nEach of the following pairs of prints should be the same\n")
+
+print("All types:")
+for name, t in debug_info.types:
+ print(f" \"{name}\": `{t}`")
+
+print("Types from parser:")
+for name, t in debug_info.types_from_parser(parser.name):
+ print(f" \"{name}\": `{t}`")
+
+print("")
+
+print("All functions:")
+for func in debug_info.functions:
+ print(f" {func}")
+
+print("Functions from parser:")
+for func in debug_info.functions_from_parser(parser.name):
+ print(f" {func}")
+
+print("")
+
+print("All data variables:")
+for data_var in debug_info.data_variables:
+ print(f" {data_var}")
+
+print("Data variables from parser:")
+for data_var in debug_info.data_variables_from_parser(parser.name):
+ print(f" {data_var}")
+
+
+print("Appling debug info!")
+bv.apply_debug_info(debug_info)
+bv.update_analysis_and_wait()
+
+# Checking applied debug info
+print("")
+print("Types:")
+for name, t in debug_info.types:
+ print(f" {bv.get_type_by_name(name)}")
+
+print("")
+
+print("Functions:")
+for func in debug_info.functions:
+ print(f" {bv.get_function_at(func.address)}")
+
+print("")
+
+print("Data variables:")
+for data_var in debug_info.data_variables:
+ print(f" {bv.get_data_var_at(data_var.address)}")
diff --git a/python/examples/test_debug_info b/python/examples/test_debug_info
new file mode 100755
index 00000000..8a650411
--- /dev/null
+++ b/python/examples/test_debug_info
Binary files differ
diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs
index 21239fc2..98e7a7a2 100644
--- a/rust/src/binaryview.rs
+++ b/rust/src/binaryview.rs
@@ -24,6 +24,7 @@ use crate::architecture::Architecture;
use crate::architecture::CoreArchitecture;
use crate::basicblock::BasicBlock;
use crate::databuffer::DataBuffer;
+use crate::debuginfo::DebugInfo;
use crate::fileaccessor::FileAccessor;
use crate::filemetadata::FileMetadata;
use crate::flowgraph::FlowGraph;
@@ -607,6 +608,18 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
+ fn debug_info(&self) -> Ref<DebugInfo> {
+ unsafe { DebugInfo::from_raw(BNGetDebugInfo(self.as_ref().handle)) }
+ }
+
+ fn set_debug_info(&self, debug_info: &DebugInfo) {
+ unsafe { BNSetDebugInfo(self.as_ref().handle, debug_info.handle) }
+ }
+
+ fn apply_debug_info(&self, debug_info: &DebugInfo) {
+ unsafe { BNApplyDebugInfo(self.as_ref().handle, debug_info.handle) }
+ }
+
fn show_graph_report<S: BnStrCompatible>(&self, raw_name: S, graph: FlowGraph) {
let raw_name = raw_name.as_bytes_with_nul();
unsafe {
diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs
new file mode 100644
index 00000000..a22450d5
--- /dev/null
+++ b/rust/src/debuginfo.rs
@@ -0,0 +1,657 @@
+// Copyright 2021 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.
+
+// TODO : These docs are here, but could afford to be cleaned up
+
+//! Parsers and providers of debug information to Binary Ninja.
+//!
+//! The debug information is used by Binary Ninja as ground-truth information about the attributes of functions,
+//! types, and variables that Binary Ninja's analysis pipeline would otherwise work to deduce. By providing
+//! debug info, Binary Ninja's output can be generated quicker, more accurately, and more completely.
+//!
+//! A DebugInfoParser consists of:
+//! 1. A name
+//! 2. An `is_valid` function which takes a BV and returns a bool
+//! 3. A `parse` function which takes a `DebugInfo` object and uses the member functions `add_type`, `add_function`, and `add_data_variable` to populate all the info it can.
+//! And finally calling `binaryninja::debuginfo::DebugInfoParser::register` to register it with the core.
+//!
+//! Here's a minimal, complete example boilerplate-plugin:
+//! ```
+//! use binaryninja::{
+//! binaryview::BinaryView,
+//! debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
+//! };
+//!
+//! struct ExampleDebugInfoParser;
+//!
+//! impl CustomDebugInfoParser for ExampleDebugInfoParser {
+//! fn is_valid(&self, _view: &BinaryView) -> bool {
+//! true
+//! }
+//!
+//! fn parse_info(&self, _debug_info: &mut DebugInfo, _view: &BinaryView) {
+//! println!("Parsing info");
+//! }
+//! }
+//!
+//! #[no_mangle]
+//! pub extern "C" fn CorePluginInit() -> bool {
+//! DebugInfoParser::register("example debug info parser", ExampleDebugInfoParser {});
+//! true
+//! }
+//! ```
+//!
+//! `DebugInfo` can then be automatically applied to valid binary views (via the "Parse and Apply Debug Info" setting), or manually fetched/applied as bellow:
+//! ```
+//! let valid_parsers = DebugInfoParser::parsers_for_view(bv);
+//! let parser = valid_parsers[0];
+//! let debug_info = parser.parse_debug_info(bv);
+//! bv.apply_debug_info(debug_info);
+//! ```
+//!
+//! Multiple debug-info parsers can manually contribute debug info for a binary view by simply calling `parse_debug_info` with the
+//! `DebugInfo` object just returned. This is automatic when opening a binary view with multiple valid debug info parsers. If you
+//! wish to set the debug info for a binary view without applying it as well, you can call `binaryninja::binaryview::BinaryView::set_debug_info`.
+
+use binaryninjacore_sys::*;
+
+use crate::{
+ architecture::{Architecture, CoreArchitecture},
+ binaryview::BinaryView,
+ callingconvention::CallingConvention,
+ platform::Platform,
+ rc::*,
+ string::{raw_to_string, BnStrCompatible, BnString},
+ types::{DataVariableAndName, NameAndType, Type},
+};
+
+use std::{
+ hash::Hash,
+ mem,
+ os::raw::{c_char, c_void},
+ ptr, slice,
+};
+
+//////////////////////
+// DebugInfoParser
+
+/// Represents the registered parsers and providers of debug information to Binary Ninja.
+/// See `binaryninja::debuginfo` for more information
+#[derive(PartialEq, Eq, Hash)]
+pub struct DebugInfoParser {
+ pub(crate) handle: *mut BNDebugInfoParser,
+}
+
+impl DebugInfoParser {
+ pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfoParser) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
+
+ Ref::new(Self { handle })
+ }
+
+ /// Returns debug info parser of the given name, if it exists
+ pub fn from_name<S: BnStrCompatible>(name: S) -> Result<Ref<Self>, ()> {
+ let name = name.as_bytes_with_nul();
+ let parser = unsafe { BNGetDebugInfoParserByName(name.as_ref().as_ptr() as *mut _) };
+
+ if parser.is_null() {
+ Err(())
+ } else {
+ unsafe { Ok(Self::from_raw(parser)) }
+ }
+ }
+
+ /// List all debug-info parsers
+ pub fn list() -> Array<DebugInfoParser> {
+ let mut count: usize = unsafe { mem::zeroed() };
+ let raw_parsers = unsafe { BNGetDebugInfoParsers(&mut count as *mut _) };
+ unsafe { Array::new(raw_parsers, count, ()) }
+ }
+
+ /// Returns a list of debug-info parsers that are valid for the provided binary view
+ pub fn parsers_for_view(bv: &BinaryView) -> Array<DebugInfoParser> {
+ let mut count: usize = unsafe { mem::zeroed() };
+ let raw_parsers = unsafe { BNGetDebugInfoParsersForView(bv.handle, &mut count as *mut _) };
+ unsafe { Array::new(raw_parsers, count, ()) }
+ }
+
+ /// Returns the name of the current parser
+ pub fn name(&self) -> BnString {
+ unsafe { BnString::from_raw(BNGetDebugInfoParserName(self.handle)) }
+ }
+
+ /// Returns whether this debug-info parser is valid for the provided binary view
+ pub fn is_valid_for_view(&self, view: &BinaryView) -> bool {
+ unsafe { BNIsDebugInfoParserValidForView(self.handle, view.handle) }
+ }
+
+ /// Returns a `DebugInfo` object populated with debug info by this debug-info parser. Only provide a `DebugInfo` object if you wish to append to the existing debug info
+ pub fn parse_debug_info(
+ &self,
+ view: &BinaryView,
+ existing_debug_info: Option<&DebugInfo>,
+ ) -> Ref<DebugInfo> {
+ match existing_debug_info {
+ Some(debug_info) => unsafe {
+ DebugInfo::from_raw(BNParseDebugInfo(
+ self.handle,
+ view.handle,
+ debug_info.handle,
+ ))
+ },
+ None => unsafe {
+ DebugInfo::from_raw(BNParseDebugInfo(self.handle, view.handle, ptr::null_mut()))
+ },
+ }
+ }
+
+ // Registers a DebugInfoParser. See `binaryninja::debuginfo::DebugInfoParser` for more details.
+ pub fn register<S, C>(name: S, parser_callbacks: C) -> Ref<Self>
+ where
+ S: BnStrCompatible,
+ C: CustomDebugInfoParser,
+ {
+ extern "C" fn cb_is_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
+ where
+ C: CustomDebugInfoParser,
+ {
+ ffi_wrap!("CustomDebugInfoParser::is_valid", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+
+ cmd.is_valid(&view)
+ })
+ }
+
+ extern "C" fn cb_parse_info<C>(
+ ctxt: *mut c_void,
+ debug_info: *mut BNDebugInfo,
+ view: *mut BNBinaryView,
+ ) where
+ C: CustomDebugInfoParser,
+ {
+ ffi_wrap!("CustomDebugInfoParser::parse_info", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+ let mut debug_info = DebugInfo::from_raw(debug_info);
+
+ cmd.parse_info(&mut debug_info, &view);
+ })
+ }
+
+ let name = name.as_bytes_with_nul();
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+ let ctxt = Box::into_raw(Box::new(parser_callbacks));
+
+ unsafe {
+ DebugInfoParser::from_raw(BNRegisterDebugInfoParser(
+ name_ptr,
+ Some(cb_is_valid::<C>),
+ Some(cb_parse_info::<C>),
+ ctxt as *mut _,
+ ))
+ }
+ }
+}
+
+unsafe impl RefCountable for DebugInfoParser {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewDebugInfoParserReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeDebugInfoParserReference(handle.handle);
+ }
+}
+
+impl AsRef<DebugInfoParser> for DebugInfoParser {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+impl ToOwned for DebugInfoParser {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for DebugInfoParser {
+ type Raw = *mut BNDebugInfoParser;
+ type Context = ();
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) {
+ BNFreeDebugInfoParserList(raw, count);
+ }
+}
+
+///////////////////////
+// DebugFunctionInfo
+
+/// Collates ground-truth function-external attributes for use in BinaryNinja's internal analysis.
+///
+/// When contributing function info, provide only what you know - BinaryNinja will figure out everything else that it can, as it usually does.
+///
+/// Functions will not be created if an address is not provided, but will be able to be queried from debug info for later user analysis.
+pub struct DebugFunctionInfo<A: Architecture, S1: BnStrCompatible, S2: BnStrCompatible> {
+ short_name: Option<S1>,
+ full_name: Option<S1>,
+ raw_name: Option<S1>,
+ return_type: Option<Ref<Type>>,
+ address: u64,
+ parameters: Vec<(S2, Ref<Type>)>,
+ variable_parameters: bool,
+ calling_convention: Option<Ref<CallingConvention<A>>>,
+ platform: Option<Ref<Platform>>,
+}
+
+impl From<&BNDebugFunctionInfo> for DebugFunctionInfo<CoreArchitecture, String, String> {
+ fn from(raw: &BNDebugFunctionInfo) -> Self {
+ let raw_parameter_names: &[*mut ::std::os::raw::c_char] =
+ unsafe { slice::from_raw_parts(raw.parameterNames as *mut _, raw.parameterCount) };
+ let raw_parameter_types: &[*mut BNType] =
+ unsafe { slice::from_raw_parts(raw.parameterTypes as *mut _, raw.parameterCount) };
+
+ let parameters: Vec<(String, Ref<Type>)> = (0..raw.parameterCount)
+ .map(|i| {
+ (raw_to_string(raw_parameter_names[i]).unwrap(), unsafe {
+ Type::ref_from_raw(raw_parameter_types[i])
+ })
+ })
+ .collect();
+
+ Self {
+ short_name: raw_to_string(raw.shortName),
+ full_name: raw_to_string(raw.fullName),
+ raw_name: raw_to_string(raw.rawName),
+ return_type: if raw.returnType.is_null() {
+ None
+ } else {
+ Some(unsafe { Type::ref_from_raw(raw.returnType) })
+ },
+ address: raw.address,
+ parameters,
+ variable_parameters: raw.variableParameters,
+ calling_convention: if raw.callingConvention.is_null() {
+ None
+ } else {
+ Some(unsafe {
+ CallingConvention::ref_from_raw(
+ raw.callingConvention,
+ CoreArchitecture::from_raw(BNGetCallingConventionArchitecture(
+ raw.callingConvention,
+ )),
+ )
+ })
+ },
+ platform: if raw.returnType.is_null() {
+ None
+ } else {
+ Some(unsafe { Platform::ref_from_raw(raw.platform) })
+ },
+ }
+ }
+}
+
+impl<A: Architecture, S1: BnStrCompatible, S2: BnStrCompatible> Into<BNDebugFunctionInfo>
+ for DebugFunctionInfo<A, S1, S2>
+{
+ fn into(self) -> BNDebugFunctionInfo {
+ let parameter_count: usize = self.parameters.len();
+
+ let (short_name, _short_name_ref) = match self.short_name {
+ Some(name) => {
+ let temp = Box::new(name.as_bytes_with_nul());
+ ((*temp).as_ref().as_ptr() as *mut _, Some(temp))
+ }
+ _ => (ptr::null_mut() as *mut _, None),
+ };
+ let (full_name, _full_name_ref) = match self.full_name {
+ Some(name) => {
+ let temp = Box::new(name.as_bytes_with_nul());
+ ((*temp).as_ref().as_ptr() as *mut _, Some(temp))
+ }
+ _ => (ptr::null_mut() as *mut _, None),
+ };
+ let (raw_name, _raw_name_ref) = match self.raw_name {
+ Some(name) => {
+ let temp = Box::new(name.as_bytes_with_nul());
+ ((*temp).as_ref().as_ptr() as *mut _, Some(temp))
+ }
+ _ => (ptr::null_mut() as *mut _, None),
+ };
+
+ let (_parameter_name_bytes, mut parameter_names, mut parameter_types): (
+ Vec<S2::Result>,
+ Vec<*mut c_char>,
+ Vec<*mut BNType>,
+ ) = self.parameters.into_iter().fold(
+ (
+ Vec::with_capacity(parameter_count),
+ Vec::with_capacity(parameter_count),
+ Vec::with_capacity(parameter_count),
+ ),
+ |(mut parameter_name_bytes, mut parameter_names, mut parameter_types), (n, t)| {
+ parameter_name_bytes.push(n.as_bytes_with_nul());
+ parameter_names
+ .push(parameter_name_bytes.last().unwrap().as_ref().as_ptr() as *mut c_char);
+ parameter_types.push(t.handle);
+ (parameter_name_bytes, parameter_names, parameter_types)
+ },
+ );
+
+ BNDebugFunctionInfo {
+ shortName: short_name,
+ fullName: full_name,
+ rawName: raw_name,
+ address: self.address,
+ returnType: match self.return_type {
+ Some(return_type) => return_type.handle,
+ _ => ptr::null_mut(),
+ },
+ parameterNames: match parameter_count {
+ 0 => ptr::null_mut(),
+ _ => parameter_names.as_mut_ptr(),
+ },
+ parameterTypes: match parameter_count {
+ 0 => ptr::null_mut(),
+ _ => parameter_types.as_mut_ptr(),
+ },
+ parameterCount: parameter_count,
+ variableParameters: self.variable_parameters,
+ callingConvention: match self.calling_convention {
+ Some(calling_convention) => calling_convention.handle,
+ _ => ptr::null_mut(),
+ },
+ platform: match self.platform {
+ Some(platform) => platform.handle,
+ _ => ptr::null_mut(),
+ },
+ }
+ }
+}
+
+impl<A: Architecture, S1: BnStrCompatible, S2: BnStrCompatible> DebugFunctionInfo<A, S1, S2> {
+ pub fn new(
+ short_name: Option<S1>,
+ full_name: Option<S1>,
+ raw_name: Option<S1>,
+ return_type: Option<Ref<Type>>,
+ address: Option<u64>,
+ parameters: Option<Vec<(S2, Ref<Type>)>>,
+ variable_parameters: Option<bool>,
+ calling_convention: Option<Ref<CallingConvention<A>>>,
+ platform: Option<Ref<Platform>>,
+ ) -> Self {
+ Self {
+ short_name,
+ full_name,
+ raw_name,
+ return_type,
+ address: match address {
+ Some(address) => address,
+ _ => 0,
+ },
+ parameters: match parameters {
+ Some(parameters) => parameters,
+ _ => vec![],
+ },
+ variable_parameters: match variable_parameters {
+ Some(variable_parameters) => variable_parameters,
+ _ => false,
+ },
+ calling_convention,
+ platform,
+ }
+ }
+}
+
+///////////////
+// DebugInfo
+
+/// Provides an interface to both provide and query debug info. The DebugInfo object is used
+/// internally by the binary view to which it is applied to determine the attributes of functions, types, and variables
+/// that would otherwise be costly to deduce.
+///
+/// DebugInfo objects themselves are independent of binary views; their data can be sourced from any arbitrary binary
+/// views and be applied to any other arbitrary binary view. A DebugInfo object can also contain debug info from multiple
+/// DebugInfoParsers. This makes it possible to gather debug info that may be distributed across several different
+/// formats and files.
+///
+/// DebugInfo cannot be instantiated by the user, instead get it from either the binary view (see `binaryninja::binaryview::BinaryView::debug_info`)
+/// or a debug-info parser (see `binaryninja::debuginfo::DebugInfoParser::parse_debug_info`).
+///
+/// Please note that calling one of `add_*` functions will not work outside of a debuginfo plugin.
+#[derive(PartialEq, Eq, Hash)]
+pub struct DebugInfo {
+ pub(crate) handle: *mut BNDebugInfo,
+}
+
+impl DebugInfo {
+ pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfo) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
+
+ Ref::new(Self { handle })
+ }
+
+ /// Returns a generator of all types provided by a named DebugInfoParser
+ pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<NameAndType<String>> {
+ let parser_name = parser_name.as_bytes_with_nul();
+
+ let mut count: usize = 0;
+ let debug_types_ptr = unsafe {
+ BNGetDebugTypes(
+ self.handle,
+ parser_name.as_ref().as_ptr() as *mut _,
+ &mut count,
+ )
+ };
+ let result: Vec<NameAndType<String>> = unsafe {
+ slice::from_raw_parts_mut(debug_types_ptr, count)
+ .iter()
+ .map(NameAndType::<String>::from_raw)
+ .collect()
+ };
+
+ unsafe { BNFreeDebugTypes(debug_types_ptr, count) };
+ result
+ }
+
+ /// A generator of all types provided by DebugInfoParsers
+ pub fn types(&self) -> Vec<NameAndType<String>> {
+ let mut count: usize = 0;
+ let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, ptr::null_mut(), &mut count) };
+ let result: Vec<NameAndType<String>> = unsafe {
+ slice::from_raw_parts_mut(debug_types_ptr, count)
+ .iter()
+ .map(NameAndType::<String>::from_raw)
+ .collect()
+ };
+
+ unsafe { BNFreeDebugTypes(debug_types_ptr, count) };
+ result
+ }
+
+ /// Returns a generator of all functions provided by a named DebugInfoParser
+ pub fn functions_by_name<S: BnStrCompatible>(
+ &self,
+ parser_name: S,
+ ) -> Vec<DebugFunctionInfo<CoreArchitecture, String, String>> {
+ let parser_name = parser_name.as_bytes_with_nul();
+
+ let mut count: usize = 0;
+ let functions_ptr = unsafe {
+ BNGetDebugFunctions(
+ self.handle,
+ parser_name.as_ref().as_ptr() as *mut _,
+ &mut count,
+ )
+ };
+
+ let result: Vec<DebugFunctionInfo<CoreArchitecture, String, String>> = unsafe {
+ slice::from_raw_parts_mut(functions_ptr, count)
+ .iter()
+ .map(DebugFunctionInfo::<CoreArchitecture, String, String>::from)
+ .collect()
+ };
+
+ unsafe { BNFreeDebugFunctions(functions_ptr, count) };
+ result
+ }
+
+ /// A generator of all functions provided by DebugInfoParsers
+ pub fn functions(&self) -> Vec<DebugFunctionInfo<CoreArchitecture, String, String>> {
+ let mut count: usize = 0;
+ let functions_ptr =
+ unsafe { BNGetDebugFunctions(self.handle, ptr::null_mut(), &mut count) };
+
+ let result: Vec<DebugFunctionInfo<CoreArchitecture, String, String>> = unsafe {
+ slice::from_raw_parts_mut(functions_ptr, count)
+ .iter()
+ .map(DebugFunctionInfo::<CoreArchitecture, String, String>::from)
+ .collect()
+ };
+
+ unsafe { BNFreeDebugFunctions(functions_ptr, count) };
+ result
+ }
+
+ /// Returns a generator of all data variables provided by a named DebugInfoParser
+ pub fn data_variables_by_name<S: BnStrCompatible>(
+ &self,
+ parser_name: S,
+ ) -> Vec<DataVariableAndName<String>> {
+ let parser_name = parser_name.as_bytes_with_nul();
+
+ let mut count: usize = 0;
+ let data_variables_ptr = unsafe {
+ BNGetDebugDataVariables(
+ self.handle,
+ parser_name.as_ref().as_ptr() as *mut _,
+ &mut count,
+ )
+ };
+
+ let result: Vec<DataVariableAndName<String>> = unsafe {
+ slice::from_raw_parts_mut(data_variables_ptr, count)
+ .iter()
+ .map(DataVariableAndName::<String>::from_raw)
+ .collect()
+ };
+
+ unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) };
+ result
+ }
+
+ /// A generator of all data variables provided by DebugInfoParsers
+ pub fn data_variables(&self) -> Vec<DataVariableAndName<String>> {
+ let mut count: usize = 0;
+ let data_variables_ptr =
+ unsafe { BNGetDebugDataVariables(self.handle, ptr::null_mut(), &mut count) };
+
+ let result: Vec<DataVariableAndName<String>> = unsafe {
+ slice::from_raw_parts_mut(data_variables_ptr, count)
+ .iter()
+ .map(DataVariableAndName::<String>::from_raw)
+ .collect()
+ };
+
+ unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) };
+ result
+ }
+
+ /// Adds a type scoped under the current parser's name to the debug info
+ pub fn add_type<S: BnStrCompatible>(&mut self, name: S, new_type: &Type) -> bool {
+ let name = name.as_bytes_with_nul();
+ unsafe {
+ BNAddDebugType(
+ self.handle,
+ name.as_ref().as_ptr() as *mut _,
+ new_type.handle,
+ )
+ }
+ }
+
+ /// Adds a function scoped under the current parser's name to the debug info
+ pub fn add_function<A: Architecture, S1: BnStrCompatible, S2: BnStrCompatible>(
+ &mut self,
+ new_func: DebugFunctionInfo<A, S1, S2>,
+ ) -> bool {
+ unsafe { BNAddDebugFunction(self.handle, &mut new_func.into() as *mut _) }
+ }
+
+ /// Adds a data variable scoped under the current parser's name to the debug info
+ pub fn add_data_variable<S: BnStrCompatible>(
+ &self,
+ address: u64,
+ t: &Type,
+ name: Option<S>,
+ ) -> bool {
+ match name {
+ Some(name) => {
+ let name = name.as_bytes_with_nul();
+ unsafe {
+ BNAddDebugDataVariable(
+ self.handle,
+ address,
+ t.handle,
+ name.as_ref().as_ptr() as *mut _,
+ )
+ }
+ }
+ None => unsafe {
+ BNAddDebugDataVariable(self.handle, address, t.handle, ptr::null_mut())
+ },
+ }
+ }
+}
+
+unsafe impl RefCountable for DebugInfo {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewDebugInfoReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeDebugInfoReference(handle.handle);
+ }
+}
+
+impl AsRef<DebugInfo> for DebugInfo {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+impl ToOwned for DebugInfo {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+////////////////////////////
+// CustomDebugInfoParser
+
+/// Implement this trait to implement a debug info parser. See `DebugInfoParser` for more details.
+pub trait CustomDebugInfoParser: 'static + Sync {
+ fn is_valid(&self, view: &BinaryView) -> bool;
+ fn parse_info(&self, debug_info: &mut DebugInfo, view: &BinaryView);
+}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 56ca2989..6826e9f5 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -44,6 +44,7 @@ pub mod callingconvention;
pub mod command;
pub mod custombinaryview;
pub mod databuffer;
+pub mod debuginfo;
pub mod disassembly;
pub mod fileaccessor;
pub mod filemetadata;
diff --git a/rust/src/string.rs b/rust/src/string.rs
index e042dbdd..198c764b 100644
--- a/rust/src/string.rs
+++ b/rust/src/string.rs
@@ -21,6 +21,14 @@ use std::os::raw;
use crate::rc::*;
+pub(crate) fn raw_to_string(ptr: *const raw::c_char) -> Option<String> {
+ if ptr.is_null() {
+ None
+ } else {
+ Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() })
+ }
+}
+
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(C)]
pub struct BnStr {
diff --git a/rust/src/types.rs b/rust/src/types.rs
index 91bf6972..0884d9ac 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -21,7 +21,7 @@ use std::{fmt, mem, ptr, result, slice};
use crate::architecture::{Architecture, CoreArchitecture};
use crate::callingconvention::CallingConvention;
-use crate::string::{BnStr, BnStrCompatible, BnString};
+use crate::string::{raw_to_string, BnStr, BnStrCompatible, BnString};
use crate::rc::*;
@@ -774,7 +774,6 @@ impl Type {
let mut stack_adjust = Conf::<i64>::new(0, min_confidence()).into();
let mut raw_parameters = Vec::<BNFunctionParameter>::with_capacity(parameters.len());
for parameter in parameters {
- // TODO : The core side is fine, but test this function with named parameters to ensure rust doesn't delete the reference
let raw_name = parameter.name.as_bytes_with_nul();
let location = match &parameter.location {
@@ -823,7 +822,6 @@ impl Type {
let mut raw_parameters = Vec::<BNFunctionParameter>::with_capacity(parameters.len());
for parameter in parameters {
- // TODO : The core side is fine, but test this function with named parameters to ensure rust doesn't delete the reference
let raw_name = parameter.name.as_bytes_with_nul();
let location = match &parameter.location {
@@ -1105,10 +1103,9 @@ impl EnumerationBuilder {
let members: &[*mut BNEnumerationMember] =
slice::from_raw_parts(members_raw as *mut _, count);
- let mut result = vec![];
- for i in 0..count {
- result.push(EnumerationMember::from_raw(members[i]));
- }
+ let result = (0..count)
+ .map(|i| EnumerationMember::from_raw(members[i]))
+ .collect();
BNFreeEnumerationMemberList(members_raw, count);
@@ -1166,10 +1163,9 @@ impl Enumeration {
let members: &[*mut BNEnumerationMember] =
slice::from_raw_parts(members_raw as *mut _, count);
- let mut result = vec![];
- for i in 0..count {
- result.push(EnumerationMember::from_raw(members[i]));
- }
+ let result = (0..count)
+ .map(|i| EnumerationMember::from_raw(members[i]))
+ .collect();
BNFreeEnumerationMemberList(members_raw, count);
@@ -1217,10 +1213,7 @@ impl StructureBuilder {
//! use binaryninja::types::{Structure, Type};
//! // Define struct, set size (in bytes)
- //! let mut my_custom_struct = Structure::new();
- //! my_custom_struct.set_width(17);
-
- //! // Create some fields for the struct
+ //! let mut my_custom_struct = Structure::new();BnStr::from_raw(raw.name)
//! let field_1 = Self::named_int(5, false, "my_weird_int_type");
//! let field_2 = Self::int(4, false);
//! let field_3 = Self::int(8, false);
@@ -1537,3 +1530,137 @@ unsafe impl<'a> CoreOwnedArrayWrapper<'a> for QualifiedNameAndType {
mem::transmute(raw)
}
}
+
+//////////////////////////
+// QualifiedNameAndType
+
+pub struct NameAndType<S: BnStrCompatible> {
+ pub name: S,
+ t: Conf<Ref<Type>>,
+}
+
+impl NameAndType<String> {
+ pub(crate) fn from_raw(raw: &BNNameAndType) -> Self {
+ Self::new(
+ raw_to_string(raw.name).unwrap(),
+ unsafe { &Type::ref_from_raw(raw.type_) },
+ raw.typeConfidence,
+ )
+ }
+}
+
+impl<S: BnStrCompatible> NameAndType<S> {
+ pub fn new(name: S, t: &Ref<Type>, confidence: u8) -> Self {
+ Self {
+ name: name,
+ t: Conf::new(t.clone(), confidence),
+ }
+ }
+
+ pub fn type_with_confidence(&self) -> Conf<Ref<Type>> {
+ self.t.clone()
+ }
+}
+
+unsafe impl<S: BnStrCompatible> CoreOwnedArrayProvider for NameAndType<S> {
+ type Raw = BNNameAndType;
+ type Context = ();
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeNameAndTypeList(raw, count);
+ }
+}
+
+unsafe impl<'a, S: 'a + BnStrCompatible> CoreOwnedArrayWrapper<'a> for NameAndType<S> {
+ type Wrapped = &'a NameAndType<S>;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
+ mem::transmute(raw)
+ }
+}
+
+//////////////////
+// DataVariable
+
+pub struct DataVariable {
+ pub address: u64,
+ pub t: Conf<Ref<Type>>,
+ pub auto_discovered: bool,
+}
+
+// impl DataVariable {
+// pub(crate) fn from_raw(var: &BNDataVariable) -> Self {
+// Self {
+// address: var.address,
+// t: Conf::new(unsafe { Type::ref_from_raw(var.type_) }, var.typeConfidence),
+// auto_discovered: var.autoDiscovered,
+// }
+// }
+// }
+
+impl DataVariable {
+ pub fn type_with_confidence(&self) -> Conf<Ref<Type>> {
+ Conf::new(self.t.contents.clone(), self.t.confidence)
+ }
+}
+
+// unsafe impl CoreOwnedArrayProvider for DataVariable {
+// type Raw = BNDataVariable;
+// type Context = ();
+
+// unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+// BNFreeDataVariables(raw, count);
+// }
+// }
+
+// unsafe impl<'a> CoreOwnedArrayWrapper<'a> for DataVariable {
+// type Wrapped = &'a DataVariable;
+
+// unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
+// mem::transmute(raw)
+// }
+// }
+
+/////////////////////////
+// DataVariableAndName
+
+pub struct DataVariableAndName<S: BnStrCompatible> {
+ pub address: u64,
+ pub t: Conf<Ref<Type>>,
+ pub auto_discovered: bool,
+ pub name: S,
+}
+
+impl DataVariableAndName<String> {
+ pub(crate) fn from_raw(var: &BNDataVariableAndName) -> Self {
+ Self {
+ address: var.address,
+ t: Conf::new(unsafe { Type::ref_from_raw(var.type_) }, var.typeConfidence),
+ auto_discovered: var.autoDiscovered,
+ name: raw_to_string(var.name).unwrap(),
+ }
+ }
+}
+
+impl<S: BnStrCompatible> DataVariableAndName<S> {
+ pub fn type_with_confidence(&self) -> Conf<Ref<Type>> {
+ Conf::new(self.t.contents.clone(), self.t.confidence)
+ }
+}
+
+// unsafe impl<S: BnStrCompatible> CoreOwnedArrayProvider for DataVariableAndName<S> {
+// type Raw = BNDataVariableAndName;
+// type Context = ();
+
+// unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+// BNFreeDataVariablesAndName(raw, count);
+// }
+// }
+
+// unsafe impl<'a, S: 'a + BnStrCompatible> CoreOwnedArrayWrapper<'a> for DataVariableAndName<S> {
+// type Wrapped = &'a DataVariableAndName<S>;
+
+// unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
+// mem::transmute(raw)
+// }
+// }