summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--architecture.cpp13
-rw-r--r--binaryninjaapi.h415
-rw-r--r--binaryninjacore.h186
-rw-r--r--binaryview.cpp58
-rw-r--r--demangle.cpp4
-rw-r--r--python/__init__.py2
-rw-r--r--python/generator.cpp12
-rw-r--r--python/typeparser.py589
-rw-r--r--python/typeprinter.py390
-rw-r--r--python/types.py157
-rw-r--r--rust/src/types.rs34
-rw-r--r--suite/api_test.py428
-rw-r--r--type.cpp203
-rw-r--r--typeparser.cpp513
-rw-r--r--typeprinter.cpp400
-rw-r--r--ui/createtypedialog.h7
16 files changed, 3319 insertions, 92 deletions
diff --git a/architecture.cpp b/architecture.cpp
index cd5306ea..b1acceb5 100644
--- a/architecture.cpp
+++ b/architecture.cpp
@@ -140,6 +140,19 @@ BNInstructionTextToken* InstructionTextToken::CreateInstructionTextTokenList(con
}
+void InstructionTextToken::FreeInstructionTextTokenList(BNInstructionTextToken* tokens, size_t count)
+{
+ for (size_t i = 0; i < count; i++)
+ {
+ BNFreeString(tokens[i].text);
+ for (size_t j = 0; j < tokens[i].namesCount; j++)
+ BNFreeString(tokens[i].typeNames[j]);
+ delete[] tokens[i].typeNames;
+ }
+ delete[] tokens;
+}
+
+
vector<InstructionTextToken> InstructionTextToken::ConvertInstructionTextTokenList(
const BNInstructionTextToken* tokens, size_t count)
{
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 0ae9097a..d0725e5c 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -1377,7 +1377,7 @@ namespace BinaryNinja {
BNQualifiedName GetAPIObject() const;
static void FreeAPIObject(BNQualifiedName* name);
- static QualifiedName FromAPIObject(BNQualifiedName* name);
+ static QualifiedName FromAPIObject(const BNQualifiedName* name);
};
class NameSpace : public NameList
@@ -1506,6 +1506,8 @@ namespace BinaryNinja {
InstructionTextToken WithConfidence(uint8_t conf);
static BNInstructionTextToken* CreateInstructionTextTokenList(const std::vector<InstructionTextToken>& tokens);
+ static void FreeInstructionTextTokenList(
+ BNInstructionTextToken* tokens, size_t count);
static std::vector<InstructionTextToken> ConvertAndFreeInstructionTextTokenList(
BNInstructionTextToken* tokens, size_t count);
static std::vector<InstructionTextToken> ConvertInstructionTextTokenList(
@@ -1556,6 +1558,10 @@ namespace BinaryNinja {
size_t fieldIndex;
static TypeDefinitionLine FromAPIObject(BNTypeDefinitionLine* line);
+ static BNTypeDefinitionLine* CreateTypeDefinitionLineList(
+ const std::vector<TypeDefinitionLine>& lines);
+ static void FreeTypeDefinitionLineList(
+ BNTypeDefinitionLine* lines, size_t count);
};
class DisassemblySettings;
@@ -2117,7 +2123,9 @@ namespace BinaryNinja {
QualifiedName GetTypeNameById(const std::string& id);
bool IsTypeAutoDefined(const QualifiedName& name);
QualifiedName DefineType(const std::string& id, const QualifiedName& defaultName, Ref<Type> type);
+ void DefineTypes(const std::vector<QualifiedNameAndType>& types, std::function<bool(size_t, size_t)> progress = {});
void DefineUserType(const QualifiedName& name, Ref<Type> type);
+ void DefineUserTypes(const std::vector<QualifiedNameAndType>& types, std::function<bool(size_t, size_t)> progress = {});
void UndefineType(const std::string& id);
void UndefineUserType(const QualifiedName& name);
void RenameType(const QualifiedName& oldName, const QualifiedName& newName);
@@ -2980,12 +2988,78 @@ namespace BinaryNinja {
Confidence<Ref<Type>> type;
bool defaultLocation;
Variable location;
+
+ FunctionParameter() = default;
+ FunctionParameter(const std::string& name, Confidence<Ref<Type>> type): name(name), type(type), defaultLocation(true)
+ {}
+
+ FunctionParameter(const std::string& name, const Confidence<Ref<Type>>& type, bool defaultLocation,
+ const Variable& location):
+ name(name), type(type), defaultLocation(defaultLocation), location(location)
+ {}
};
struct QualifiedNameAndType
{
QualifiedName name;
Ref<Type> type;
+
+ QualifiedNameAndType() = default;
+ QualifiedNameAndType(const std::string& name, const Ref<Type>& type): name(name), type(type)
+ {}
+ QualifiedNameAndType(const QualifiedName& name, const Ref<Type>& type): name(name), type(type)
+ {}
+
+ bool operator<(const QualifiedNameAndType& other) const
+ {
+ return name < other.name;
+ }
+ };
+
+ struct TypeAndId
+ {
+ std::string id;
+ Ref<Type> type;
+
+ TypeAndId() = default;
+ TypeAndId(const std::string& id, const Ref<Type>& type): id(id), type(type)
+ {}
+ };
+
+ struct ParsedType
+ {
+ QualifiedName name;
+ Ref<Type> type;
+ bool isUser;
+
+ ParsedType() = default;
+ ParsedType(const std::string& name, const Ref<Type>& type, bool isUser): name(name), type(type), isUser(isUser)
+ {}
+ ParsedType(const QualifiedName& name, const Ref<Type>& type, bool isUser): name(name), type(type), isUser(isUser)
+ {}
+
+ bool operator<(const ParsedType& other) const
+ {
+ if (isUser != other.isUser)
+ return isUser;
+ return name < other.name;
+ }
+ };
+
+ struct TypeParserResult
+ {
+ std::vector<ParsedType> types;
+ std::vector<ParsedType> variables;
+ std::vector<ParsedType> functions;
+ };
+
+ struct TypeParserError
+ {
+ BNTypeParserErrorSeverity severity;
+ std::string message;
+ std::string fileName;
+ uint64_t line;
+ uint64_t column;
};
class Type : public CoreRefCountObject<BNType, BNNewTypeReference, BNFreeType>
@@ -3067,6 +3141,15 @@ namespace BinaryNinja {
const Confidence<Ref<CallingConvention>>& callingConvention, const std::vector<FunctionParameter>& params,
const Confidence<bool>& varArg = Confidence<bool>(false, 0),
const Confidence<int64_t>& stackAdjust = Confidence<int64_t>(0, 0));
+ static Ref<Type> FunctionType(const Confidence<Ref<Type>>& returnValue,
+ const Confidence<Ref<CallingConvention>>& callingConvention,
+ const std::vector<FunctionParameter>& params,
+ const Confidence<bool>& hasVariableArguments,
+ const Confidence<bool>& canReturn,
+ const Confidence<int64_t>& stackAdjust,
+ const std::map<uint32_t, Confidence<int32_t>>& regStackAdjust = std::map<uint32_t, Confidence<int32_t>>(),
+ const Confidence<std::vector<uint32_t>>& returnRegs = Confidence<std::vector<uint32_t>>(std::vector<uint32_t>(), 0),
+ BNNameType ft = NoNameType);
static std::string GenerateAutoTypeId(const std::string& source, const QualifiedName& name);
static std::string GenerateAutoDemangledTypeId(const QualifiedName& name);
@@ -3212,6 +3295,15 @@ namespace BinaryNinja {
const Confidence<Ref<CallingConvention>>& callingConvention, const std::vector<FunctionParameter>& params,
const Confidence<bool>& varArg = Confidence<bool>(false, 0),
const Confidence<int64_t>& stackAdjust = Confidence<int64_t>(0, 0));
+ static TypeBuilder FunctionType(const Confidence<Ref<Type>>& returnValue,
+ const Confidence<Ref<CallingConvention>>& callingConvention,
+ const std::vector<FunctionParameter>& params,
+ const Confidence<bool>& hasVariableArguments,
+ const Confidence<bool>& canReturn,
+ const Confidence<int64_t>& stackAdjust,
+ const std::map<uint32_t, Confidence<int32_t>>& regStackAdjust = std::map<uint32_t, Confidence<int32_t>>(),
+ const Confidence<std::vector<uint32_t>>& returnRegs = Confidence<std::vector<uint32_t>>(std::vector<uint32_t>(), 0),
+ BNNameType ft = NoNameType);
bool IsReferenceOfType(BNNamedTypeReferenceClass refType);
bool IsStructReference() { return IsReferenceOfType(StructNamedTypeClass); }
@@ -5572,6 +5664,327 @@ namespace BinaryNinja {
const std::string& autoTypeSource = "");
};
+ class TypeParser: public StaticCoreRefCountObject<BNTypeParser>
+ {
+ std::string m_nameForRegister;
+ protected:
+ explicit TypeParser(const std::string& name);
+ TypeParser(BNTypeParser* parser);
+
+ static bool PreprocessSourceCallback(void* ctxt,
+ const char* source, const char* fileName, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ const char* const* options, size_t optionCount,
+ const char* const* includeDirs, size_t includeDirCount,
+ char** output, BNTypeParserError** errors, size_t* errorCount
+ );
+ static bool ParseTypesFromSourceCallback(void* ctxt,
+ const char* source, const char* fileName, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ const char* const* options, size_t optionCount,
+ const char* const* includeDirs, size_t includeDirCount,
+ const char* autoTypeSource, BNTypeParserResult* result,
+ BNTypeParserError** errors, size_t* errorCount
+ );
+ static bool ParseTypeStringCallback(void* ctxt,
+ const char* source, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ BNQualifiedNameAndType* result,
+ BNTypeParserError** errors, size_t* errorCount
+ );
+ static void FreeStringCallback(void* ctxt, char* result);
+ static void FreeResultCallback(void* ctxt, BNTypeParserResult* result);
+ static void FreeErrorListCallback(void* ctxt, BNTypeParserError* errors, size_t errorCount);
+
+ public:
+ static void Register(TypeParser* parser);
+ static std::vector<Ref<TypeParser>> GetList();
+ static Ref<TypeParser> GetByName(const std::string& name);
+ static Ref<TypeParser> GetDefault();
+
+ /*!
+ Preprocess a block of source, returning the source that would be parsed
+ \param source Source code to process
+ \param fileName Name of the file containing the source (does not need to exist on disk)
+ \param platform Platform to assume the source is relevant to
+ \param existingTypes Map of all existing types to use for parsing context
+ \param options String arguments to pass as options, e.g. command line arguments
+ \param includeDirs List of directories to include in the header search path
+ \param output Reference to a string into which the preprocessed source will be written
+ \param errors Reference to a list into which any parse errors will be written
+ \return True if preprocessing was successful
+ */
+ virtual bool PreprocessSource(
+ const std::string& source,
+ const std::string& fileName,
+ Ref<Platform> platform,
+ const std::map<QualifiedName, TypeAndId>& existingTypes,
+ const std::vector<std::string>& options,
+ const std::vector<std::string>& includeDirs,
+ std::string& output,
+ std::vector<TypeParserError>& errors
+ ) = 0;
+
+ /*!
+ Parse an entire block of source into types, variables, and functions
+ \param source Source code to parse
+ \param fileName Name of the file containing the source (optional: exists on disk)
+ \param platform Platform to assume the types are relevant to
+ \param existingTypes Map of all existing types to use for parsing context
+ \param options String arguments to pass as options, e.g. command line arguments
+ \param includeDirs List of directories to include in the header search path
+ \param autoTypeSource Optional source of types if used for automatically generated types
+ \param result Reference to structure into which the results will be written
+ \param errors Reference to a list into which any parse errors will be written
+ \return True if parsing was successful
+ */
+ virtual bool ParseTypesFromSource(
+ const std::string& source,
+ const std::string& fileName,
+ Ref<Platform> platform,
+ const std::map<QualifiedName, TypeAndId>& existingTypes,
+ const std::vector<std::string>& options,
+ const std::vector<std::string>& includeDirs,
+ const std::string& autoTypeSource,
+ TypeParserResult& result,
+ std::vector<TypeParserError>& errors
+ ) = 0;
+
+ /*!
+ Parse a single type and name from a string containing their definition.
+ \param source Source code to parse
+ \param platform Platform to assume the types are relevant to
+ \param existingTypes Map of all existing types to use for parsing context
+ \param result Reference into which the resulting type and name will be written
+ \param errors Reference to a list into which any parse errors will be written
+ \return True if parsing was successful
+ */
+ virtual bool ParseTypeString(
+ const std::string& source,
+ Ref<Platform> platform,
+ const std::map<QualifiedName, TypeAndId>& existingTypes,
+ QualifiedNameAndType& result,
+ std::vector<TypeParserError>& errors
+ ) = 0;
+ };
+
+ class CoreTypeParser: public TypeParser
+ {
+ public:
+ CoreTypeParser(BNTypeParser* parser);
+ virtual ~CoreTypeParser() {}
+
+ virtual bool PreprocessSource(
+ const std::string& source,
+ const std::string& fileName,
+ Ref<Platform> platform,
+ const std::map<QualifiedName, TypeAndId>& existingTypes,
+ const std::vector<std::string>& options,
+ const std::vector<std::string>& includeDirs,
+ std::string& output,
+ std::vector<TypeParserError>& errors
+ ) override;
+
+ virtual bool ParseTypesFromSource(
+ const std::string& source,
+ const std::string& fileName,
+ Ref<Platform> platform,
+ const std::map<QualifiedName, TypeAndId>& existingTypes,
+ const std::vector<std::string>& options,
+ const std::vector<std::string>& includeDirs,
+ const std::string& autoTypeSource,
+ TypeParserResult& result,
+ std::vector<TypeParserError>& errors
+ ) override;
+
+ virtual bool ParseTypeString(
+ const std::string& source,
+ Ref<Platform> platform,
+ const std::map<QualifiedName, TypeAndId>& existingTypes,
+ QualifiedNameAndType& result,
+ std::vector<TypeParserError>& errors
+ ) override;
+ };
+
+ class TypePrinter: public StaticCoreRefCountObject<BNTypePrinter>
+ {
+ std::string m_nameForRegister;
+ protected:
+ explicit TypePrinter(const std::string& name);
+ TypePrinter(BNTypePrinter* printer);
+
+ static bool GetTypeTokensCallback(void* ctxt, BNType* type, BNPlatform* platform,
+ BNQualifiedName* name, uint8_t baseConfidence, BNTokenEscapingType escaping,
+ BNInstructionTextToken** result, size_t* resultCount);
+ static bool GetTypeTokensBeforeNameCallback(void* ctxt, BNType* type,
+ BNPlatform* platform, uint8_t baseConfidence, BNType* parentType,
+ BNTokenEscapingType escaping, BNInstructionTextToken** result,
+ size_t* resultCount);
+ static bool GetTypeTokensAfterNameCallback(void* ctxt, BNType* type,
+ BNPlatform* platform, uint8_t baseConfidence, BNType* parentType,
+ BNTokenEscapingType escaping, BNInstructionTextToken** result,
+ size_t* resultCount);
+ static bool GetTypeStringCallback(void* ctxt, BNType* type, BNPlatform* platform,
+ BNQualifiedName* name, BNTokenEscapingType escaping, char** result);
+ static bool GetTypeStringBeforeNameCallback(void* ctxt, BNType* type,
+ BNPlatform* platform, BNTokenEscapingType escaping, char** result);
+ static bool GetTypeStringAfterNameCallback(void* ctxt, BNType* type,
+ BNPlatform* platform, BNTokenEscapingType escaping, char** result);
+ static bool GetTypeLinesCallback(void* ctxt, BNType* type, BNBinaryView* data,
+ BNQualifiedName* name, int lineWidth, bool collapsed,
+ BNTokenEscapingType escaping, BNTypeDefinitionLine** result, size_t* resultCount);
+ static void FreeTokensCallback(void* ctxt, BNInstructionTextToken* tokens, size_t count);
+ static void FreeStringCallback(void* ctxt, char* string);
+ static void FreeLinesCallback(void* ctxt, BNTypeDefinitionLine* lines, size_t count);
+
+ public:
+ static void Register(TypePrinter* printer);
+ static std::vector<Ref<TypePrinter>> GetList();
+ static Ref<TypePrinter> GetByName(const std::string& name);
+ static Ref<TypePrinter> GetDefault();
+
+ /*!
+ Generate a single-line text representation of a type
+ \param type Type to print
+ \param platform Platform responsible for this type
+ \param name Name of the type
+ \param baseConfidence Confidence to use for tokens created for this type
+ \param escaping Style of escaping literals which may not be parsable
+ \return List of text tokens representing the type
+ */
+ virtual std::vector<InstructionTextToken> GetTypeTokens(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ const QualifiedName& name,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE,
+ BNTokenEscapingType escaping = NoTokenEscapingType
+ );
+ /*!
+ In a single-line text representation of a type, generate the tokens that should
+ be printed before the type's name.
+
+ \param type Type to print
+ \param platform Platform responsible for this type
+ \param baseConfidence Confidence to use for tokens created for this type
+ \param parentType Type of the parent of this type, or nullptr
+ \param escaping Style of escaping literals which may not be parsable
+ \return List of text tokens representing the type
+ */
+ virtual std::vector<InstructionTextToken> GetTypeTokensBeforeName(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE,
+ Ref<Type> parentType = nullptr,
+ BNTokenEscapingType escaping = NoTokenEscapingType
+ ) = 0;
+ /*!
+ In a single-line text representation of a type, generate the tokens that should
+ be printed after the type's name.
+
+ \param type Type to print
+ \param platform Platform responsible for this type
+ \param baseConfidence Confidence to use for tokens created for this type
+ \param parentType Type of the parent of this type, or nullptr
+ \param escaping Style of escaping literals which may not be parsable
+ \return List of text tokens representing the type
+ */
+ virtual std::vector<InstructionTextToken> GetTypeTokensAfterName(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE,
+ Ref<Type> parentType = nullptr,
+ BNTokenEscapingType escaping = NoTokenEscapingType
+ ) = 0;
+
+ /*!
+ Generate a single-line text representation of a type
+ \param type Type to print
+ \param platform Platform responsible for this type
+ \param name Name of the type
+ \param escaping Style of escaping literals which may not be parsable
+ \return String representing the type
+ */
+ virtual std::string GetTypeString(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ const QualifiedName& name,
+ BNTokenEscapingType escaping = NoTokenEscapingType
+ );
+ /*!
+ In a single-line text representation of a type, generate the string that should
+ be printed before the type's name.
+
+ \param type Type to print
+ \param platform Platform responsible for this type
+ \param escaping Style of escaping literals which may not be parsable
+ \return String representing the type
+ */
+ virtual std::string GetTypeStringBeforeName(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ BNTokenEscapingType escaping = NoTokenEscapingType
+ );
+ /*!
+ In a single-line text representation of a type, generate the string that should
+ be printed after the type's name.
+
+ \param type Type to print
+ \param platform Platform responsible for this type
+ \param escaping Style of escaping literals which may not be parsable
+ \return String representing the type
+ */
+ virtual std::string GetTypeStringAfterName(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ BNTokenEscapingType escaping = NoTokenEscapingType
+ );
+
+ /*!
+ Generate a multi-line representation of a type
+ \param type Type to print
+ \param data Binary View in which the type is defined
+ \param name Name of the type
+ \param lineWidth Maximum width of lines, in characters
+ \param collapsed Whether to collapse structure/enum blocks
+ \param escaping Style of escaping literals which may not be parsable
+ \return List of type definition lines
+ */
+ virtual std::vector<TypeDefinitionLine> GetTypeLines(
+ Ref<Type> type,
+ Ref<BinaryView> data,
+ const QualifiedName& name,
+ int lineWidth = 80,
+ bool collapsed = false,
+ BNTokenEscapingType escaping = NoTokenEscapingType
+ ) = 0;
+ };
+
+ class CoreTypePrinter: public TypePrinter
+ {
+ public:
+ CoreTypePrinter(BNTypePrinter* printer);
+ virtual ~CoreTypePrinter() {}
+
+ virtual std::vector<InstructionTextToken> GetTypeTokens(Ref<Type> type,
+ Ref<Platform> platform, const QualifiedName& name,
+ uint8_t baseConfidence, BNTokenEscapingType escaping) override;
+ virtual std::vector<InstructionTextToken> GetTypeTokensBeforeName(Ref<Type> type,
+ Ref<Platform> platform, uint8_t baseConfidence,
+ Ref<Type> parentType, BNTokenEscapingType escaping) override;
+ virtual std::vector<InstructionTextToken> GetTypeTokensAfterName(Ref<Type> type,
+ Ref<Platform> platform, uint8_t baseConfidence,
+ Ref<Type> parentType, BNTokenEscapingType escaping) override;
+ virtual std::string GetTypeString(Ref<Type> type, Ref<Platform> platform,
+ const QualifiedName& name, BNTokenEscapingType escaping) override;
+ virtual std::string GetTypeStringBeforeName(Ref<Type> type, Ref<Platform> platform,
+ BNTokenEscapingType escaping) override;
+ virtual std::string GetTypeStringAfterName(Ref<Type> type, Ref<Platform> platform,
+ BNTokenEscapingType escaping) override;
+ virtual std::vector<TypeDefinitionLine> GetTypeLines(Ref<Type> type,
+ Ref<BinaryView> data, const QualifiedName& name, int lineWidth,
+ bool collapsed, BNTokenEscapingType escaping) override;
+ };
+
// DownloadProvider
class DownloadProvider;
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 3eaeb628..4fb254e7 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -34,14 +34,14 @@
// Current ABI version for linking to the core. This is incremented any time
// there are changes to the API that affect linking, including new functions,
// new types, or modifications to existing functions or types.
-#define BN_CURRENT_CORE_ABI_VERSION 19
+#define BN_CURRENT_CORE_ABI_VERSION 20
// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and
// will require rebuilding. The minimum version is increased when there are
// incompatible changes that break binary compatibility, such as changes to
// existing types or functions.
-#define BN_MINIMUM_CORE_ABI_VERSION 19
+#define BN_MINIMUM_CORE_ABI_VERSION 20
#ifdef __GNUC__
#ifdef BINARYNINJACORE_LIBRARY
@@ -178,6 +178,8 @@ extern "C"
struct BNDownloadInstance;
struct BNWebsocketProvider;
struct BNWebsocketClient;
+ struct BNTypeParser;
+ struct BNTypePrinter;
struct BNFlowGraph;
struct BNFlowGraphNode;
struct BNFlowGraphLayoutRequest;
@@ -1991,6 +1993,13 @@ extern "C"
BNType* type;
};
+ struct BNQualifiedNameTypeAndId
+ {
+ BNQualifiedName name;
+ char* id;
+ BNType* type;
+ };
+
struct BNStructureMember
{
BNType* type;
@@ -2028,14 +2037,40 @@ extern "C"
BNLowLevelILFunction* il, BNRelocation* relocation);
};
+ struct BNParsedType
+ {
+ BNQualifiedName name;
+ BNType* type;
+ bool isUser;
+ };
+
struct BNTypeParserResult
{
- BNQualifiedNameAndType* types;
- BNQualifiedNameAndType* variables;
- BNQualifiedNameAndType* functions;
+ BNParsedType* types;
+ BNParsedType* variables;
+ BNParsedType* functions;
size_t typeCount, variableCount, functionCount;
};
+ enum BNTypeParserErrorSeverity
+ {
+ IgnoredSeverity = 0,
+ NoteSeverity = 1,
+ RemarkSeverity = 2,
+ WarningSeverity = 3,
+ ErrorSeverity = 4,
+ FatalSeverity = 5,
+ };
+
+ struct BNTypeParserError
+ {
+ BNTypeParserErrorSeverity severity;
+ char* message;
+ char* fileName;
+ uint64_t line;
+ uint64_t column;
+ };
+
struct BNQualifiedNameList
{
BNQualifiedName* names;
@@ -2409,6 +2444,63 @@ extern "C"
void (*addAction)(void* ctxt, BNMainThreadAction* action);
};
+ struct BNTypeParserCallbacks
+ {
+ void* context;
+ bool (*preprocessSource)(void* ctxt,
+ const char* source, const char* fileName, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ const char* const* options, size_t optionCount,
+ const char* const* includeDirs, size_t includeDirCount,
+ char** output, BNTypeParserError** errors, size_t* errorCount
+ );
+ bool (*parseTypesFromSource)(void* ctxt,
+ const char* source, const char* fileName, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ const char* const* options, size_t optionCount,
+ const char* const* includeDirs, size_t includeDirCount,
+ const char* autoTypeSource, BNTypeParserResult* result,
+ BNTypeParserError** errors, size_t* errorCount
+ );
+ bool (*parseTypeString)(void* ctxt,
+ const char* source, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ BNQualifiedNameAndType* result,
+ BNTypeParserError** errors, size_t* errorCount
+ );
+ void (*freeString)(void* ctxt, char* string);
+ void (*freeResult)(void* ctxt, BNTypeParserResult* result);
+ void (*freeErrorList)(void* ctxt, BNTypeParserError* errors, size_t errorCount);
+ };
+
+ struct BNTypePrinterCallbacks
+ {
+ void* context;
+ bool (*getTypeTokens)(void* ctxt, BNType* type, BNPlatform* platform,
+ BNQualifiedName* name, uint8_t baseConfidence, BNTokenEscapingType escaping,
+ BNInstructionTextToken** result, size_t* resultCount);
+ bool (*getTypeTokensBeforeName)(void* ctxt, BNType* type,
+ BNPlatform* platform, uint8_t baseConfidence, BNType* parentType,
+ BNTokenEscapingType escaping, BNInstructionTextToken** result,
+ size_t* resultCount);
+ bool (*getTypeTokensAfterName)(void* ctxt, BNType* type,
+ BNPlatform* platform, uint8_t baseConfidence, BNType* parentType,
+ BNTokenEscapingType escaping, BNInstructionTextToken** result,
+ size_t* resultCount);
+ bool (*getTypeString)(void* ctxt, BNType* type, BNPlatform* platform,
+ BNQualifiedName* name, BNTokenEscapingType escaping, char** result);
+ bool (*getTypeStringBeforeName)(void* ctxt, BNType* type,
+ BNPlatform* platform, BNTokenEscapingType escaping, char** result);
+ bool (*getTypeStringAfterName)(void* ctxt, BNType* type,
+ BNPlatform* platform, BNTokenEscapingType escaping, char** result);
+ bool (*getTypeLines)(void* ctxt, BNType* type, BNBinaryView* data,
+ BNQualifiedName* name, int lineWidth, bool collapsed,
+ BNTokenEscapingType escaping, BNTypeDefinitionLine** result, size_t* resultCount);
+ void (*freeTokens)(void* ctxt, BNInstructionTextToken* tokens, size_t count);
+ void (*freeString)(void* ctxt, char* string);
+ void (*freeLines)(void* ctxt, BNTypeDefinitionLine* lines, size_t count);
+ };
+
struct BNConstantReference
{
int64_t value;
@@ -4086,6 +4178,7 @@ extern "C"
BINARYNINJACOREAPI BNQualifiedNameAndType* BNGetAnalysisTypeList(BNBinaryView* view, size_t* count);
BINARYNINJACOREAPI void BNFreeTypeList(BNQualifiedNameAndType* types, size_t count);
+ BINARYNINJACOREAPI void BNFreeTypeIdList(BNQualifiedNameTypeAndId* types, size_t count);
BINARYNINJACOREAPI BNQualifiedName* BNGetAnalysisTypeNames(BNBinaryView* view, size_t* count, const char* matching);
BINARYNINJACOREAPI void BNFreeTypeNameList(BNQualifiedName* names, size_t count);
BINARYNINJACOREAPI BNType* BNGetAnalysisTypeByName(BNBinaryView* view, BNQualifiedName* name);
@@ -4096,6 +4189,8 @@ extern "C"
BINARYNINJACOREAPI BNQualifiedName BNDefineAnalysisType(
BNBinaryView* view, const char* id, BNQualifiedName* defaultName, BNType* type);
BINARYNINJACOREAPI void BNDefineUserAnalysisType(BNBinaryView* view, BNQualifiedName* name, BNType* type);
+ BINARYNINJACOREAPI void BNDefineAnalysisTypes(BNBinaryView* view, BNQualifiedNameAndType* types, size_t count, bool (*progress)(void*, size_t, size_t), void* progressContext);
+ BINARYNINJACOREAPI void BNDefineUserAnalysisTypes(BNBinaryView* view, BNQualifiedNameAndType* types, size_t count, bool (*progress)(void*, size_t, size_t), void* progressContext);
BINARYNINJACOREAPI void BNUndefineAnalysisType(BNBinaryView* view, const char* id);
BINARYNINJACOREAPI void BNUndefineUserAnalysisType(BNBinaryView* view, BNQualifiedName* name);
BINARYNINJACOREAPI void BNRenameAnalysisType(
@@ -5009,9 +5104,11 @@ extern "C"
BINARYNINJACOREAPI BNType* BNCreatePointerTypeOfWidth(size_t width, const BNTypeWithConfidence* const type,
BNBoolWithConfidence* cnst, BNBoolWithConfidence* vltl, BNReferenceType refType);
BINARYNINJACOREAPI BNType* BNCreateArrayType(const BNTypeWithConfidence* const type, uint64_t elem);
- BINARYNINJACOREAPI BNType* BNCreateFunctionType(BNTypeWithConfidence* returnValue,
- BNCallingConventionWithConfidence* callingConvention, BNFunctionParameter* params, size_t paramCount,
- BNBoolWithConfidence* varArg, BNOffsetWithConfidence* stackAdjust);
+ BINARYNINJACOREAPI BNType* BNCreateFunctionType(BNTypeWithConfidence* returnValue, BNCallingConventionWithConfidence* callingConvention,
+ BNFunctionParameter* params, size_t paramCount, BNBoolWithConfidence* varArg,
+ BNBoolWithConfidence* canReturn, BNOffsetWithConfidence* stackAdjust,
+ uint32_t* regStackAdjustRegs, BNOffsetWithConfidence* regStackAdjustValues, size_t regStackAdjustCount,
+ BNRegisterSetWithConfidence* returnRegs, BNNameType ft);
BINARYNINJACOREAPI BNType* BNNewTypeReference(BNType* type);
BINARYNINJACOREAPI BNType* BNDuplicateType(BNType* type);
BINARYNINJACOREAPI char* BNGetTypeAndName(BNType* type, BNQualifiedName* name, BNTokenEscapingType escaping);
@@ -5037,9 +5134,11 @@ extern "C"
const BNTypeWithConfidence* const type, BNBoolWithConfidence* cnst, BNBoolWithConfidence* vltl,
BNReferenceType refType);
BINARYNINJACOREAPI BNTypeBuilder* BNCreateArrayTypeBuilder(const BNTypeWithConfidence* const type, uint64_t elem);
- BINARYNINJACOREAPI BNTypeBuilder* BNCreateFunctionTypeBuilder(BNTypeWithConfidence* returnValue,
- BNCallingConventionWithConfidence* callingConvention, BNFunctionParameter* params, size_t paramCount,
- BNBoolWithConfidence* varArg, BNOffsetWithConfidence* stackAdjust);
+ BINARYNINJACOREAPI BNTypeBuilder* BNCreateFunctionTypeBuilder(BNTypeWithConfidence* returnValue, BNCallingConventionWithConfidence* callingConvention,
+ BNFunctionParameter* params, size_t paramCount, BNBoolWithConfidence* varArg,
+ BNBoolWithConfidence* canReturn, BNOffsetWithConfidence* stackAdjust,
+ uint32_t* regStackAdjustRegs, BNOffsetWithConfidence* regStackAdjustValues, size_t regStackAdjustCount,
+ BNRegisterSetWithConfidence* returnRegs, BNNameType ft);
BINARYNINJACOREAPI BNType* BNFinalizeTypeBuilder(BNTypeBuilder* type);
BINARYNINJACOREAPI BNTypeBuilder* BNDuplicateTypeBuilder(BNTypeBuilder* type);
BINARYNINJACOREAPI char* BNGetTypeBuilderTypeAndName(BNTypeBuilder* type, BNQualifiedName* name);
@@ -5258,8 +5357,71 @@ extern "C"
BINARYNINJACOREAPI bool BNParseTypesFromSourceFile(BNPlatform* platform, const char* fileName,
BNTypeParserResult* result, char** errors, const char** includeDirs, size_t includeDirCount,
const char* autoTypeSource);
- BINARYNINJACOREAPI void BNFreeTypeParserResult(BNTypeParserResult* result);
+ BINARYNINJACOREAPI BNTypeParser* BNRegisterTypeParser(
+ const char* name, BNTypeParserCallbacks* callbacks);
+ BINARYNINJACOREAPI BNTypeParser** BNGetTypeParserList(size_t* count);
+ BINARYNINJACOREAPI void BNFreeTypeParserList(BNTypeParser** parsers);
+ BINARYNINJACOREAPI BNTypeParser* BNGetTypeParserByName(const char* name);
+
+ BINARYNINJACOREAPI char* BNGetTypeParserName(BNTypeParser* parser);
+
+ BINARYNINJACOREAPI bool BNTypeParserPreprocessSource(BNTypeParser* parser,
+ const char* source, const char* fileName, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ const char* const* options, size_t optionCount,
+ const char* const* includeDirs, size_t includeDirCount,
+ char** output, BNTypeParserError** errors, size_t* errorCount
+ );
+ BINARYNINJACOREAPI bool BNTypeParserParseTypesFromSource(BNTypeParser* parser,
+ const char* source, const char* fileName, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ const char* const* options, size_t optionCount,
+ const char* const* includeDirs, size_t includeDirCount,
+ const char* autoTypeSource, BNTypeParserResult* result,
+ BNTypeParserError** errors, size_t* errorCount
+ );
+ BINARYNINJACOREAPI bool BNTypeParserParseTypeString(BNTypeParser* parser,
+ const char* source, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ BNQualifiedNameAndType* result,
+ BNTypeParserError** errors, size_t* errorCount
+ );
+
+ BINARYNINJACOREAPI BNTypePrinter* BNRegisterTypePrinter(
+ const char* name, BNTypePrinterCallbacks* callbacks);
+ BINARYNINJACOREAPI BNTypePrinter** BNGetTypePrinterList(size_t* count);
+ BINARYNINJACOREAPI void BNFreeTypePrinterList(BNTypePrinter** printers);
+ BINARYNINJACOREAPI BNTypePrinter* BNGetTypePrinterByName(const char* name);
+
+ BINARYNINJACOREAPI char* BNGetTypePrinterName(BNTypePrinter* printer);
+
+ BINARYNINJACOREAPI bool BNGetTypePrinterTypeTokens(BNTypePrinter* printer,
+ BNType* type, BNPlatform* platform, BNQualifiedName* name,
+ uint8_t baseConfidence, BNTokenEscapingType escaping,
+ BNInstructionTextToken** result, size_t* resultCount);
+ BINARYNINJACOREAPI bool BNGetTypePrinterTypeTokensBeforeName(BNTypePrinter* printer,
+ BNType* type, BNPlatform* platform, uint8_t baseConfidence, BNType* parentType,
+ BNTokenEscapingType escaping, BNInstructionTextToken** result,
+ size_t* resultCount);
+ BINARYNINJACOREAPI bool BNGetTypePrinterTypeTokensAfterName(BNTypePrinter* printer,
+ BNType* type, BNPlatform* platform, uint8_t baseConfidence, BNType* parentType,
+ BNTokenEscapingType escaping, BNInstructionTextToken** result,
+ size_t* resultCount);
+ BINARYNINJACOREAPI bool BNGetTypePrinterTypeString(BNTypePrinter* printer,
+ BNType* type, BNPlatform* platform, BNQualifiedName* name,
+ BNTokenEscapingType escaping, char** result);
+ BINARYNINJACOREAPI bool BNGetTypePrinterTypeStringBeforeName(BNTypePrinter* printer,
+ BNType* type, BNPlatform* platform, BNTokenEscapingType escaping, char** result);
+ BINARYNINJACOREAPI bool BNGetTypePrinterTypeStringAfterName(BNTypePrinter* printer,
+ BNType* type, BNPlatform* platform, BNTokenEscapingType escaping, char** result);
+ BINARYNINJACOREAPI bool BNGetTypePrinterTypeLines(BNTypePrinter* printer,
+ BNType* type, BNBinaryView* data,
+ BNQualifiedName* name, int lineWidth, bool collapsed,
+ BNTokenEscapingType escaping, BNTypeDefinitionLine** result, size_t* resultCount);
+
+ BINARYNINJACOREAPI void BNFreeTypeParserResult(BNTypeParserResult* result);
+ BINARYNINJACOREAPI void BNFreeTypeParserErrors(BNTypeParserError* errors, size_t count);
// Updates
BINARYNINJACOREAPI BNUpdateChannel* BNGetUpdateChannels(size_t* count, char** errors);
BINARYNINJACOREAPI void BNFreeUpdateChannelList(BNUpdateChannel* list, size_t count);
diff --git a/binaryview.cpp b/binaryview.cpp
index d5390ddc..78b653a0 100644
--- a/binaryview.cpp
+++ b/binaryview.cpp
@@ -3171,6 +3171,64 @@ void BinaryView::DefineUserType(const QualifiedName& name, Ref<Type> type)
}
+struct ProgressCallback
+{
+ std::function<bool(size_t, size_t)> func;
+};
+
+
+void BinaryView::DefineTypes(const vector<QualifiedNameAndType>& types, std::function<bool(size_t, size_t)> progress)
+{
+ BNQualifiedNameAndType* apiTypes = new BNQualifiedNameAndType[types.size()];
+ for (size_t i = 0; i < types.size(); i++)
+ {
+ apiTypes[i].name = types[i].name.GetAPIObject();
+ apiTypes[i].type = types[i].type->GetObject();
+ }
+
+ ProgressCallback cb;
+ cb.func = progress;
+ BNDefineAnalysisTypes(m_object, apiTypes, types.size(), [](void* ctxt, size_t cur, size_t total) {
+ ProgressCallback* cb = (ProgressCallback*)ctxt;
+ if (cb->func)
+ return cb->func(cur, total);
+ return true;
+ }, &cb);
+
+ for (size_t i = 0; i < types.size(); i++)
+ {
+ QualifiedName::FreeAPIObject(&apiTypes[i].name);
+ }
+ delete [] apiTypes;
+}
+
+
+void BinaryView::DefineUserTypes(const vector<QualifiedNameAndType>& types, std::function<bool(size_t, size_t)> progress)
+{
+ BNQualifiedNameAndType* apiTypes = new BNQualifiedNameAndType[types.size()];
+ for (size_t i = 0; i < types.size(); i++)
+ {
+ apiTypes[i].name = types[i].name.GetAPIObject();
+ apiTypes[i].type = types[i].type->GetObject();
+ }
+
+ ProgressCallback cb;
+ cb.func = progress;
+ BNDefineUserAnalysisTypes(m_object, apiTypes, types.size(), [](void* ctxt, size_t cur, size_t total) {
+ ProgressCallback* cb = (ProgressCallback*)ctxt;
+ if (cb->func)
+ return cb->func(cur, total);
+ return true;
+ }, &cb);
+
+ for (size_t i = 0; i < types.size(); i++)
+ {
+ QualifiedName::FreeAPIObject(&apiTypes[i].name);
+ }
+ delete [] apiTypes;
+}
+
+
void BinaryView::UndefineType(const string& id)
{
BNUndefineAnalysisType(m_object, id.c_str());
diff --git a/demangle.cpp b/demangle.cpp
index 66395fc1..277c621f 100644
--- a/demangle.cpp
+++ b/demangle.cpp
@@ -72,13 +72,13 @@ namespace BinaryNinja {
QualifiedName SimplifyName::to_qualified_name(const string& input, bool simplify)
{
- return (QualifiedName)SimplifyName(input, SimplifierDest::fqn, simplify);
+ return SimplifyName(input, SimplifierDest::fqn, simplify).operator QualifiedName();
}
QualifiedName SimplifyName::to_qualified_name(const QualifiedName& input)
{
- return (QualifiedName)SimplifyName(input.GetString(), SimplifierDest::fqn, true);
+ return SimplifyName(input.GetString(), SimplifierDest::fqn, true).operator QualifiedName();
}
diff --git a/python/__init__.py b/python/__init__.py
index a230dec4..2d0f7065 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -65,6 +65,8 @@ from .workflow import *
from .commonil import *
from .database import *
from .secretsprovider import *
+from .typeparser import *
+from .typeprinter import *
# We import each of these by name to prevent conflicts between
# log.py and the function 'log' which we don't import below
from .log import (
diff --git a/python/generator.cpp b/python/generator.cpp
index 99d2d455..cc4b5102 100644
--- a/python/generator.cpp
+++ b/python/generator.cpp
@@ -239,7 +239,19 @@ int main(int argc, char* argv[])
map<QualifiedName, Ref<Type>> types, vars, funcs;
string errors;
auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture());
+
+ string oldParser;
+ if (Settings::Instance()->Contains("analysis.types.parserNamer"))
+ oldParser = Settings::Instance()->Get<string>("analysis.types.parserNamer");
+ Settings::Instance()->Set("analysis.types.parserName", "CoreTypeParser");
+
bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors);
+
+ if (!oldParser.empty())
+ Settings::Instance()->Set("analysis.types.parserName", oldParser);
+ else
+ Settings::Instance()->Reset("analysis.types.parserName");
+
fprintf(stderr, "Errors: %s\n", errors.c_str());
if (!ok)
return 1;
diff --git a/python/typeparser.py b/python/typeparser.py
new file mode 100644
index 00000000..ac5134f0
--- /dev/null
+++ b/python/typeparser.py
@@ -0,0 +1,589 @@
+# Copyright (c) 2015-2022 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 abc
+import ctypes
+import dataclasses
+from json import dumps
+from typing import List, Tuple, Optional
+
+import sys
+import traceback
+
+# Binary Ninja Components
+import binaryninja
+import binaryninja._binaryninjacore as core
+
+from .settings import Settings
+from . import platform
+from . import types
+from .log import log_error
+from .enums import TypeParserErrorSeverity
+
+
+@dataclasses.dataclass(frozen=True)
+class QualifiedNameTypeAndId:
+ name: 'types.QualifiedNameType'
+ id: str
+ type: 'types.Type'
+
+ @classmethod
+ def _from_core_struct(cls, struct: core.BNQualifiedNameTypeAndId) -> 'QualifiedNameTypeAndId':
+ name = types.QualifiedName._from_core_struct(struct.name)
+ type = types.Type.create(handle=core.BNNewTypeReference(struct.type))
+ return QualifiedNameTypeAndId(name, struct.id, type)
+
+ def _to_core_struct(self) -> core.BNQualifiedNameTypeAndId:
+ result = core.BNQualifiedNameTypeAndId()
+ result.name = types.QualifiedName(self.name)._to_core_struct()
+ result.type = core.BNNewTypeReference(self.type.handle)
+ result.id = self.id
+ return result
+
+
+@dataclasses.dataclass(frozen=True)
+class TypeParserError:
+ severity: TypeParserErrorSeverity
+ message: str
+ file_name: str
+ line: int
+ column: int
+
+ def __str__(self):
+ text = ""
+ if self.severity == TypeParserErrorSeverity.ErrorSeverity \
+ or self.severity == TypeParserErrorSeverity.FatalSeverity:
+ text += "error: "
+ if self.severity == TypeParserErrorSeverity.WarningSeverity:
+ text += "warning: "
+ if self.severity == TypeParserErrorSeverity.NoteSeverity:
+ text += "note: "
+ if self.severity == TypeParserErrorSeverity.RemarkSeverity:
+ text += "remark: "
+ if self.severity == TypeParserErrorSeverity.IgnoredSeverity:
+ text += "ignored: "
+
+ if self.file_name == "":
+ text += f"<unknown>: {self.message}\n"
+ else:
+ text += f"{self.file_name}: {self.line}:{self.column} {self.message}\n"
+ return text
+
+ @classmethod
+ def _from_core_struct(cls, struct: core.BNTypeParserError) -> 'TypeParserError':
+ return TypeParserError(struct.severity, struct.message, struct.fileName, struct.line, struct.column)
+
+ def _to_core_struct(self) -> core.BNTypeParserError:
+ result = core.BNTypeParserError()
+ result.severity = self.severity
+ result.message = self.message
+ result.fileName = self.file_name
+ result.line = self.line
+ result.column = self.column
+ return result
+
+
+@dataclasses.dataclass(frozen=True)
+class ParsedType:
+ name: 'types.QualifiedNameType'
+ type: 'types.Type'
+ is_user: bool
+
+ @classmethod
+ def _from_core_struct(cls, struct: core.BNParsedType) -> 'ParsedType':
+ name = types.QualifiedName._from_core_struct(struct.name)
+ type = types.Type.create(handle=core.BNNewTypeReference(struct.type))
+ return ParsedType(name, type, struct.isUser)
+
+ def _to_core_struct(self) -> core.BNParsedType:
+ result = core.BNParsedType()
+ result.name = types.QualifiedName(self.name)._to_core_struct()
+ result.type = core.BNNewTypeReference(self.type.handle)
+ result.isUser = self.is_user
+ return result
+
+
+@dataclasses.dataclass(frozen=True)
+class TypeParserResult:
+ types: List[ParsedType]
+ variables: List[ParsedType]
+ functions: List[ParsedType]
+
+ def __repr__(self):
+ return f"<types: {self.types}, variables: {self.variables}, functions: {self.functions}>"
+
+ @classmethod
+ def _from_core_struct(cls, struct: core.BNTypeParserResult) -> 'TypeParserResult':
+ types = []
+ variables = []
+ functions = []
+ for i in range(struct.typeCount):
+ types.append(ParsedType._from_core_struct(struct.types[i]))
+ for i in range(struct.variableCount):
+ variables.append(ParsedType._from_core_struct(struct.variables[i]))
+ for i in range(struct.functionCount):
+ functions.append(ParsedType._from_core_struct(struct.functions[i]))
+ return TypeParserResult(types, variables, functions)
+
+ def _to_core_struct(self) -> core.BNTypeParserResult:
+ result = core.BNTypeParserResult()
+ result.typeCount = len(self.types)
+ result.variableCount = len(self.variables)
+ result.functionCount = len(self.functions)
+ result.types = (core.BNParsedType * len(self.types))()
+ result.variables = (core.BNParsedType * len(self.variables))()
+ result.functions = (core.BNParsedType * len(self.functions))()
+
+ for (i, type) in enumerate(self.types):
+ result.types[i] = type._to_core_struct()
+ for (i, variable) in enumerate(self.variables):
+ result.variables[i] = variable._to_core_struct()
+ for (i, function) in enumerate(self.functions):
+ result.functions[i] = function._to_core_struct()
+
+ return result
+
+
+def to_bytes(field):
+ if type(field) == bytes:
+ return field
+ if type(field) == str:
+ return field.encode()
+ return str(field).encode()
+
+
+class _TypeParserMetaclass(type):
+ def __iter__(self):
+ binaryninja._init_plugins()
+ count = ctypes.c_ulonglong()
+ types = core.BNGetTypeParserList(count)
+ try:
+ for i in range(0, count.value):
+ yield CoreTypeParser(types[i])
+ finally:
+ core.BNFreeTypeParserList(types)
+
+ def __getitem__(self, value):
+ binaryninja._init_plugins()
+ handle = core.BNGetTypeParserByName(str(value))
+ if handle is None:
+ raise KeyError(f"'{value}' is not a valid TypeParser")
+ return CoreTypeParser(handle)
+
+ @property
+ def default(self):
+ name = binaryninja.Settings().get_string("analysis.types.parserName")
+ return CoreTypeParser[name]
+
+
+class TypeParser(metaclass=_TypeParserMetaclass):
+ name = None
+ _registered_parsers = []
+ _cached_string = None
+ _cached_result = None
+ _cached_error = None
+
+ def __init__(self, handle=None):
+ if handle is not None:
+ self.handle = core.handle_of_type(handle, core.BNTypeParser)
+ self.__dict__["name"] = core.BNGetTypeParserName(handle)
+
+ def register(self):
+ assert self.__class__.name is not None
+
+ self._cb = core.BNTypeParserCallbacks()
+ self._cb.context = 0
+ self._cb.preprocessSource = self._cb.preprocessSource.__class__(self._preprocess_source)
+ self._cb.parseTypesFromSource = self._cb.parseTypesFromSource.__class__(self._parse_types_from_source)
+ self._cb.parseTypeString = self._cb.parseTypeString.__class__(self._parse_type_string)
+ self._cb.freeString = self._cb.freeString.__class__(self._free_string)
+ self._cb.freeResult = self._cb.freeResult.__class__(self._free_result)
+ self._cb.freeErrorList = self._cb.freeErrorList.__class__(self._free_error_list)
+ self.handle = core.BNRegisterTypeParser(self.__class__.name, self._cb)
+ self.__class__._registered_parsers.append(self)
+
+ def __str__(self):
+ return f'<TypeParser: {self.name}>'
+
+ def __repr__(self):
+ return f'<TypeParser: {self.name}>'
+
+ def _preprocess_source(
+ self, ctxt, source, fileName, platform_, existingTypes, existingTypeCount,
+ options, optionCount, includeDirs, includeDirCount,
+ output, errors, errorCount
+ ) -> bool:
+ try:
+ source_py = core.pyNativeStr(source)
+ file_name_py = core.pyNativeStr(fileName)
+ platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_))
+
+ existing_types_py = []
+ for i in range(existingTypeCount):
+ existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i]))
+
+ options_py = []
+ for i in range(optionCount):
+ options_py.append(core.pyNativeStr(options[i]))
+
+ include_dirs_py = []
+ for i in range(includeDirCount):
+ include_dirs_py.append(core.pyNativeStr(includeDirs[i]))
+
+ (output_py, errors_py) = self.preprocess_source(
+ source_py, file_name_py, platform_py, existing_types_py, options_py,
+ include_dirs_py)
+
+ if output_py is not None and output is not None:
+ TypeParser._cached_string = core.cstr(output_py)
+ output[0] = TypeParser._cached_string
+ if errorCount is not None:
+ errorCount[0] = len(errors_py)
+ if errors is not None:
+ errors_out = (core.BNTypeParserError * len(errors_py))()
+ for i in range(len(errors_py)):
+ errors_out[i] = errors_py[i]._to_core_struct()
+ TypeParser._cached_error = errors_out
+ errors[0] = errors_out
+
+ return output_py is not None
+ except:
+ errorCount[0] = 0
+ log_error(traceback.format_exc())
+ return False
+
+ def _parse_types_from_source(
+ self, ctxt, source, fileName, platform_, existingTypes, existingTypeCount,
+ options, optionCount, includeDirs, includeDirCount, autoTypeSource,
+ result, errors, errorCount
+ ) -> bool:
+ try:
+ source_py = core.pyNativeStr(source)
+ file_name_py = core.pyNativeStr(fileName)
+ platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_))
+
+ existing_types_py = []
+ for i in range(existingTypeCount):
+ existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i]))
+
+ options_py = []
+ for i in range(optionCount):
+ options_py.append(core.pyNativeStr(options[i]))
+
+ include_dirs_py = []
+ for i in range(includeDirCount):
+ include_dirs_py.append(core.pyNativeStr(includeDirs[i]))
+
+ auto_type_source = core.pyNativeStr(autoTypeSource)
+
+ (result_py, errors_py) = self.parse_types_from_source(
+ source_py, file_name_py, platform_py, existing_types_py, options_py,
+ include_dirs_py, auto_type_source)
+
+ if result_py is not None and result is not None:
+ result_struct = result_py._to_core_struct()
+ TypeParser._cached_result = result_struct
+ result[0] = result_struct
+
+ if errorCount is not None:
+ errorCount[0] = len(errors_py)
+ if errors is not None:
+ errors_out = (core.BNTypeParserError * len(errors_py))()
+ for i in range(len(errors_py)):
+ errors_out[i] = errors_py[i]._to_core_struct()
+ TypeParser._cached_error = errors_out
+ errors[0] = errors_out
+
+ return result_py is not None
+ except:
+ result[0].typeCount = 0
+ result[0].variableCount = 0
+ result[0].functionCount = 0
+ errorCount[0] = 0
+ log_error(traceback.format_exc())
+ return False
+
+ def _parse_type_string(
+ self, ctxt, source, platform_, existingTypes, existingTypeCount,
+ result, errors, errorCount
+ ) -> bool:
+ try:
+ source_py = core.pyNativeStr(source)
+ platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_))
+
+ existing_types_py = []
+ for i in range(existingTypeCount):
+ existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i]))
+
+ (result_py, errors_py) = self.parse_type_string(
+ source_py, platform_py, existing_types_py)
+
+ if result_py is not None and result is not None:
+ result[0].name = types.QualifiedName(result_py[0])._to_core_struct()
+ result[0].type = core.BNNewTypeReference(result_py[1].handle)
+
+ if errorCount is not None:
+ errorCount[0] = len(errors_py)
+ if errors is not None:
+ errors_out = (core.BNTypeParserError * len(errors_py))()
+ for i in range(len(errors_py)):
+ errors_out[i] = errors_py[i]._to_core_struct()
+ TypeParser._cached_error = errors_out
+ errors[0] = errors_out
+
+ return result_py is not None
+ except:
+ errorCount[0] = 0
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_string(
+ self, ctxt, string
+ ) -> bool:
+ try:
+ TypeParser._cached_string = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_result(
+ self, ctxt, result
+ ) -> bool:
+ try:
+ if TypeParser._cached_result is not None:
+ for i in range(TypeParser._cached_result.typeCount):
+ core.BNFreeType(TypeParser._cached_result.types[i].type)
+ for i in range(TypeParser._cached_result.variableCount):
+ core.BNFreeType(TypeParser._cached_result.variables[i].type)
+ for i in range(TypeParser._cached_result.functionCount):
+ core.BNFreeType(TypeParser._cached_result.functions[i].type)
+ TypeParser._cached_result = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_error_list(
+ self, ctxt, errors, errorCount
+ ) -> bool:
+ try:
+ TypeParser._cached_error = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def preprocess_source(
+ self, source: str, file_name: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
+ options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None
+ ) -> Tuple[Optional[str], List[TypeParserError]]:
+ raise NotImplementedError("Not implemented")
+
+ def parse_types_from_source(
+ self, source: str, file_name: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
+ options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None,
+ auto_type_source: str = ""
+ ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]:
+ raise NotImplementedError("Not implemented")
+
+ def parse_type_string(
+ self, source: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None
+ ) -> Tuple[Optional[Tuple['types.QualifiedNameType', 'types.Type']], List[TypeParserError]]:
+ raise NotImplementedError("Not implemented")
+
+
+class CoreTypeParser(TypeParser):
+
+ def preprocess_source(
+ self, source: str, file_name: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
+ options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None
+ ) -> Tuple[Optional[str], List[TypeParserError]]:
+ if existing_types is None:
+ existing_types = []
+ if options is None:
+ options = []
+ if include_dirs is None:
+ include_dirs = []
+
+ existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))()
+ for (i, qnatid) in enumerate(existing_types):
+ existing_types_cpp[i] = qnatid._to_core_struct()
+
+ options_cpp = (ctypes.c_char_p * len(options))()
+ for (i, s) in enumerate(options):
+ options_cpp[i] = core.cstr(s)
+
+ include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))()
+ for (i, s) in enumerate(include_dirs):
+ include_dirs_cpp[i] = core.cstr(s)
+
+ output_cpp = ctypes.c_char_p()
+ errors_cpp = ctypes.POINTER(core.BNTypeParserError)()
+ error_count = ctypes.c_size_t()
+
+ success = core.BNTypeParserPreprocessSource(
+ self.handle, source, file_name, platform.handle,
+ existing_types_cpp, len(existing_types), options_cpp, len(options),
+ include_dirs_cpp, len(include_dirs),
+ output_cpp, errors_cpp, error_count
+ )
+
+ if success:
+ output = core.pyNativeStr(output_cpp.value)
+ core.free_string(output_cpp)
+ else:
+ output = None
+
+ errors = []
+ for i in range(error_count.value):
+ errors.append(TypeParserError._from_core_struct(errors_cpp[i]))
+ core.BNFreeTypeParserErrors(errors_cpp, error_count.value)
+
+ return output, errors
+
+ def parse_types_from_source(
+ self, source: str, file_name: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
+ options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None,
+ auto_type_source: str = ""
+ ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]:
+ if existing_types is None:
+ existing_types = []
+ if options is None:
+ options = []
+ if include_dirs is None:
+ include_dirs = []
+
+ existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))()
+ for (i, qnatid) in enumerate(existing_types):
+ existing_types_cpp[i] = qnatid._to_core_struct()
+
+ options_cpp = (ctypes.c_char_p * len(options))()
+ for (i, s) in enumerate(options):
+ options_cpp[i] = core.cstr(s)
+
+ include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))()
+ for (i, s) in enumerate(include_dirs):
+ include_dirs_cpp[i] = core.cstr(s)
+
+ result_cpp = core.BNTypeParserResult()
+ errors_cpp = ctypes.POINTER(core.BNTypeParserError)()
+ error_count = ctypes.c_size_t()
+
+ success = core.BNTypeParserParseTypesFromSource(
+ self.handle, source, file_name, platform.handle,
+ existing_types_cpp, len(existing_types), options_cpp, len(options),
+ include_dirs_cpp, len(include_dirs), auto_type_source,
+ result_cpp, errors_cpp, error_count
+ )
+
+ if success:
+ result = TypeParserResult._from_core_struct(result_cpp)
+ else:
+ result = None
+ core.BNFreeTypeParserResult(result_cpp)
+
+ errors = []
+ for i in range(error_count.value):
+ errors.append(TypeParserError._from_core_struct(errors_cpp[i]))
+ core.BNFreeTypeParserErrors(errors_cpp, error_count.value)
+
+ return result, errors
+
+
+ def parse_type_string(
+ self, source: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None
+ ) -> Tuple[Optional[Tuple['types.QualifiedNameType', 'types.Type']], List[TypeParserError]]:
+ if existing_types is None:
+ existing_types = []
+ existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))()
+ for (i, qnatid) in enumerate(existing_types):
+ existing_types_cpp[i] = qnatid._to_core_struct()
+
+ result_cpp = core.BNQualifiedNameAndType()
+ errors_cpp = ctypes.POINTER(core.BNTypeParserError)()
+ error_count = ctypes.c_size_t()
+
+ success = core.BNTypeParserParseTypeString(
+ self.handle, source, platform.handle,
+ existing_types_cpp, len(existing_types),
+ result_cpp, errors_cpp, error_count
+ )
+
+ if success:
+ result = (
+ types.QualifiedName._from_core_struct(result_cpp.name),
+ types.Type.create(handle=core.BNNewTypeReference(result_cpp.type))
+ )
+ core.BNFreeQualifiedNameAndType(result_cpp)
+ else:
+ result = None
+
+ errors = []
+ for i in range(error_count.value):
+ errors.append(TypeParserError._from_core_struct(errors_cpp[i]))
+ core.BNFreeTypeParserErrors(errors_cpp, error_count.value)
+
+ return result, errors
+
+
+def preprocess_source(source: str, filename: str = None,
+ include_dirs: Optional[List[str]] = None) -> Tuple[Optional[str], str]:
+ """
+ ``preprocess_source`` run the C preprocessor on the given source or source filename.
+
+ :param str source: source to pre-process
+ :param str filename: optional filename to pre-process
+ :param include_dirs: list of string directories to use as include directories.
+ :type include_dirs: list(str)
+ :return: returns a tuple of (preprocessed_source, error_string)
+ :rtype: tuple(str,str)
+ :Example:
+
+ >>> source = "#define TEN 10\\nint x[TEN];\\n"
+ >>> preprocess_source(source)
+ ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '')
+ >>>
+ """
+ if filename is None:
+ filename = "input"
+ if include_dirs is None:
+ include_dirs = []
+ dir_buf = (ctypes.c_char_p * len(include_dirs))()
+ for i in range(0, len(include_dirs)):
+ dir_buf[i] = include_dirs[i].encode('charmap')
+ output = ctypes.c_char_p()
+ errors = ctypes.c_char_p()
+ result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs))
+ assert output.value is not None
+ assert errors.value is not None
+ output_str = output.value.decode('utf-8')
+ error_str = errors.value.decode('utf-8')
+ core.free_string(output)
+ core.free_string(errors)
+ if result:
+ return output_str, error_str
+ return None, error_str
diff --git a/python/typeprinter.py b/python/typeprinter.py
new file mode 100644
index 00000000..354084dd
--- /dev/null
+++ b/python/typeprinter.py
@@ -0,0 +1,390 @@
+# Copyright (c) 2015-2022 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 abc
+import ctypes
+import dataclasses
+from json import dumps
+from typing import List, Tuple, Optional
+
+import sys
+import traceback
+
+# Binary Ninja Components
+import binaryninja
+import binaryninja._binaryninjacore as core
+
+from .settings import Settings
+from . import platform as _platform
+from . import types
+from . import function as _function
+from . import binaryview
+from .log import log_error
+from .enums import TokenEscapingType
+
+
+def to_bytes(field):
+ if type(field) == bytes:
+ return field
+ if type(field) == str:
+ return field.encode()
+ return str(field).encode()
+
+
+class _TypePrinterMetaclass(type):
+ def __iter__(self):
+ binaryninja._init_plugins()
+ count = ctypes.c_ulonglong()
+ types = core.BNGetTypePrinterList(count)
+ try:
+ for i in range(0, count.value):
+ yield CoreTypePrinter(types[i])
+ finally:
+ core.BNFreeTypePrinterList(types)
+
+ def __getitem__(self, value):
+ binaryninja._init_plugins()
+ handle = core.BNGetTypePrinterByName(str(value))
+ if handle is None:
+ raise KeyError(f"'{value}' is not a valid TypePrinter")
+ return CoreTypePrinter(handle)
+
+ @property
+ def default(self):
+ name = binaryninja.Settings().get_string("analysis.types.printerName")
+ return CoreTypePrinter[name]
+
+
+class TypePrinter(metaclass=_TypePrinterMetaclass):
+ name = None
+ _registered_printers = []
+ _cached_tokens = None
+ _cached_string = None
+ _cached_error = None
+
+ def __init__(self, handle=None):
+ if handle is not None:
+ self.handle = core.handle_of_type(handle, core.BNTypePrinter)
+ self.__dict__["name"] = core.BNGetTypePrinterName(handle)
+
+ def register(self):
+ assert self.__class__.name is not None
+
+ self._cb = core.BNTypePrinterCallbacks()
+ self._cb.context = 0
+ self._cb.getTypeTokens = self._cb.getTypeTokens.__class__(self._get_type_tokens)
+ self._cb.getTypeTokensBeforeName = self._cb.getTypeTokensBeforeName.__class__(self._get_type_tokens_before_name)
+ self._cb.getTypeTokensAfterName = self._cb.getTypeTokensAfterName.__class__(self._get_type_tokens_after_name)
+ self._cb.getTypeString = self._cb.getTypeString.__class__(self._get_type_string)
+ self._cb.getTypeStringBeforeName = self._cb.getTypeStringBeforeName.__class__(self._get_type_string_before_name)
+ self._cb.getTypeStringAfterName = self._cb.getTypeStringAfterName.__class__(self._get_type_string_after_name)
+ self._cb.getTypeLines = self._cb.getTypeLines.__class__(self._get_type_lines)
+ self._cb.freeTokens = self._cb.freeTokens.__class__(self._free_tokens)
+ self._cb.freeString = self._cb.freeString.__class__(self._free_string)
+ self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines)
+ self.handle = core.BNRegisterTypePrinter(self.__class__.name, self._cb)
+ self.__class__._registered_printers.append(self)
+
+ def __str__(self):
+ return f'<TypePrinter: {self.name}>'
+
+ def __repr__(self):
+ return f'<TypePrinter: {self.name}>'
+
+ def _get_type_tokens(self, ctxt, type, platform, name, base_confidence, escaping, result, result_count):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ result_py = self.get_type_tokens(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ types.QualifiedName._from_core_struct(name.contents), base_confidence, escaping)
+
+ TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py)
+ result[0] = TypePrinter._cached_tokens
+ result_count[0] = len(result_py)
+
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_tokens_before_name(self, ctxt, type, platform, base_confidence, parent_type, escaping, result, result_count):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ parent_type_py = None
+ if parent_type:
+ parent_type_py = types.Type(handle=core.BNNewTypeReference(parent_type))
+ result_py = self.get_type_tokens_before_name(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ base_confidence, parent_type_py, escaping)
+
+ TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py)
+ result[0] = TypePrinter._cached_tokens
+ result_count[0] = len(result_py)
+
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_tokens_after_name(self, ctxt, type, platform, base_confidence, parent_type, escaping, result, result_count):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ parent_type_py = None
+ if parent_type:
+ parent_type_py = types.Type(handle=core.BNNewTypeReference(parent_type))
+ result_py = self.get_type_tokens_after_name(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ base_confidence, parent_type_py, escaping)
+
+ TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py)
+ result[0] = TypePrinter._cached_tokens
+ result_count[0] = len(result_py)
+
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_string(self, ctxt, type, platform, name, escaping, result):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ result_py = self.get_type_string(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ types.QualifiedName._from_core_struct(name.contents), escaping)
+
+ TypePrinter._cached_string = core.cstr(result_py)
+ result[0] = TypePrinter._cached_string
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_string_before_name(self, ctxt, type, platform, escaping, result):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ result_py = self.get_type_string_before_name(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ escaping)
+
+ TypePrinter._cached_string = core.cstr(result_py)
+ result[0] = TypePrinter._cached_string
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_string_after_name(self, ctxt, type, platform, escaping, result):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ result_py = self.get_type_string_after_name(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ escaping)
+
+ TypePrinter._cached_string = core.cstr(result_py)
+ result[0] = TypePrinter._cached_string
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_lines(self, ctxt, type, data, name, line_width, collapsed, escaping, result, result_count):
+ try:
+ result_py = self.get_type_lines(
+ types.Type(handle=core.BNNewTypeReference(type)),
+ binaryview.BinaryView(handle=core.BNNewViewReference(data)),
+ types.QualifiedName._from_core_struct(name.contents),
+ line_width, collapsed, escaping)
+
+ TypePrinter._cached_lines = (core.BNTypeDefinitionLine * len(result_py))()
+ for (i, line) in enumerate(result_py):
+ TypePrinter._cached_lines[i] = line._to_core_struct()
+ result[0] = TypePrinter._cached_lines
+ result_count[0] = len(result_py)
+
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_tokens(self, ctxt, tokens, count):
+ try:
+ TypePrinter._cached_tokens = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_string(self, ctxt, string):
+ try:
+ TypePrinter._cached_string = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_lines(self, ctxt, lines, count):
+ try:
+ for line in TypePrinter._cached_lines:
+ core.BNFreeType(line.type)
+ core.BNFreeType(line.rootType)
+ TypePrinter._cached_lines = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def get_type_tokens(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", base_confidence: int = core.max_confidence, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]:
+ raise NotImplementedError()
+
+ def get_type_tokens_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]:
+ raise NotImplementedError()
+
+ def get_type_tokens_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]:
+ raise NotImplementedError()
+
+ def get_type_string(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ raise NotImplementedError()
+
+ def get_type_string_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ raise NotImplementedError()
+
+ def get_type_string_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ raise NotImplementedError()
+
+ def get_type_lines(self, type: types.Type, data: binaryview.BinaryView, name: types.QualifiedNameType, line_width = 80, collapsed = False, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[types.TypeDefinitionLine]:
+ raise NotImplementedError()
+
+
+class CoreTypePrinter(TypePrinter):
+
+ def get_type_tokens(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ name: types.QualifiedNameType = "", base_confidence: int = core.max_confidence,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[
+ _function.InstructionTextToken]:
+ if not isinstance(name, types.QualifiedName):
+ name = types.QualifiedName(name)
+ count = ctypes.c_ulonglong()
+ name_cpp = name._to_core_struct()
+ result_cpp = ctypes.POINTER(core.BNInstructionTextToken)()
+ if not core.BNGetTypePrinterTypeTokens(self.handle, type.handle, None if platform is None else platform.handle, name_cpp, base_confidence, ctypes.c_int(escaping), result_cpp, count):
+ raise RuntimeError("BNGetTypePrinterTypeTokens returned False")
+
+ result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value)
+ core.BNFreeInstructionText(result_cpp.contents, count.value)
+ return result
+
+ def get_type_tokens_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[
+ _function.InstructionTextToken]:
+ count = ctypes.c_ulonglong()
+ result_cpp = ctypes.POINTER(core.BNInstructionTextToken)()
+ parent_type_cpp = None
+ if parent_type is not None:
+ parent_type_cpp = parent_type.handle
+ if not core.BNGetTypePrinterTypeTokensBeforeName(self.handle, type.handle, None if platform is None else platform.handle, base_confidence, parent_type_cpp, ctypes.c_int(escaping), result_cpp, count):
+ raise RuntimeError("BNGetTypePrinterTypeTokensBeforeName returned False")
+
+ result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value)
+ core.BNFreeInstructionText(result_cpp.contents, count.value)
+ return result
+
+ def get_type_tokens_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[
+ _function.InstructionTextToken]:
+ count = ctypes.c_ulonglong()
+ result_cpp = ctypes.POINTER(core.BNInstructionTextToken)()
+ parent_type_cpp = None
+ if parent_type is not None:
+ parent_type_cpp = parent_type.handle
+ if not core.BNGetTypePrinterTypeTokensAfterName(self.handle, type.handle, None if platform is None else platform.handle, base_confidence, parent_type_cpp, ctypes.c_int(escaping), result_cpp, count):
+ raise RuntimeError("BNGetTypePrinterTypeTokensAfterName returned False")
+
+ result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value)
+ core.BNFreeInstructionText(result_cpp.contents, count.value)
+ return result
+
+ def get_type_string(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ name: types.QualifiedNameType = "",
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ if not isinstance(name, types.QualifiedName):
+ name = types.QualifiedName(name)
+ result_cpp = ctypes.c_char_p()
+ if not core.BNGetTypePrinterTypeString(self.handle, type.handle, None if platform is None else platform.handle, name._to_core_struct(), ctypes.c_int(escaping), result_cpp):
+ raise RuntimeError("BNGetTypePrinterTypeString returned False")
+
+ result = core.pyNativeStr(result_cpp.value)
+ core.free_string(result_cpp)
+ return result
+
+ def get_type_string_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ result_cpp = ctypes.c_char_p()
+ if not core.BNGetTypePrinterTypeStringBeforeName(self.handle, type.handle, None if platform is None else platform.handle, ctypes.c_int(escaping), result_cpp):
+ raise RuntimeError("BNGetTypePrinterTypeStringBeforeName returned False")
+
+ result = core.pyNativeStr(result_cpp.value)
+ core.free_string(result_cpp)
+ return result
+
+ def get_type_string_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ result_cpp = ctypes.c_char_p()
+ if not core.BNGetTypePrinterTypeStringAfterName(self.handle, type.handle, None if platform is None else platform.handle, ctypes.c_int(escaping), result_cpp):
+ raise RuntimeError("BNGetTypePrinterTypeStringAfterName returned False")
+
+ result = core.pyNativeStr(result_cpp.value)
+ core.free_string(result_cpp)
+ return result
+
+ def get_type_lines(self, type: types.Type, data: binaryview.BinaryView,
+ name: types.QualifiedNameType, line_width = 80, collapsed = False,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType
+ ) -> List[types.TypeDefinitionLine]:
+ if not isinstance(name, types.QualifiedName):
+ name = types.QualifiedName(name)
+ count = ctypes.c_ulonglong()
+ core_lines = ctypes.POINTER(core.BNTypeDefinitionLine)()
+ if not core.BNGetTypePrinterTypeLines(self.handle, type.handle, data.handle, name._to_core_struct(), line_width, collapsed, ctypes.c_int(escaping), core_lines, count):
+ raise RuntimeError("BNGetTypePrinterTypeLines returned False")
+ lines = []
+ for i in range(count.value):
+ tokens = _function.InstructionTextToken._from_core_struct(core_lines[i].tokens, core_lines[i].count)
+ type_ = types.Type.create(handle=core.BNNewTypeReference(core_lines[i].type), platform=data.platform)
+ root_type = types.Type.create(handle=core.BNNewTypeReference(core_lines[i].rootType), platform=data.platform)
+ root_type_name = core.pyNativeStr(core_lines[i].rootTypeName)
+ line = types.TypeDefinitionLine(core_lines[i].lineType, tokens, type_, root_type, root_type_name,
+ core_lines[i].offset, core_lines[i].fieldIndex)
+ lines.append(line)
+ core.BNFreeTypeDefinitionLineList(core_lines, count.value)
+ return lines
diff --git a/python/types.py b/python/types.py
index 65f58192..e00b3408 100644
--- a/python/types.py
+++ b/python/types.py
@@ -19,7 +19,7 @@
# IN THE SOFTWARE.
import ctypes
-from typing import Generator, List, Union, Mapping, Tuple, Optional, Iterable
+from typing import Generator, List, Union, Mapping, Tuple, Optional, Iterable, Dict
from dataclasses import dataclass
import uuid
from abc import abstractmethod
@@ -28,7 +28,8 @@ from abc import abstractmethod
from . import _binaryninjacore as core
from .enums import (
StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, ReferenceType, VariableSourceType,
- TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType
+ TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType,
+ NameType
)
from . import callingconvention
from . import function as _function
@@ -37,6 +38,7 @@ from . import architecture
from . import binaryview
from . import platform as _platform
from . import typelibrary
+from . import typeparser
QualifiedNameType = Union[Iterable[Union[str, bytes]], str, 'QualifiedName']
BoolWithConfidenceType = Union[bool, 'BoolWithConfidence']
@@ -47,6 +49,7 @@ EnumMembersType = Union[List[Tuple[str, int]], List[str], List['EnumerationMembe
SomeType = Union['TypeBuilder', 'Type']
TypeContainer = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary']
NameSpaceType = Optional[Union[str, List[str], 'NameSpace']]
+TypeParserResult = typeparser.TypeParserResult
# The following are needed to prevent the type checker from getting
# confused as we have member functions in `Type` named the same thing
_int = int
@@ -223,6 +226,27 @@ class TypeDefinitionLine:
def __repr__(self):
return f"<typeDefinitionLine {self.type}: {self}>"
+ @staticmethod
+ def _from_core_struct(struct: core.BNTypeDefinitionLine, platform: Optional[_platform.Platform] = None):
+ tokens = _function.InstructionTextToken._from_core_struct(struct.tokens, struct.count)
+ type_ = Type.create(handle=core.BNNewTypeReference(struct.type), platform=platform)
+ root_type = Type.create(handle=core.BNNewTypeReference(struct.rootType), platform=platform)
+ root_type_name = core.pyNativeStr(struct.rootTypeName)
+ return TypeDefinitionLine(struct.lineType, tokens, type_, root_type, root_type_name,
+ struct.offset, struct.fieldIndex)
+
+ def _to_core_struct(self):
+ struct = core.BNTypeDefinitionLine()
+ struct.lineType = self.line_type
+ struct.tokens = _function.InstructionTextToken._get_core_struct(self.tokens)
+ struct.count = len(self.tokens)
+ struct.type = core.BNNewTypeReference(self.type.handle)
+ struct.rootType = core.BNNewTypeReference(self.root_type.handle)
+ struct.rootTypeName = self.root_type_name
+ struct.offset = self.offset
+ struct.fieldIndex = self.field_index
+ return struct
+
class CoreSymbol:
def __init__(self, handle: core.BNSymbolHandle):
@@ -888,7 +912,10 @@ class FunctionBuilder(TypeBuilder):
cls, return_type: Optional[SomeType] = None,
calling_convention: Optional['callingconvention.CallingConvention'] = None, params: Optional[ParamsType] = None,
var_args: Optional[BoolWithConfidenceType] = None, stack_adjust: Optional[OffsetWithConfidenceType] = None,
- platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence
+ platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence,
+ can_return: Optional[BoolWithConfidence] = None, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None,
+ return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None,
+ name_type: 'NameType' = NameType.NoNameType
) -> 'FunctionBuilder':
param_buf = FunctionBuilder._to_core_struct(params)
if return_type is None:
@@ -904,11 +931,38 @@ class FunctionBuilder(TypeBuilder):
conv_conf.convention = calling_convention.handle
conv_conf.confidence = calling_convention.confidence
+ if reg_stack_adjust is None:
+ reg_stack_adjust = {}
+ reg_stack_adjust_regs = (ctypes.c_uint32 * len(reg_stack_adjust))()
+ reg_stack_adjust_values = (core.BNOffsetWithConfidence * len(reg_stack_adjust))()
+
+ for i, (reg, adjust) in enumerate(reg_stack_adjust.items()):
+ reg_stack_adjust_regs[i] = reg
+ reg_stack_adjust_values[i].value = adjust.value
+ reg_stack_adjust_values[i].confidence = adjust.confidence
+
+ return_regs_set = core.BNRegisterSetWithConfidence()
+ if return_regs is None or platform is None:
+ return_regs_set.count = 0
+ return_regs_set.confidence = 0
+ else:
+ return_regs_set.count = len(return_regs)
+ return_regs_set.confidence = 255
+ return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))()
+
+ for i, reg in enumerate(return_regs):
+ return_regs_set[i] = platform.arch.get_reg_index(reg)
+
if var_args is None:
vararg_conf = BoolWithConfidence.get_core_struct(False, 0)
else:
vararg_conf = BoolWithConfidence.get_core_struct(var_args, core.max_confidence)
+ if can_return is None:
+ can_return_conf = BoolWithConfidence.get_core_struct(True, 0)
+ else:
+ can_return_conf = BoolWithConfidence.get_core_struct(can_return, core.max_confidence)
+
if stack_adjust is None:
stack_adjust_conf = OffsetWithConfidence.get_core_struct(0, 0)
else:
@@ -916,7 +970,9 @@ class FunctionBuilder(TypeBuilder):
if params is None:
params = []
handle = core.BNCreateFunctionTypeBuilder(
- ret_conf, conv_conf, param_buf, len(params), vararg_conf, stack_adjust_conf
+ ret_conf, conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf,
+ reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust),
+ return_regs_set, name_type
)
assert handle is not None, "BNCreateFunctionTypeBuilder returned None"
return cls(handle, platform, confidence)
@@ -1783,13 +1839,7 @@ class Type:
assert core_lines is not None, "core.BNGetTypeLines returned None"
lines = []
for i in range(count.value):
- tokens = _function.InstructionTextToken._from_core_struct(core_lines[i].tokens, core_lines[i].count)
- type_ = Type.create(handle=core.BNNewTypeReference(core_lines[i].type), platform=self._platform)
- root_type = Type.create(handle=core.BNNewTypeReference(core_lines[i].rootType), platform=self._platform)
- root_type_name = core.pyNativeStr(core_lines[i].rootTypeName)
- line = TypeDefinitionLine(core_lines[i].lineType, tokens, type_, root_type, root_type_name,
- core_lines[i].offset, core_lines[i].fieldIndex)
- lines.append(line)
+ lines.append(TypeDefinitionLine._from_core_struct(core_lines[i]))
core.BNFreeTypeDefinitionLineList(core_lines, count.value)
return lines
@@ -2448,7 +2498,10 @@ class FunctionType(Type):
calling_convention: Optional['callingconvention.CallingConvention'] = None,
variable_arguments: BoolWithConfidenceType = BoolWithConfidence(False),
stack_adjust: OffsetWithConfidence = OffsetWithConfidence(0), platform: Optional['_platform.Platform'] = None,
- confidence: int = core.max_confidence
+ confidence: int = core.max_confidence,
+ can_return: BoolWithConfidence = True, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None,
+ return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None,
+ name_type: 'NameType' = NameType.NoNameType
) -> 'FunctionType':
if ret is None:
ret = VoidType.create()
@@ -2474,9 +2527,38 @@ class FunctionType(Type):
else:
_stack_adjust = OffsetWithConfidence.get_core_struct(stack_adjust, core.max_confidence)
+
+ if reg_stack_adjust is None:
+ reg_stack_adjust = {}
+ reg_stack_adjust_regs = (ctypes.c_uint32 * len(reg_stack_adjust))()
+ reg_stack_adjust_values = (core.BNOffsetWithConfidence * len(reg_stack_adjust))()
+
+ for i, (reg, adjust) in enumerate(reg_stack_adjust.items()):
+ reg_stack_adjust_regs[i] = reg
+ reg_stack_adjust_values[i].value = adjust.value
+ reg_stack_adjust_values[i].confidence = adjust.confidence
+
+ return_regs_set = core.BNRegisterSetWithConfidence()
+ if return_regs is None or platform is None:
+ return_regs_set.count = 0
+ return_regs_set.confidence = 0
+ else:
+ return_regs_set.count = len(return_regs)
+ return_regs_set.confidence = 255
+ return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))()
+
+ for i, reg in enumerate(return_regs):
+ return_regs_set[i] = platform.arch.get_reg_index(reg)
+
+ _can_return = BoolWithConfidence.get_core_struct(can_return)
+ if params is None:
+ params = []
func_type = core.BNCreateFunctionType(
- ret_conf, conv_conf, param_buf, len(params), _variable_arguments, _stack_adjust
+ ret_conf, conv_conf, param_buf, len(params), _variable_arguments, _can_return, _stack_adjust,
+ reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust),
+ return_regs_set, name_type
)
+
assert func_type is not None, f"core.BNCreateFunctionType returned None {ret_conf} {conv_conf} {param_buf} {_variable_arguments} {_stack_adjust}"
return cls(core.BNNewTypeReference(func_type), platform, confidence)
@@ -2735,55 +2817,6 @@ class RegisterSet:
@dataclass(frozen=True)
-class TypeParserResult:
- types: Mapping[QualifiedName, Type]
- variables: Mapping[QualifiedName, Type]
- functions: Mapping[QualifiedName, Type]
-
- def __repr__(self):
- return f"<types: {self.types}, variables: {self.variables}, functions: {self.functions}>"
-
-
-def preprocess_source(source: str, filename: Optional[str] = None,
- include_dirs: Optional[List[str]] = None) -> Tuple[Optional[str], str]:
- """
- ``preprocess_source`` run the C preprocessor on the given source or source filename.
-
- :param str source: source to pre-process
- :param str filename: optional filename to pre-process
- :param include_dirs: list of string directories to use as include directories.
- :type include_dirs: list(str)
- :return: returns a tuple of (preprocessed_source, error_string)
- :rtype: tuple(str,str)
- :Example:
-
- >>> source = "#define TEN 10\\nint x[TEN];\\n"
- >>> preprocess_source(source)
- ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '')
- >>>
- """
- if filename is None:
- filename = "input"
- if include_dirs is None:
- include_dirs = []
- dir_buf = (ctypes.c_char_p * len(include_dirs))()
- for i in range(0, len(include_dirs)):
- dir_buf[i] = include_dirs[i].encode('charmap')
- output = ctypes.c_char_p()
- errors = ctypes.c_char_p()
- result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs))
- assert output.value is not None
- assert errors.value is not None
- output_str = output.value.decode('utf-8')
- error_str = errors.value.decode('utf-8')
- core.free_string(output)
- core.free_string(errors)
- if result:
- return output_str, error_str
- return None, error_str
-
-
-@dataclass(frozen=True)
class TypeFieldReference:
func: Optional['_function.Function']
arch: Optional['architecture.Architecture']
diff --git a/rust/src/types.rs b/rust/src/types.rs
index 3afc9c9d..01767853 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -783,6 +783,7 @@ impl Type {
) -> Ref<Self> {
let mut return_type = return_type.into().into();
let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into();
+ let mut can_return = Conf::new(true, min_confidence()).into();
let mut raw_calling_convention: BNCallingConventionWithConfidence =
BNCallingConventionWithConfidence {
@@ -809,6 +810,15 @@ impl Type {
});
parameter_name_references.push(raw_name);
}
+ let reg_stack_adjust_regs = ptr::null_mut();
+ let reg_stack_adjust_values = ptr::null_mut();
+
+ let mut return_regs: BNRegisterSetWithConfidence =
+ BNRegisterSetWithConfidence{
+ regs: ptr::null_mut(),
+ count: 0,
+ confidence: 0,
+ };
unsafe {
Self::ref_from_raw(BNCreateFunctionType(
@@ -817,7 +827,13 @@ impl Type {
raw_parameters.as_mut_ptr(),
raw_parameters.len(),
&mut variable_arguments,
+ &mut can_return,
&mut stack_adjust,
+ reg_stack_adjust_regs,
+ reg_stack_adjust_values,
+ 0,
+ &mut return_regs,
+ BNNameType::NoNameType,
))
}
}
@@ -836,6 +852,7 @@ impl Type {
) -> Ref<Self> {
let mut return_type = return_type.into().into();
let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into();
+ let mut can_return = Conf::new(true, min_confidence()).into();
let mut raw_calling_convention: BNCallingConventionWithConfidence =
calling_convention.into();
let mut stack_adjust = stack_adjust.into();
@@ -859,6 +876,17 @@ impl Type {
parameter_name_references.push(raw_name);
}
+ // TODO: Update type signature and include these (will be a breaking change)
+ let reg_stack_adjust_regs = ptr::null_mut();
+ let reg_stack_adjust_values = ptr::null_mut();
+
+ let mut return_regs: BNRegisterSetWithConfidence =
+ BNRegisterSetWithConfidence{
+ regs: ptr::null_mut(),
+ count: 0,
+ confidence: 0,
+ };
+
unsafe {
Self::ref_from_raw(BNCreateFunctionType(
&mut return_type,
@@ -866,7 +894,13 @@ impl Type {
raw_parameters.as_mut_ptr(),
raw_parameters.len(),
&mut variable_arguments,
+ &mut can_return,
&mut stack_adjust,
+ reg_stack_adjust_regs,
+ reg_stack_adjust_values,
+ 0,
+ &mut return_regs,
+ BNNameType::NoNameType,
))
}
}
diff --git a/suite/api_test.py b/suite/api_test.py
index bcb9beb4..803bd7e8 100644
--- a/suite/api_test.py
+++ b/suite/api_test.py
@@ -11,13 +11,13 @@ from binaryninja.pluginmanager import RepositoryManager
from binaryninja.platform import Platform
from binaryninja.enums import (
StructureVariant, NamedTypeReferenceClass, MemberAccess, MemberScope, ReferenceType, VariableSourceType,
- SymbolBinding, SymbolType, TokenEscapingType
+ SymbolBinding, SymbolType, TokenEscapingType, InstructionTextTokenType, TypeDefinitionLineType
)
from binaryninja.types import (
- QualifiedName, Type, TypeBuilder, EnumerationMember, FunctionParameter, OffsetWithConfidence, BoolWithConfidence,
- EnumerationBuilder, NamedTypeReferenceBuilder, StructureBuilder, StructureMember, IntegerType, StructureType,
- Symbol, NameSpace, MutableTypeBuilder, NamedTypeReferenceType
+ QualifiedName, Type, TypeBuilder, EnumerationMember, FunctionParameter, OffsetWithConfidence, BoolWithConfidence,
+ EnumerationBuilder, NamedTypeReferenceBuilder, StructureBuilder, StructureMember, IntegerType, StructureType,
+ Symbol, NameSpace, MutableTypeBuilder, NamedTypeReferenceType, QualifiedNameType, TypeDefinitionLine
)
from binaryninja.function import *
from binaryninja.basicblock import *
@@ -26,6 +26,8 @@ from binaryninja.lowlevelil import *
from binaryninja.mediumlevelil import *
from binaryninja.highlevelil import *
from binaryninja.variable import *
+from binaryninja.typeparser import *
+from binaryninja.typeprinter import *
import zipfile
@@ -577,9 +579,9 @@ class TypeParserTest(unittest.TestCase):
assert member.offset == expect_offset, f"Structure member property: 'offset' {expect_offset} incorrect for {member.name} in {definition} got {member.offset} instead"
def test_escaping(self):
- escaped = [('test', 'test', 'test'), ('a0b', 'a0b', 'a0b'), ('a$b', 'a$b', 'a$b'), ('a_b', 'a_b', 'a_b'),
- ('a@b', 'a@b', 'a@b'), ('a!b', 'a!b', 'a!b'), ('0a', '0a', '`0a`'), ('_a', '_a', '_a'),
- ('$a', '$a', '$a'), ('@a', '@a', '`@a`'), ('!a', '!a', '`!a`'), ('a::b', 'a::b', '`a::b`'),
+ escaped = [('test', 'test', 'test'), ('a0b', 'a0b', 'a0b'), ('a$b', 'a$b', '`a$b`'), ('a_b', 'a_b', 'a_b'),
+ ('a@b', 'a@b', '`a@b`'), ('a!b', 'a!b', '`a!b`'), ('0a', '0a', '`0a`'), ('_a', '_a', '_a'),
+ ('$a', '$a', '`$a`'), ('@a', '@a', '`@a`'), ('!a', '!a', '`!a`'), ('a::b', 'a::b', '`a::b`'),
('a b', 'a b', '`a b`'), ('a`b', 'a`b', '`a\\`b`'), ('a\\b', 'a\\b', '`a\\\\b`'),
('a\\`b', 'a\\`b', '`a\\\\\\`b`'), ('a\\\\`b', 'a\\\\`b', '`a\\\\\\\\\\`b`'), ]
for source, expect_none, expect_backticks in escaped:
@@ -621,6 +623,418 @@ class TypeParserTest(unittest.TestCase):
assert len(types.types['space struct'].members[2].type.target.parameters) == 1
assert types.types['space struct'].members[2].type.target.parameters[0].name == 'argument name'
+ def test_parse_class(self):
+ valid = r'''
+ class foo;
+ class bar
+ {
+ foo* foo;
+ };
+ class baz
+ {
+ class
+ {
+ bar m_bar;
+ } bar;
+ struct
+ {
+ baz* m_baz;
+ } baz;
+ };
+ '''
+ types = self.p.parse_types_from_source(valid)
+ assert types.types['foo'].type_class == TypeClass.VoidTypeClass
+ assert types.types['bar'].type_class == TypeClass.StructureTypeClass
+ assert types.types['bar'].type == StructureVariant.ClassStructureType
+ assert types.types['baz'].type_class == TypeClass.StructureTypeClass
+ assert types.types['baz'].type == StructureVariant.ClassStructureType
+ assert types.types['baz'].members[0].type.type_class == TypeClass.StructureTypeClass
+ assert types.types['baz'].members[0].type.type == StructureVariant.ClassStructureType
+ assert types.types['baz'].members[1].type.type_class == TypeClass.StructureTypeClass
+ assert types.types['baz'].members[1].type.type == StructureVariant.StructStructureType
+
+ def test_class_vs_struct(self):
+ # Trying to use `class foo` as `struct foo`
+
+ # Clang says (TIL):
+ # "Class 'foo' was previously declared as a struct; this is valid, but may result in linker errors under the Microsoft C++ ABI"
+ valid = [
+ r'''
+ class foo
+ {
+ int a;
+ };
+ class bar
+ {
+ struct foo foo;
+ };
+ ''', r'''
+ struct foo
+ {
+ int a;
+ };
+ struct bar
+ {
+ class foo foo;
+ };
+ '''
+ ]
+ for source in valid:
+ with self.subTest():
+ types = self.p.parse_types_from_source(source)
+
+
+ def test_parse_empty(self):
+ valid = [
+ # Forward declarations
+ 'struct foo;',
+ 'class foo;',
+ 'union foo;',
+ # Definition with no members
+ 'struct foo {};',
+ 'class foo {};',
+ 'union foo {};',
+ 'enum foo {};',
+ # Inner structure is empty
+ 'struct foo { struct {} bar; class {} baz; union {} alpha; enum {} bravo; };',
+ 'class foo { struct {} bar; class {} baz; union {} alpha; enum {} bravo; };',
+ 'union foo { struct {} bar; class {} baz; union {} alpha; enum {} bravo; };',
+ ]
+ for source in valid:
+ with self.subTest():
+ types = self.p.parse_types_from_source(source)
+
+ invalid = [
+ # Forward declaration of enum is not allowed
+ 'enum foo;'
+ ]
+ for source in invalid:
+ with self.subTest():
+ with self.assertRaises(SyntaxError):
+ types = self.p.parse_types_from_source(source)
+
+ def test_parse_nested(self):
+ source = r'''
+ struct foo
+ {
+ enum : uint32_t
+ {
+ a = 1,
+ b = 2,
+ c = 3
+ } bar;
+ struct
+ {
+ uint32_t a;
+ } baz;
+ class
+ {
+ uint32_t a;
+ } alpha;
+ union
+ {
+ uint32_t a;
+ uint32_t b;
+ } bravo;
+ };
+ '''
+ types = self.p.parse_types_from_source(source)
+ assert types.types['foo'].members[0].type.type_class == TypeClass.EnumerationTypeClass
+ assert types.types['foo'].members[1].type.type_class == TypeClass.StructureTypeClass
+ assert types.types['foo'].members[1].type.type == StructureVariant.StructStructureType
+ assert types.types['foo'].members[2].type.type_class == TypeClass.StructureTypeClass
+ assert types.types['foo'].members[2].type.type == StructureVariant.ClassStructureType
+ assert types.types['foo'].members[3].type.type_class == TypeClass.StructureTypeClass
+ assert types.types['foo'].members[3].type.type == StructureVariant.UnionStructureType
+ assert types.types['foo'].members[0].type.members[0].name == 'a'
+ assert types.types['foo'].members[0].type.members[1].name == 'b'
+ assert types.types['foo'].members[0].type.members[2].name == 'c'
+ assert types.types['foo'].members[1].type.members[0].name == 'a'
+ assert types.types['foo'].members[2].type.members[0].name == 'a'
+ assert types.types['foo'].members[3].type.members[0].name == 'a'
+ assert types.types['foo'].members[3].type.members[1].name == 'b'
+
+ def test_forward_declared(self):
+ # Via #2431 with a little extra sauce on LIST_ENTRY1
+ source = r'''
+ struct _LIST_ENTRY;
+ typedef struct _LIST_ENTRY LIST_ENTRY;
+ typedef LIST_ENTRY LIST_ENTRY1;
+ struct _LIST_ENTRY {
+ LIST_ENTRY * ForwardLink;
+ LIST_ENTRY * BackLink;
+ };
+
+ struct Test {
+ long long Signature;
+ LIST_ENTRY1 Link;
+ int Action;
+ short DefaultId;
+ };
+ '''
+ types = self.p.parse_types_from_source(source)
+ assert types.types['_LIST_ENTRY'].width == 0x10
+ assert types.types['LIST_ENTRY'].width == 0x10
+ assert types.types['LIST_ENTRY1'].width == 0x10
+ assert types.types['Test'].width == 0x20
+ assert types.types['Test'].members[2].offset == 0x18
+
+ def test_custom_subclass(self):
+ class MyTypeParser(TypeParser):
+ name = "MyTypeParser"
+
+ def preprocess_source(
+ self, source: str, file_name: str, platform: binaryninja.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]],
+ options: Optional[List[str]], include_dirs: Optional[List[str]]
+ ) -> Tuple[Optional[str], List[TypeParserError]]:
+ return (
+ source,
+ [
+ TypeParserError(TypeParserErrorSeverity.WarningSeverity, "Test Warning", "sources.hpp", 1, 1)
+ ]
+ )
+
+ def parse_types_from_source(
+ self,
+ source: str,
+ file_name: str,
+ platform: binaryninja.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]],
+ options: Optional[List[str]],
+ include_dirs: Optional[List[str]],
+ auto_type_source: str = ""
+ ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]:
+ return (
+ TypeParserResult(
+ [
+ ParsedType("my_type", Type.int(4, False), True)
+ ], [
+ ParsedType("my_variable", Type.int(4, False), True)
+ ], [
+ ParsedType("my_function", Type.function(Type.void(), []), True)
+ ]
+ ),
+ [
+ TypeParserError(TypeParserErrorSeverity.WarningSeverity, "Test Warning", "sources.hpp", 1, 1)
+ ]
+ )
+
+ def parse_type_string(
+ self, source: str, platform: binaryninja.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]]
+ ) -> Tuple[Optional[Tuple[QualifiedNameType, binaryninja.Type]],
+ List[TypeParserError]]:
+ return (
+ ("my_type", Type.int(4, False)),
+ [
+ TypeParserError(TypeParserErrorSeverity.WarningSeverity, "Test Warning", "sources.hpp", 1, 1)
+ ]
+ )
+
+ MyTypeParser().register()
+
+ tp = TypeParser['MyTypeParser']
+ (result, errors) = tp.preprocess_source('some test source', 'source.h', Platform['windows-x86'])
+ assert result is not None
+ assert result == 'some test source'
+
+ assert errors is not None
+ assert len(errors) == 1
+
+ assert errors[0].severity == TypeParserErrorSeverity.WarningSeverity
+ assert errors[0].message == "Test Warning"
+ assert errors[0].file_name == "sources.hpp"
+ assert errors[0].line == 1
+ assert errors[0].column == 1
+
+ (result, errors) = tp.parse_types_from_source('some test source', 'source.h', Platform['windows-x86'])
+ assert result is not None
+ assert len(result.types) == 1
+ assert len(result.variables) == 1
+ assert len(result.functions) == 1
+
+ assert result.types[0].name == 'my_type'
+ assert result.types[0].type == Type.int(4, False)
+ assert result.types[0].is_user
+
+ assert result.variables[0].name == 'my_variable'
+ assert result.variables[0].type == Type.int(4, False)
+ assert result.variables[0].is_user
+
+ assert result.functions[0].name == 'my_function'
+ assert result.functions[0].type == Type.function(Type.void(), [])
+ assert result.functions[0].is_user
+
+ assert errors is not None
+ assert len(errors) == 1
+
+ assert errors[0].severity == TypeParserErrorSeverity.WarningSeverity
+ assert errors[0].message == "Test Warning"
+ assert errors[0].file_name == "sources.hpp"
+ assert errors[0].line == 1
+ assert errors[0].column == 1
+
+ (result, errors) = tp.parse_type_string('some test source', Platform['windows-x86'])
+ assert result is not None
+ assert result[0] == 'my_type'
+ assert result[1] == Type.int(4, False)
+
+ assert errors is not None
+ assert len(errors) == 1
+
+ assert errors[0].severity == TypeParserErrorSeverity.WarningSeverity
+ assert errors[0].message == "Test Warning"
+ assert errors[0].file_name == "sources.hpp"
+ assert errors[0].line == 1
+ assert errors[0].column == 1
+
+
+class TestTypePrinter(unittest.TestCase):
+ def test_getlines(self):
+ arch = 'x86'
+ platform = Platform['windows-x86']
+ bv = BinaryView.new()
+ bv.platform = platform
+
+ types = [
+ (Type.int(4), 'basic_int', 'typedef int32_t basic_int;\n'),
+ (Type.array(Type.int(4), 4), 'basic_array', 'typedef int32_t basic_array[0x4];\n'),
+ (Type.pointer(platform.arch, Type.array(
+ Type.int(4), 4
+ )), 'pointer_array', 'typedef int32_t (* pointer_array)[0x4];\n'),
+ (Type.array(
+ Type.pointer(platform.arch, Type.int(4)), 4
+ ), 'array_pointer', 'typedef int32_t* array_pointer[0x4];\n'),
+ (Type.function(
+ Type.int(4), []
+ ), 'basic_func', 'typedef int32_t basic_func();\n'),
+ (Type.function(
+ Type.int(4), [], platform.fastcall_calling_convention
+ ), 'convention_func', 'typedef int32_t __fastcall convention_func();\n'),
+ (Type.pointer(platform.arch, Type.function(
+ Type.int(4), []
+ ), True), 'const_func_pointer', 'typedef int32_t (* const const_func_pointer)();\n'),
+ (Type.pointer(platform.arch, Type.function(
+ Type.int(4), []
+ )), 'basic_func_pointer', 'typedef int32_t (* basic_func_pointer)();\n'),
+ (Type.function(
+ Type.pointer(platform.arch, Type.int(4)), []
+ ), 'func_returning_ptr', 'typedef int32_t* func_returning_ptr();\n'),
+ (Type.structure([
+ (Type.int(4), 'foo')
+ ]), 'basic_struct', 'struct basic_struct\n{\n int32_t foo;\n};\n'),
+ (Type.pointer(platform.arch, Type.structure([
+ (Type.int(4), 'foo')
+ ])), 'pointer_struct', 'typedef struct { int32_t foo; }* pointer_struct;\n'),
+ (Type.pointer(platform.arch, Type.structure([
+ (Type.pointer(platform.arch, Type.int(4)), 'foo')
+ ])), 'pointer_in_pointer_struct', 'typedef struct { int32_t* foo; }* pointer_in_pointer_struct;\n'),
+ (Type.pointer(platform.arch, Type.structure([
+ (Type.pointer(platform.arch, Type.structure([
+ (Type.pointer(platform.arch, Type.int(4)), 'foo')
+ ])), 'foo')
+ ])), 'nested_pointer_struct', 'typedef struct { struct { int32_t* foo; }* foo; }* nested_pointer_struct;\n'),
+ (Type.pointer(platform.arch, Type.structure([
+ (Type.pointer(platform.arch, Type.function(Type.int(4), [('param', Type.int(4))])), 'foo')
+ ])), 'pointer_function_struct', 'typedef struct { int32_t (* foo)(int32_t param); }* pointer_function_struct;\n'),
+ (Type.pointer(platform.arch, Type.enumeration(platform.arch, [
+ ('one', 1)
+ ])), 'pointer_enumeration', 'typedef enum {}* pointer_enumeration;\n'),
+ ]
+
+ for t in types:
+ with self.subTest(t[1]):
+ lines = t[0].get_lines(bv, t[1])
+ text = ""
+ for l in lines:
+ for tok in l.tokens:
+ text += tok.text
+ text += "\n"
+ assert text == t[2], f"Invalid printing of type, got {text} expected {t[2]}"
+
+
+ def test_custom_subclass(self):
+ class MyTypePrinter(TypePrinter):
+ name = "MyTypePrinter"
+
+ def get_type_tokens(self, type: types.Type, platform: Optional[Platform], name: types.QualifiedName,
+ base_confidence: int, escaping: TokenEscapingType) -> List[InstructionTextToken]:
+ return [
+ InstructionTextToken(InstructionTextTokenType.TextToken, "the type is: ", 0),
+ InstructionTextToken(InstructionTextTokenType.TypeNameToken, str(name), 0),
+ InstructionTextToken(InstructionTextTokenType.TextToken, " bottom text", 0)
+ ]
+
+ def get_type_tokens_before_name(self, type: types.Type, platform: Optional[Platform], base_confidence: int,
+ parent_type: Optional[types.Type], escaping: TokenEscapingType) -> List[
+ InstructionTextToken]:
+ return [
+ InstructionTextToken(InstructionTextTokenType.TextToken, "the type is: ", 0),
+ ]
+
+ def get_type_tokens_after_name(self, type: types.Type, platform: Optional[Platform], base_confidence: int,
+ parent_type: Optional[types.Type], escaping: TokenEscapingType) -> List[InstructionTextToken]:
+ return [
+ InstructionTextToken(InstructionTextTokenType.TextToken, " bottom text", 0),
+ ]
+
+ def get_type_string(self, type: types.Type, platform: Optional[Platform], name: types.QualifiedName,
+ escaping: TokenEscapingType) -> str:
+ return f"the type is: {name} bottom text"
+
+ def get_type_string_before_name(self, type: types.Type, platform: Optional[Platform],
+ escaping: TokenEscapingType) -> str:
+ return f"the type is: "
+
+ def get_type_string_after_name(self, type: types.Type, platform: Optional[Platform],
+ escaping: TokenEscapingType) -> str:
+ return f" bottom text"
+
+ def get_type_lines(self, type: types.Type, data: binaryview.BinaryView, name: types.QualifiedName, line_width,
+ collapsed, escaping: TokenEscapingType) -> List[types.TypeDefinitionLine]:
+ return [
+ TypeDefinitionLine(TypeDefinitionLineType.TypedefLineType, [
+ InstructionTextToken(InstructionTextTokenType.TextToken, "the type is: ", 0),
+ InstructionTextToken(InstructionTextTokenType.TypeNameToken, str(name), 0),
+ InstructionTextToken(InstructionTextTokenType.TextToken, " bottom text", 0)
+ ], type, type, '', 0, 1)
+ ]
+
+ MyTypePrinter().register()
+
+ tp = TypePrinter['MyTypePrinter']
+ result = tp.get_type_tokens(Type.void(), None, QualifiedName(['test']), 255, TokenEscapingType.NoTokenEscapingType)
+ assert result is not None
+ assert len(result) == 3
+ assert result[0].text == "the type is: "
+ assert result[1].text == "test"
+ assert result[2].text == " bottom text"
+ result = tp.get_type_tokens_before_name(Type.void())
+ assert result is not None
+ assert len(result) == 1
+ assert result[0].text == "the type is: "
+ result = tp.get_type_tokens_after_name(Type.void())
+ assert result is not None
+ assert len(result) == 1
+ assert result[0].text == " bottom text"
+ result = tp.get_type_string(Type.void(), None, QualifiedName(['test']))
+ assert result is not None
+ assert result == "the type is: test bottom text"
+ result = tp.get_type_string_before_name(Type.void())
+ assert result is not None
+ assert result == "the type is: "
+ result = tp.get_type_string_after_name(Type.void())
+ assert result is not None
+ assert result == " bottom text"
+ result = tp.get_type_lines(Type.void(), BinaryView.new(b''), QualifiedName(['test']))
+ assert result is not None
+ assert len(result) == 1
+ assert len(result[0].tokens) == 3
+ assert result[0].tokens[0].text == "the type is: "
+ assert result[0].tokens[1].text == "test"
+ assert result[0].tokens[2].text == " bottom text"
+
class TestQualifiedName(unittest.TestCase):
def test_constructors_and_equality(self):
diff --git a/type.cpp b/type.cpp
index 56526611..d563ce29 100644
--- a/type.cpp
+++ b/type.cpp
@@ -210,16 +210,16 @@ string NameList::GetString(BNTokenEscapingType escaping) const
{
if (!first)
{
- out += m_join + EscapeTypeName(name, escaping);
+ out += m_join + name;
}
else
{
- out += EscapeTypeName(name, escaping);
+ out += name;
}
if (name.length() != 0)
first = false;
}
- return out;
+ return EscapeTypeName(out, escaping);
}
@@ -340,7 +340,7 @@ void QualifiedName::FreeAPIObject(BNQualifiedName* name)
}
-QualifiedName QualifiedName::FromAPIObject(BNQualifiedName* name)
+QualifiedName QualifiedName::FromAPIObject(const BNQualifiedName* name)
{
QualifiedName result;
for (size_t i = 0; i < name->nameCount; i++)
@@ -451,6 +451,38 @@ TypeDefinitionLine TypeDefinitionLine::FromAPIObject(BNTypeDefinitionLine* line)
}
+BNTypeDefinitionLine* TypeDefinitionLine::CreateTypeDefinitionLineList(
+ const std::vector<TypeDefinitionLine>& lines)
+{
+ BNTypeDefinitionLine* result = new BNTypeDefinitionLine[lines.size()];
+ for (size_t i = 0; i < lines.size(); ++i)
+ {
+ result[i].lineType = lines[i].lineType;
+ result[i].tokens = InstructionTextToken::CreateInstructionTextTokenList(lines[i].tokens);
+ result[i].count = lines[i].tokens.size();
+ result[i].type = BNNewTypeReference(lines[i].type->GetObject());
+ result[i].rootType = BNNewTypeReference(lines[i].rootType->GetObject());
+ result[i].rootTypeName = BNAllocString(lines[i].rootTypeName.c_str());
+ result[i].offset = lines[i].offset;
+ result[i].fieldIndex = lines[i].fieldIndex;
+ }
+ return result;
+}
+
+
+void TypeDefinitionLine::FreeTypeDefinitionLineList(BNTypeDefinitionLine* lines, size_t count)
+{
+ for (size_t i = 0; i < count; ++i)
+ {
+ InstructionTextToken::FreeInstructionTextTokenList(lines[i].tokens, lines[i].count);
+ BNFreeType(lines[i].type);
+ BNFreeType(lines[i].rootType);
+ BNFreeString(lines[i].rootTypeName);
+ }
+ delete[] lines;
+}
+
+
Type::Type(BNType* type)
{
m_object = type;
@@ -843,16 +875,96 @@ Ref<Type> Type::FunctionType(const Confidence<Ref<Type>>& returnValue,
varArgConf.value = varArg.GetValue();
varArgConf.confidence = varArg.GetConfidence();
+ BNBoolWithConfidence canReturnConf;
+ canReturnConf.value = true;
+ canReturnConf.confidence = 0;
+
+ BNOffsetWithConfidence stackAdjustConf;
+ stackAdjustConf.value = stackAdjust.GetValue();
+ stackAdjustConf.confidence = stackAdjust.GetConfidence();
+
+ BNRegisterSetWithConfidence returnRegsConf;
+ returnRegsConf.regs = nullptr;
+ returnRegsConf.count = 0;
+ returnRegsConf.confidence = 0;
+
+ Type* type = new Type(BNCreateFunctionType(
+ &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf,
+ &canReturnConf, &stackAdjustConf, nullptr, nullptr, 0, &returnRegsConf, NoNameType));
+ delete[] paramArray;
+ return type;
+}
+
+
+Ref<Type> Type::FunctionType(const Confidence<Ref<Type>>& returnValue,
+ const Confidence<Ref<CallingConvention>>& callingConvention,
+ const std::vector<FunctionParameter>& params,
+ const Confidence<bool>& hasVariableArguments,
+ const Confidence<bool>& canReturn,
+ const Confidence<int64_t>& stackAdjust,
+ const std::map<uint32_t, Confidence<int32_t>>& regStackAdjust,
+ const Confidence<std::vector<uint32_t>>& returnRegs,
+ BNNameType ft)
+{
+ BNTypeWithConfidence returnValueConf;
+ returnValueConf.type = returnValue->GetObject();
+ returnValueConf.confidence = returnValue.GetConfidence();
+
+ BNCallingConventionWithConfidence callingConventionConf;
+ callingConventionConf.convention = callingConvention ? callingConvention->GetObject() : nullptr;
+ callingConventionConf.confidence = callingConvention.GetConfidence();
+
+ BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()];
+ for (size_t i = 0; i < params.size(); i++)
+ {
+ paramArray[i].name = (char*)params[i].name.c_str();
+ paramArray[i].type = params[i].type->GetObject();
+ paramArray[i].typeConfidence = params[i].type.GetConfidence();
+ paramArray[i].defaultLocation = params[i].defaultLocation;
+ paramArray[i].location.type = params[i].location.type;
+ paramArray[i].location.index = params[i].location.index;
+ paramArray[i].location.storage = params[i].location.storage;
+ }
+
+ BNBoolWithConfidence varArgConf;
+ varArgConf.value = hasVariableArguments.GetValue();
+ varArgConf.confidence = hasVariableArguments.GetConfidence();
+
+ BNBoolWithConfidence canReturnConf;
+ canReturnConf.value = canReturn.GetValue();
+ canReturnConf.confidence = canReturn.GetConfidence();
+
BNOffsetWithConfidence stackAdjustConf;
stackAdjustConf.value = stackAdjust.GetValue();
stackAdjustConf.confidence = stackAdjust.GetConfidence();
+ std::vector<uint32_t> regStackAdjustRegs;
+ std::vector<BNOffsetWithConfidence> regStackAdjustValues;
+ size_t i = 0;
+ for (const auto& adjust: regStackAdjust)
+ {
+ regStackAdjustRegs[i] = adjust.first;
+ regStackAdjustValues[i].value = adjust.second.GetValue();
+ regStackAdjustValues[i].confidence = adjust.second.GetConfidence();
+ i ++;
+ }
+
+ std::vector<uint32_t> returnRegsRegs = returnRegs.GetValue();
+
+ BNRegisterSetWithConfidence returnRegsConf;
+ returnRegsConf.regs = returnRegsRegs.data();
+ returnRegsConf.count = returnRegs->size();
+ returnRegsConf.confidence = returnRegs.GetConfidence();
+
Type* type = new Type(BNCreateFunctionType(
- &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf, &stackAdjustConf));
+ &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf,
+ &canReturnConf, &stackAdjustConf, regStackAdjustRegs.data(),
+ regStackAdjustValues.data(), regStackAdjust.size(), &returnRegsConf, NoNameType));
delete[] paramArray;
return type;
}
+
BNIntegerDisplayType Type::GetIntegerTypeDisplayType() const
{
return BNGetIntegerTypeDisplayType(m_object);
@@ -1587,8 +1699,87 @@ TypeBuilder TypeBuilder::FunctionType(const Confidence<Ref<Type>>& returnValue,
stackAdjustConf.value = stackAdjust.GetValue();
stackAdjustConf.confidence = stackAdjust.GetConfidence();
+ BNBoolWithConfidence canReturnConf;
+ canReturnConf.value = true;
+ canReturnConf.confidence = 0;
+
+ BNRegisterSetWithConfidence returnRegsConf;
+ returnRegsConf.regs = nullptr;
+ returnRegsConf.count = 0;
+ returnRegsConf.confidence = 0;
+
+ TypeBuilder type(BNCreateFunctionTypeBuilder(
+ &returnValueConf, &callingConventionConf, paramArray, paramCount, &varArgConf,
+ &canReturnConf, &stackAdjustConf, nullptr, nullptr, 0, &returnRegsConf, NoNameType));
+ delete[] paramArray;
+ return type;
+}
+
+
+TypeBuilder TypeBuilder::FunctionType(const Confidence<Ref<Type>>& returnValue,
+ const Confidence<Ref<CallingConvention>>& callingConvention,
+ const std::vector<FunctionParameter>& params,
+ const Confidence<bool>& hasVariableArguments,
+ const Confidence<bool>& canReturn,
+ const Confidence<int64_t>& stackAdjust,
+ const std::map<uint32_t, Confidence<int32_t>>& regStackAdjust,
+ const Confidence<std::vector<uint32_t>>& returnRegs,
+ BNNameType ft)
+{
+ BNTypeWithConfidence returnValueConf;
+ returnValueConf.type = returnValue->GetObject();
+ returnValueConf.confidence = returnValue.GetConfidence();
+
+ BNCallingConventionWithConfidence callingConventionConf;
+ callingConventionConf.convention = callingConvention ? callingConvention->GetObject() : nullptr;
+ callingConventionConf.confidence = callingConvention.GetConfidence();
+
+ BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()];
+ for (size_t i = 0; i < params.size(); i++)
+ {
+ paramArray[i].name = (char*)params[i].name.c_str();
+ paramArray[i].type = params[i].type->GetObject();
+ paramArray[i].typeConfidence = params[i].type.GetConfidence();
+ paramArray[i].defaultLocation = params[i].defaultLocation;
+ paramArray[i].location.type = params[i].location.type;
+ paramArray[i].location.index = params[i].location.index;
+ paramArray[i].location.storage = params[i].location.storage;
+ }
+
+ BNBoolWithConfidence varArgConf;
+ varArgConf.value = hasVariableArguments.GetValue();
+ varArgConf.confidence = hasVariableArguments.GetConfidence();
+
+ BNBoolWithConfidence canReturnConf;
+ canReturnConf.value = canReturn.GetValue();
+ canReturnConf.confidence = canReturn.GetConfidence();
+
+ BNOffsetWithConfidence stackAdjustConf;
+ stackAdjustConf.value = stackAdjust.GetValue();
+ stackAdjustConf.confidence = stackAdjust.GetConfidence();
+
+ std::vector<uint32_t> regStackAdjustRegs;
+ std::vector<BNOffsetWithConfidence> regStackAdjustValues;
+ size_t i = 0;
+ for (const auto& adjust: regStackAdjust)
+ {
+ regStackAdjustRegs[i] = adjust.first;
+ regStackAdjustValues[i].value = adjust.second.GetValue();
+ regStackAdjustValues[i].confidence = adjust.second.GetConfidence();
+ i ++;
+ }
+
+ std::vector<uint32_t> returnRegsRegs = returnRegs.GetValue();
+
+ BNRegisterSetWithConfidence returnRegsConf;
+ returnRegsConf.regs = returnRegsRegs.data();
+ returnRegsConf.count = returnRegs->size();
+ returnRegsConf.confidence = returnRegs.GetConfidence();
+
TypeBuilder type(BNCreateFunctionTypeBuilder(
- &returnValueConf, &callingConventionConf, paramArray, paramCount, &varArgConf, &stackAdjustConf));
+ &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf,
+ &canReturnConf, &stackAdjustConf, regStackAdjustRegs.data(),
+ regStackAdjustValues.data(), regStackAdjust.size(), &returnRegsConf, NoNameType));
delete[] paramArray;
return type;
}
diff --git a/typeparser.cpp b/typeparser.cpp
new file mode 100644
index 00000000..db6f8b21
--- /dev/null
+++ b/typeparser.cpp
@@ -0,0 +1,513 @@
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+
+TypeParser::TypeParser(const string& name) : m_nameForRegister(name) {}
+
+
+TypeParser::TypeParser(BNTypeParser* parser)
+{
+ m_object = parser;
+}
+
+
+vector<Ref<TypeParser>> TypeParser::GetList()
+{
+ size_t count;
+ BNTypeParser** list = BNGetTypeParserList(&count);
+ vector<Ref<TypeParser>> result;
+ for (size_t i = 0; i < count; i++)
+ result.push_back(new CoreTypeParser(list[i]));
+ BNFreeTypeParserList(list);
+ return result;
+}
+
+
+Ref<TypeParser> TypeParser::GetByName(const string& name)
+{
+ BNTypeParser* result = BNGetTypeParserByName(name.c_str());
+ if (!result)
+ return nullptr;
+ return new CoreTypeParser(result);
+}
+
+
+Ref<TypeParser> TypeParser::GetDefault()
+{
+ string name = Settings::Instance()->Get<string>("analysis.types.parserName");
+ return GetByName(name);
+}
+
+
+void TypeParser::Register(TypeParser* parser)
+{
+ BNTypeParserCallbacks cb;
+ cb.context = parser;
+ cb.preprocessSource = PreprocessSourceCallback;
+ cb.parseTypesFromSource = ParseTypesFromSourceCallback;
+ cb.parseTypeString = ParseTypeStringCallback;
+ cb.freeString = FreeStringCallback;
+ cb.freeResult = FreeResultCallback;
+ cb.freeErrorList = FreeErrorListCallback;
+ parser->m_object = BNRegisterTypeParser(parser->m_nameForRegister.c_str(), &cb);
+}
+
+
+bool TypeParser::PreprocessSourceCallback(void* ctxt,
+ const char* source, const char* fileName, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ const char* const* options, size_t optionCount,
+ const char* const* includeDirs, size_t includeDirCount,
+ char** output, BNTypeParserError** errors, size_t* errorCount
+)
+{
+ TypeParser* parser = (TypeParser*)ctxt;
+
+ map<QualifiedName, TypeAndId> existingTypesCpp;
+ for (size_t i = 0; i < existingTypeCount; i ++)
+ {
+ QualifiedName qname = QualifiedName::FromAPIObject(&existingTypes[i].name);
+ TypeAndId type = {
+ existingTypes[i].id,
+ new Type(existingTypes[i].type),
+ };
+ existingTypesCpp.insert({qname, type});
+ }
+
+ vector<string> optionsCpp;
+ for (size_t i = 0; i < optionCount; i ++)
+ {
+ optionsCpp.push_back(options[i]);
+ }
+
+ vector<string> includeDirsCpp;
+ for (size_t i = 0; i < includeDirCount; i ++)
+ {
+ includeDirsCpp.push_back(includeDirs[i]);
+ }
+
+ std::string outputCpp;
+ vector<TypeParserError> errorsCpp;
+ bool success = parser->PreprocessSource(source, fileName, new Platform(platform),
+ existingTypesCpp, optionsCpp, includeDirsCpp, outputCpp, errorsCpp);
+
+ if (success)
+ {
+ *output = BNAllocString(outputCpp.c_str());
+ }
+ else
+ {
+ *output = nullptr;
+ }
+
+ *errorCount = errorsCpp.size();
+ *errors = new BNTypeParserError[errorsCpp.size()];
+ for (size_t i = 0; i < errorsCpp.size(); ++i)
+ {
+ (*errors)[i].severity = errorsCpp[i].severity;
+ (*errors)[i].message = BNAllocString(errorsCpp[i].message.c_str());
+ (*errors)[i].fileName = BNAllocString(errorsCpp[i].fileName.c_str());
+ (*errors)[i].line = errorsCpp[i].line;
+ (*errors)[i].column = errorsCpp[i].column;
+ }
+
+ return success;
+}
+
+
+bool TypeParser::ParseTypesFromSourceCallback(void* ctxt,
+ const char* source, const char* fileName, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ const char* const* options, size_t optionCount,
+ const char* const* includeDirs, size_t includeDirCount,
+ const char* autoTypeSource, BNTypeParserResult* result,
+ BNTypeParserError** errors, size_t* errorCount
+)
+{
+ TypeParser* parser = (TypeParser*)ctxt;
+
+ map<QualifiedName, TypeAndId> existingTypesCpp;
+ for (size_t i = 0; i < existingTypeCount; i ++)
+ {
+ QualifiedName qname = QualifiedName::FromAPIObject(&existingTypes[i].name);
+ TypeAndId type = {
+ existingTypes[i].id,
+ new Type(existingTypes[i].type),
+ };
+ existingTypesCpp.insert({qname, type});
+ }
+
+ vector<string> optionsCpp;
+ for (size_t i = 0; i < optionCount; i ++)
+ {
+ optionsCpp.push_back(options[i]);
+ }
+
+ vector<string> includeDirsCpp;
+ for (size_t i = 0; i < includeDirCount; i ++)
+ {
+ includeDirsCpp.push_back(includeDirs[i]);
+ }
+
+ TypeParserResult resultCpp;
+ vector<TypeParserError> errorsCpp;
+ bool success = parser->ParseTypesFromSource(source, fileName, new Platform(platform),
+ existingTypesCpp, optionsCpp, includeDirsCpp, autoTypeSource, resultCpp, errorsCpp);
+
+ result->typeCount = resultCpp.types.size();
+ result->variableCount = resultCpp.variables.size();
+ result->functionCount = resultCpp.functions.size();
+ result->types = new BNParsedType[resultCpp.types.size()];
+ result->variables = new BNParsedType[resultCpp.variables.size()];
+ result->functions = new BNParsedType[resultCpp.functions.size()];
+
+ size_t n = 0;
+ for (auto& i : resultCpp.types)
+ {
+ result->types[n].name = i.name.GetAPIObject();
+ result->types[n].type = BNNewTypeReference(i.type->GetObject());
+ result->types[n].isUser = i.isUser;
+ n++;
+ }
+
+ n = 0;
+ for (auto& i : resultCpp.variables)
+ {
+ result->variables[n].name = i.name.GetAPIObject();
+ result->variables[n].type = BNNewTypeReference(i.type->GetObject());
+ result->variables[n].isUser = i.isUser;
+ n++;
+ }
+
+ n = 0;
+ for (auto& i : resultCpp.functions)
+ {
+ result->functions[n].name = i.name.GetAPIObject();
+ result->functions[n].type = BNNewTypeReference(i.type->GetObject());
+ result->functions[n].isUser = i.isUser;
+ n++;
+ }
+
+ *errorCount = errorsCpp.size();
+ *errors = new BNTypeParserError[errorsCpp.size()];
+ for (size_t i = 0; i < errorsCpp.size(); ++i)
+ {
+ (*errors)[i].severity = errorsCpp[i].severity;
+ (*errors)[i].message = BNAllocString(errorsCpp[i].message.c_str());
+ (*errors)[i].fileName = BNAllocString(errorsCpp[i].fileName.c_str());
+ (*errors)[i].line = errorsCpp[i].line;
+ (*errors)[i].column = errorsCpp[i].column;
+ }
+
+ return success;
+}
+
+
+bool TypeParser::ParseTypeStringCallback(void* ctxt,
+ const char* source, BNPlatform* platform,
+ const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
+ BNQualifiedNameAndType* result,
+ BNTypeParserError** errors, size_t* errorCount
+)
+{
+ TypeParser* parser = (TypeParser*)ctxt;
+
+ map<QualifiedName, TypeAndId> existingTypesCpp;
+ for (size_t i = 0; i < existingTypeCount; i ++)
+ {
+ QualifiedName qname = QualifiedName::FromAPIObject(&existingTypes[i].name);
+ TypeAndId type = {
+ existingTypes[i].id,
+ new Type(existingTypes[i].type),
+ };
+ existingTypesCpp.insert({qname, type});
+ }
+
+ QualifiedNameAndType resultCpp;
+ vector<TypeParserError> errorsCpp;
+ bool success = parser->ParseTypeString(source, new Platform(platform), existingTypesCpp,
+ resultCpp, errorsCpp);
+
+ result->name = resultCpp.name.GetAPIObject();
+ result->type = BNNewTypeReference(resultCpp.type->GetObject());
+
+ *errorCount = errorsCpp.size();
+ *errors = new BNTypeParserError[errorsCpp.size()];
+ for (size_t i = 0; i < errorsCpp.size(); ++i)
+ {
+ (*errors)[i].severity = errorsCpp[i].severity;
+ (*errors)[i].message = BNAllocString(errorsCpp[i].message.c_str());
+ (*errors)[i].fileName = BNAllocString(errorsCpp[i].fileName.c_str());
+ (*errors)[i].line = errorsCpp[i].line;
+ (*errors)[i].column = errorsCpp[i].column;
+ }
+
+ return success;
+}
+
+
+void TypeParser::FreeStringCallback(void* ctxt, char* result)
+{
+ BNFreeString(result);
+}
+
+
+void TypeParser::FreeResultCallback(void* ctxt, BNTypeParserResult* result)
+{
+ for (size_t i = 0; i < result->typeCount; i++)
+ {
+ QualifiedName::FreeAPIObject(&result->types[i].name);
+ BNFreeType(result->types[i].type);
+ }
+ for (size_t i = 0; i < result->variableCount; i++)
+ {
+ QualifiedName::FreeAPIObject(&result->variables[i].name);
+ BNFreeType(result->variables[i].type);
+ }
+ for (size_t i = 0; i < result->functionCount; i++)
+ {
+ QualifiedName::FreeAPIObject(&result->functions[i].name);
+ BNFreeType(result->functions[i].type);
+ }
+ delete[] result->types;
+ delete[] result->variables;
+ delete[] result->functions;
+}
+
+
+void TypeParser::FreeErrorListCallback(void* ctxt, BNTypeParserError* errors, size_t errorCount)
+{
+ for (size_t i = 0; i < errorCount; i ++)
+ {
+ BNFreeString(errors[i].message);
+ BNFreeString(errors[i].fileName);
+ }
+ delete[] errors;
+}
+
+
+CoreTypeParser::CoreTypeParser(BNTypeParser* parser): TypeParser(parser)
+{
+
+}
+
+
+bool CoreTypeParser::PreprocessSource(const std::string& source, const std::string& fileName,
+ Ref<Platform> platform, const std::map<QualifiedName, TypeAndId>& existingTypes,
+ const std::vector<std::string>& options, const std::vector<std::string>& includeDirs,
+ std::string& output, std::vector<TypeParserError>& errors)
+{
+ const char** apiOptions = new const char*[options.size()];
+ for (size_t i = 0; i < options.size(); ++i)
+ {
+ apiOptions[i] = options[i].c_str();
+ }
+ const char** apiIncludeDirs = new const char*[includeDirs.size()];
+ for (size_t i = 0; i < includeDirs.size(); ++i)
+ {
+ apiIncludeDirs[i] = includeDirs[i].c_str();
+ }
+
+ BNQualifiedNameTypeAndId* apiExistingTypes = new BNQualifiedNameTypeAndId[existingTypes.size()];
+ size_t i = 0;
+ for (const auto& pair: existingTypes)
+ {
+ apiExistingTypes[i].name = pair.first.GetAPIObject();
+ apiExistingTypes[i].id = BNAllocString(pair.second.id.c_str());
+ apiExistingTypes[i].type = pair.second.type->GetObject();
+ i++;
+ }
+
+ char* apiOutput;
+ BNTypeParserError* apiErrors;
+ size_t errorCount;
+
+ auto success = BNTypeParserPreprocessSource(m_object, source.c_str(), fileName.c_str(),
+ platform->GetObject(), apiExistingTypes, existingTypes.size(),
+ apiOptions, options.size(), apiIncludeDirs, includeDirs.size(), &apiOutput,
+ &apiErrors, &errorCount);
+
+ delete [] apiOptions;
+ delete [] apiIncludeDirs;
+
+ for (size_t j = 0; j < existingTypes.size(); j ++)
+ {
+ QualifiedName::FreeAPIObject(&apiExistingTypes[j].name);
+ BNFreeString(apiExistingTypes[j].id);
+ }
+ delete [] apiExistingTypes;
+
+ for (size_t j = 0; j < errorCount; j ++)
+ {
+ TypeParserError error;
+ error.severity = apiErrors[j].severity,
+ error.message = apiErrors[j].message,
+ error.fileName = apiErrors[j].fileName,
+ error.line = apiErrors[j].line,
+ error.column = apiErrors[j].column,
+ errors.push_back(error);
+ }
+ BNFreeTypeParserErrors(apiErrors, errorCount);
+
+ if (!success)
+ {
+ return false;
+ }
+
+ output = apiOutput;
+ BNFreeString(apiOutput);
+ return true;
+}
+
+
+bool CoreTypeParser::ParseTypesFromSource(const std::string& source, const std::string& fileName,
+ Ref<Platform> platform, const std::map<QualifiedName, TypeAndId>& existingTypes,
+ const std::vector<std::string>& options, const std::vector<std::string>& includeDirs,
+ const std::string& autoTypeSource, TypeParserResult& result, std::vector<TypeParserError>& errors)
+{
+ const char** apiOptions = new const char*[options.size()];
+ for (size_t i = 0; i < options.size(); ++i)
+ {
+ apiOptions[i] = options[i].c_str();
+ }
+ const char** apiIncludeDirs = new const char*[includeDirs.size()];
+ for (size_t i = 0; i < includeDirs.size(); ++i)
+ {
+ apiIncludeDirs[i] = includeDirs[i].c_str();
+ }
+
+ BNQualifiedNameTypeAndId* apiExistingTypes = new BNQualifiedNameTypeAndId[existingTypes.size()];
+ size_t i = 0;
+ for (const auto& pair: existingTypes)
+ {
+ apiExistingTypes[i].name = pair.first.GetAPIObject();
+ apiExistingTypes[i].id = BNAllocString(pair.second.id.c_str());
+ apiExistingTypes[i].type = pair.second.type->GetObject();
+ i++;
+ }
+
+ BNTypeParserResult apiResult;
+ BNTypeParserError* apiErrors;
+ size_t errorCount;
+
+ auto success = BNTypeParserParseTypesFromSource(m_object, source.c_str(), fileName.c_str(),
+ platform->GetObject(), apiExistingTypes, existingTypes.size(),
+ apiOptions, options.size(), apiIncludeDirs, includeDirs.size(), autoTypeSource.c_str(), &apiResult,
+ &apiErrors, &errorCount);
+
+ delete [] apiOptions;
+ delete [] apiIncludeDirs;
+
+ for (size_t j = 0; j < existingTypes.size(); j ++)
+ {
+ QualifiedName::FreeAPIObject(&apiExistingTypes[j].name);
+ BNFreeString(apiExistingTypes[j].id);
+ }
+ delete [] apiExistingTypes;
+
+ for (size_t j = 0; j < errorCount; j ++)
+ {
+ TypeParserError error;
+ error.severity = apiErrors[j].severity,
+ error.message = apiErrors[j].message,
+ error.fileName = apiErrors[j].fileName,
+ error.line = apiErrors[j].line,
+ error.column = apiErrors[j].column,
+ errors.push_back(error);
+ }
+ BNFreeTypeParserErrors(apiErrors, errorCount);
+
+ if (!success)
+ {
+ return false;
+ }
+
+ result.types.clear();
+ for (size_t j = 0; j < apiResult.typeCount; ++j)
+ {
+ result.types.push_back({
+ QualifiedName::FromAPIObject(&apiResult.types[j].name),
+ new Type(BNNewTypeReference(apiResult.types[j].type)),
+ apiResult.types[j].isUser
+ });
+ }
+
+ result.variables.clear();
+ for (size_t j = 0; j < apiResult.variableCount; ++j)
+ {
+ result.variables.push_back({
+ QualifiedName::FromAPIObject(&apiResult.variables[j].name),
+ new Type(BNNewTypeReference(apiResult.variables[j].type)),
+ apiResult.types[j].isUser
+ });
+ }
+
+ result.functions.clear();
+ for (size_t j = 0; j < apiResult.functionCount; ++j)
+ {
+ result.functions.push_back({
+ QualifiedName::FromAPIObject(&apiResult.functions[j].name),
+ new Type(BNNewTypeReference(apiResult.functions[j].type)),
+ apiResult.types[j].isUser
+ });
+ }
+
+ BNFreeTypeParserResult(&apiResult);
+ return true;
+}
+
+
+bool CoreTypeParser::ParseTypeString(const std::string& source, Ref<Platform> platform,
+ const std::map<QualifiedName, TypeAndId>& existingTypes,
+ QualifiedNameAndType& result, std::vector<TypeParserError>& errors)
+{
+ BNQualifiedNameTypeAndId* apiExistingTypes = new BNQualifiedNameTypeAndId[existingTypes.size()];
+ size_t i = 0;
+ for (const auto& pair: existingTypes)
+ {
+ apiExistingTypes[i].name = pair.first.GetAPIObject();
+ apiExistingTypes[i].id = BNAllocString(pair.second.id.c_str());
+ apiExistingTypes[i].type = pair.second.type->GetObject();
+ }
+
+ BNQualifiedNameAndType apiResult;
+ BNTypeParserError* apiErrors;
+ size_t errorCount;
+
+ auto success = BNTypeParserParseTypeString(m_object, source.c_str(), platform->GetObject(),
+ apiExistingTypes, existingTypes.size(), &apiResult,
+ &apiErrors, &errorCount);
+
+ for (size_t j = 0; j < existingTypes.size(); j ++)
+ {
+ QualifiedName::FreeAPIObject(&apiExistingTypes[j].name);
+ BNFreeString(apiExistingTypes[j].id);
+ }
+ delete [] apiExistingTypes;
+
+ for (size_t j = 0; j < errorCount; j ++)
+ {
+ TypeParserError error;
+ error.severity = apiErrors[j].severity,
+ error.message = apiErrors[j].message,
+ error.fileName = apiErrors[j].fileName,
+ error.line = apiErrors[j].line,
+ error.column = apiErrors[j].column,
+ errors.push_back(error);
+ }
+ BNFreeTypeParserErrors(apiErrors, errorCount);
+
+ if (!success)
+ {
+ return false;
+ }
+
+ result.name = QualifiedName::FromAPIObject(&apiResult.name);
+ result.type = new Type(BNNewTypeReference(apiResult.type));
+ BNFreeQualifiedNameAndType(&apiResult);
+
+ return true;
+}
diff --git a/typeprinter.cpp b/typeprinter.cpp
new file mode 100644
index 00000000..aed010b2
--- /dev/null
+++ b/typeprinter.cpp
@@ -0,0 +1,400 @@
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+TypePrinter::TypePrinter(const std::string& name): m_nameForRegister(name)
+{
+
+}
+
+
+TypePrinter::TypePrinter(BNTypePrinter* printer)
+{
+ m_object = printer;
+}
+
+
+bool TypePrinter::GetTypeTokensCallback(void* ctxt, BNType* type, BNPlatform* platform,
+ BNQualifiedName* name, uint8_t baseConfidence, BNTokenEscapingType escaping,
+ BNInstructionTextToken** result, size_t* resultCount)
+{
+ TypePrinter* printer = (TypePrinter*)ctxt;
+ vector<InstructionTextToken> tokens = printer->GetTypeTokens(
+ new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr,
+ QualifiedName::FromAPIObject(name), baseConfidence, escaping);
+
+ *resultCount = tokens.size();
+ *result = InstructionTextToken::CreateInstructionTextTokenList(tokens);
+ return true;
+}
+
+
+bool TypePrinter::GetTypeTokensBeforeNameCallback(void* ctxt, BNType* type,
+ BNPlatform* platform, uint8_t baseConfidence, BNType* parentType,
+ BNTokenEscapingType escaping, BNInstructionTextToken** result,
+ size_t* resultCount)
+{
+ TypePrinter* printer = (TypePrinter*)ctxt;
+ vector<InstructionTextToken> tokens = printer->GetTypeTokensBeforeName(
+ new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr,
+ baseConfidence, parentType ? new Type(BNNewTypeReference(parentType)) : nullptr, escaping);
+
+ *resultCount = tokens.size();
+ *result = InstructionTextToken::CreateInstructionTextTokenList(tokens);
+ return true;
+}
+
+
+bool TypePrinter::GetTypeTokensAfterNameCallback(void* ctxt, BNType* type,
+ BNPlatform* platform, uint8_t baseConfidence, BNType* parentType,
+ BNTokenEscapingType escaping, BNInstructionTextToken** result,
+ size_t* resultCount)
+{
+ TypePrinter* printer = (TypePrinter*)ctxt;
+ vector<InstructionTextToken> tokens = printer->GetTypeTokensAfterName(
+ new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr,
+ baseConfidence, parentType ? new Type(BNNewTypeReference(parentType)) : nullptr, escaping);
+
+ *resultCount = tokens.size();
+ *result = InstructionTextToken::CreateInstructionTextTokenList(tokens);
+ return true;
+}
+
+
+bool TypePrinter::GetTypeStringCallback(void* ctxt, BNType* type, BNPlatform* platform,
+ BNQualifiedName* name, BNTokenEscapingType escaping, char** result)
+{
+ TypePrinter* printer = (TypePrinter*)ctxt;
+ string text = printer->GetTypeString(
+ new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr,
+ QualifiedName::FromAPIObject(name), escaping);
+
+ *result = BNAllocString(text.c_str());
+ return true;
+}
+
+
+bool TypePrinter::GetTypeStringBeforeNameCallback(void* ctxt, BNType* type,
+ BNPlatform* platform, BNTokenEscapingType escaping, char** result)
+{
+ TypePrinter* printer = (TypePrinter*)ctxt;
+ string text = printer->GetTypeStringBeforeName(
+ new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr,
+ escaping);
+
+ *result = BNAllocString(text.c_str());
+ return true;
+}
+
+
+bool TypePrinter::GetTypeStringAfterNameCallback(void* ctxt, BNType* type,
+ BNPlatform* platform, BNTokenEscapingType escaping, char** result)
+{
+ TypePrinter* printer = (TypePrinter*)ctxt;
+ string text = printer->GetTypeStringAfterName(
+ new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr,
+ escaping);
+
+ *result = BNAllocString(text.c_str());
+ return true;
+}
+
+
+bool TypePrinter::GetTypeLinesCallback(void* ctxt, BNType* type, BNBinaryView* data,
+ BNQualifiedName* name, int lineWidth, bool collapsed,
+ BNTokenEscapingType escaping, BNTypeDefinitionLine** result, size_t* resultCount)
+{
+ TypePrinter* printer = (TypePrinter*)ctxt;
+ vector<TypeDefinitionLine> lines = printer->GetTypeLines(
+ new Type(BNNewTypeReference(type)), new BinaryView(BNNewViewReference(data)),
+ QualifiedName::FromAPIObject(name), lineWidth, collapsed, escaping);
+
+ *resultCount = lines.size();
+ *result = TypeDefinitionLine::CreateTypeDefinitionLineList(lines);
+ return true;
+}
+
+
+void TypePrinter::FreeTokensCallback(void* ctxt, BNInstructionTextToken* tokens, size_t count)
+{
+ InstructionTextToken::FreeInstructionTextTokenList(tokens, count);
+}
+
+
+void TypePrinter::FreeStringCallback(void* ctxt, char* string)
+{
+ BNFreeString(string);
+}
+
+
+void TypePrinter::FreeLinesCallback(void* ctxt, BNTypeDefinitionLine* lines, size_t count)
+{
+ TypeDefinitionLine::FreeTypeDefinitionLineList(lines, count);
+}
+
+
+void TypePrinter::Register(TypePrinter* printer)
+{
+ BNTypePrinterCallbacks cb;
+ cb.context = printer;
+ cb.getTypeTokens = GetTypeTokensCallback;
+ cb.getTypeTokensBeforeName = GetTypeTokensBeforeNameCallback;
+ cb.getTypeTokensAfterName = GetTypeTokensAfterNameCallback;
+ cb.getTypeString = GetTypeStringCallback;
+ cb.getTypeStringBeforeName = GetTypeStringBeforeNameCallback;
+ cb.getTypeStringAfterName = GetTypeStringAfterNameCallback;
+ cb.getTypeLines = GetTypeLinesCallback;
+ cb.freeTokens = FreeTokensCallback;
+ cb.freeString = FreeStringCallback;
+ cb.freeLines = FreeLinesCallback;
+ printer->m_object = BNRegisterTypePrinter(printer->m_nameForRegister.c_str(), &cb);
+}
+
+
+std::vector<Ref<TypePrinter>> TypePrinter::GetList()
+{
+ size_t count;
+ BNTypePrinter** list = BNGetTypePrinterList(&count);
+ vector<Ref<TypePrinter>> result;
+ for (size_t i = 0; i < count; i++)
+ result.push_back(new CoreTypePrinter(list[i]));
+ BNFreeTypePrinterList(list);
+ return result;
+}
+
+
+Ref<TypePrinter> TypePrinter::GetByName(const std::string& name)
+{
+ BNTypePrinter* result = BNGetTypePrinterByName(name.c_str());
+ if (!result)
+ return nullptr;
+ return new CoreTypePrinter(result);
+}
+
+
+Ref<TypePrinter> TypePrinter::GetDefault()
+{
+ string name = Settings::Instance()->Get<string>("analysis.types.printerName");
+ return GetByName(name);
+}
+
+
+std::vector<InstructionTextToken> TypePrinter::GetTypeTokens(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ const QualifiedName& name,
+ uint8_t baseConfidence,
+ BNTokenEscapingType escaping
+)
+{
+ vector<InstructionTextToken> before = GetTypeTokensBeforeName(type, platform, baseConfidence, nullptr, escaping);
+ vector<InstructionTextToken> after = GetTypeTokensAfterName(type, platform, baseConfidence, nullptr, escaping);
+ if (before.size() > 0 && before.back().text.back() != ' ' && before.back().text.back() != '*' &&
+ before.back().text.back() != '&' && after.size() > 0 && after.front().text.front() != ' ')
+ {
+ if (type->GetClass() != FunctionTypeClass)
+ before.emplace_back(TextToken, " ");
+ }
+ before.insert(before.end(), after.begin(), after.end());
+ return before;
+}
+
+
+std::string TypePrinter::GetTypeString(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ const QualifiedName& name,
+ BNTokenEscapingType escaping
+)
+{
+ const string before = GetTypeStringBeforeName(type, platform, escaping);
+ const string qName = name.GetString(escaping);
+ const string after = GetTypeStringAfterName(type, platform, escaping);
+ if (((before.size() > 0) && (qName.size() > 0) && (before.back() != ' ') && (qName.front() != ' ')) ||
+ ((before.size() > 0) && (after.size() > 0) && (before.back() != ' ') && (after.front() != ' ')))
+ return before + " " + qName + after;
+ return before + qName + after;
+}
+
+
+std::string TypePrinter::GetTypeStringBeforeName(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ BNTokenEscapingType escaping
+)
+{
+ vector<InstructionTextToken> tokens = GetTypeTokensBeforeName(type, platform, BN_FULL_CONFIDENCE, nullptr, escaping);
+ string result;
+ for (const auto& i : tokens)
+ result += i.text;
+ return result;
+}
+
+
+std::string TypePrinter::GetTypeStringAfterName(
+ Ref<Type> type,
+ Ref<Platform> platform,
+ BNTokenEscapingType escaping
+)
+{
+ vector<InstructionTextToken> tokens = GetTypeTokensAfterName(type, platform, BN_FULL_CONFIDENCE, nullptr, escaping);
+ string result;
+ for (const auto& i : tokens)
+ result += i.text;
+ return result;
+}
+
+
+CoreTypePrinter::CoreTypePrinter(BNTypePrinter* printer): TypePrinter(printer)
+{
+
+}
+
+
+std::vector<InstructionTextToken> CoreTypePrinter::GetTypeTokens(Ref<Type> type,
+ Ref<Platform> platform, const QualifiedName& name,
+ uint8_t baseConfidence, BNTokenEscapingType escaping)
+{
+ BNQualifiedName qname = name.GetAPIObject();
+
+ BNInstructionTextToken* tokens;
+ size_t tokenCount;
+
+ bool success = BNGetTypePrinterTypeTokens(GetObject(), type->GetObject(),
+ platform ? platform->GetObject() : nullptr, &qname, baseConfidence, escaping, &tokens, &tokenCount);
+
+ QualifiedName::FreeAPIObject(&qname);
+ if (!success)
+ return {};
+
+ vector<InstructionTextToken> cppTokens =
+ InstructionTextToken::ConvertInstructionTextTokenList(tokens, tokenCount);
+ BNFreeInstructionText(tokens, tokenCount);
+
+ return cppTokens;
+}
+
+
+std::vector<InstructionTextToken> CoreTypePrinter::GetTypeTokensBeforeName(Ref<Type> type,
+ Ref<Platform> platform, uint8_t baseConfidence,
+ Ref<Type> parentType, BNTokenEscapingType escaping)
+{
+ BNInstructionTextToken* tokens;
+ size_t tokenCount;
+
+ bool success = BNGetTypePrinterTypeTokensBeforeName(GetObject(), type->GetObject(),
+ platform ? platform->GetObject() : nullptr, baseConfidence,
+ parentType ? parentType->GetObject() : nullptr, escaping, &tokens, &tokenCount);
+
+ if (!success)
+ return {};
+
+ vector<InstructionTextToken> cppTokens =
+ InstructionTextToken::ConvertInstructionTextTokenList(tokens, tokenCount);
+ BNFreeInstructionText(tokens, tokenCount);
+
+ return cppTokens;
+}
+
+
+std::vector<InstructionTextToken> CoreTypePrinter::GetTypeTokensAfterName(Ref<Type> type,
+ Ref<Platform> platform, uint8_t baseConfidence,
+ Ref<Type> parentType, BNTokenEscapingType escaping)
+{
+ BNInstructionTextToken* tokens;
+ size_t tokenCount;
+
+ bool success = BNGetTypePrinterTypeTokensAfterName(GetObject(), type->GetObject(),
+ platform ? platform->GetObject() : nullptr, baseConfidence,
+ parentType ? parentType->GetObject() : nullptr, escaping, &tokens, &tokenCount);
+
+ if (!success)
+ return {};
+
+ vector<InstructionTextToken> cppTokens =
+ InstructionTextToken::ConvertInstructionTextTokenList(tokens, tokenCount);
+ BNFreeInstructionText(tokens, tokenCount);
+
+ return cppTokens;
+}
+
+
+std::string CoreTypePrinter::GetTypeString(Ref<Type> type, Ref<Platform> platform,
+ const QualifiedName& name, BNTokenEscapingType escaping)
+{
+ BNQualifiedName qname = name.GetAPIObject();
+
+ char* result;
+ bool success = BNGetTypePrinterTypeString(GetObject(), type->GetObject(), platform ? platform->GetObject() : nullptr, &qname, escaping, &result);
+
+ QualifiedName::FreeAPIObject(&qname);
+ if (!success)
+ return {};
+
+ string cppResult = result;
+ BNFreeString(result);
+
+ return cppResult;
+
+}
+
+
+std::string CoreTypePrinter::GetTypeStringBeforeName(Ref<Type> type, Ref<Platform> platform,
+ BNTokenEscapingType escaping)
+{
+ char* result;
+ bool success = BNGetTypePrinterTypeStringBeforeName(GetObject(), type->GetObject(), platform ? platform->GetObject() : nullptr, escaping, &result);
+
+ if (!success)
+ return {};
+
+ string cppResult = result;
+ BNFreeString(result);
+
+ return cppResult;
+}
+
+
+std::string CoreTypePrinter::GetTypeStringAfterName(Ref<Type> type, Ref<Platform> platform,
+ BNTokenEscapingType escaping)
+{
+ char* result;
+ bool success = BNGetTypePrinterTypeStringAfterName(GetObject(), type->GetObject(), platform ? platform->GetObject() : nullptr, escaping, &result);
+
+ if (!success)
+ return {};
+
+ string cppResult = result;
+ BNFreeString(result);
+
+ return cppResult;
+}
+
+
+std::vector<TypeDefinitionLine> CoreTypePrinter::GetTypeLines(Ref<Type> type,
+ Ref<BinaryView> data, const QualifiedName& name, int lineWidth,
+ bool collapsed, BNTokenEscapingType escaping)
+{
+ BNTypeDefinitionLine* lines;
+ size_t lineCount;
+
+ BNQualifiedName qname = name.GetAPIObject();
+
+ bool success = BNGetTypePrinterTypeLines(GetObject(), type->GetObject(), data->GetObject(), &qname, lineWidth, collapsed, escaping, &lines, &lineCount);
+
+ QualifiedName::FreeAPIObject(&qname);
+ if (!success)
+ return {};
+
+ vector<TypeDefinitionLine> cppLines;
+
+ for (size_t i = 0; i < lineCount; ++i)
+ {
+ cppLines.push_back(TypeDefinitionLine::FromAPIObject(&lines[i]));
+ }
+ BNFreeTypeDefinitionLineList(lines, lineCount);
+
+ return cppLines;
+
+}
diff --git a/ui/createtypedialog.h b/ui/createtypedialog.h
index 0df1c613..a2bd6e2c 100644
--- a/ui/createtypedialog.h
+++ b/ui/createtypedialog.h
@@ -2,6 +2,7 @@
#include <QtWidgets/QDialog>
#include <QtWidgets/QLineEdit>
+#include <QtWidgets/QTextEdit>
#include "binaryninjaapi.h"
#include "dialogtextedit.h"
#include "uicontext.h"
@@ -10,16 +11,18 @@ class BINARYNINJAUIAPI CreateTypeDialog : public QDialog
{
Q_OBJECT
+ QLineEdit* m_arguments;
DialogTextEdit* m_code;
+ QTextEdit* m_errors;
BinaryViewRef m_data;
- std::map<BinaryNinja::QualifiedName, TypeRef> m_results;
+ std::vector<BinaryNinja::ParsedType> m_results;
std::set<BinaryNinja::QualifiedName> m_typesAllowRedefinition;
public:
CreateTypeDialog(QWidget* parent, BinaryViewRef data, const QString& title, const QString& definition,
const std::set<BinaryNinja::QualifiedName>& typesAllowRedefinition = {});
- std::map<BinaryNinja::QualifiedName, TypeRef> getResults() { return m_results; }
+ std::vector<BinaryNinja::ParsedType> getResults() { return m_results; }
private Q_SLOTS:
void createType();