From 1a565da676a7d1be45bfde1a5f40252838ccf5d8 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Mon, 7 Nov 2022 21:39:41 -0500 Subject: Convenience helpers for type parsing in c++ --- binaryninjaapi.h | 46 ++++++++++++++++++++++++++++ binaryninjacore.h | 2 ++ binaryview.cpp | 21 +++++++++++++ typeparser.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 159 insertions(+), 1 deletion(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index efebbfe7..b738c92a 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2805,6 +2805,7 @@ namespace BinaryNinja { class Metadata; class Structure; class NamedTypeReference; + struct ParsedType; struct TypeParserResult; class Component; class DebugInfo; @@ -4433,6 +4434,7 @@ namespace BinaryNinja { void DefineTypes(const std::vector>& types, std::function progress = {}); void DefineUserType(const QualifiedName& name, Ref type); void DefineUserTypes(const std::vector& types, std::function progress = {}); + void DefineUserTypes(const std::vector& types, std::function progress = {}); void UndefineType(const std::string& id); void UndefineUserType(const QualifiedName& name); void RenameType(const QualifiedName& oldName, const QualifiedName& newName); @@ -6478,6 +6480,13 @@ namespace BinaryNinja { std::string fileName; uint64_t line; uint64_t column; + + TypeParserError() = default; + TypeParserError(BNTypeParserErrorSeverity severity, const std::string& message): + severity(severity), message(message), fileName(""), line(0), column(0) + { + + } }; /*! @@ -11561,6 +11570,20 @@ namespace BinaryNinja { static Ref GetByName(const std::string& name); static Ref GetDefault(); + /*! + Parse a space-separated string of options into a list + \param optionsText Space-separated options text + \return List of options + */ + static std::vector ParseOptionsText(const std::string& optionsText); + + /*! + Format a list of parser errors into a big string + \param errors List of errors + \return String of formatted errors + */ + static std::string FormatParseErrors(const std::vector& errors); + /** Get the string representation of an option for passing to ParseTypes* \param option Option type @@ -11618,6 +11641,29 @@ namespace BinaryNinja { std::vector& errors ) = 0; + /*! + Parse an entire source file into types, variables, and functions + \param fileName Name of the file on disk containing the source + \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 + */ + bool ParseTypesFromSourceFile( + const std::string& fileName, + Ref platform, + const std::map& existingTypes, + const std::vector& options, + const std::vector& includeDirs, + const std::string& autoTypeSource, + TypeParserResult& result, + std::vector& errors + ); + /*! Parse a single type and name from a string containing their definition. \param source Source code to parse diff --git a/binaryninjacore.h b/binaryninjacore.h index f45fa675..c7530c22 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -5514,6 +5514,8 @@ extern "C" BNQualifiedNameAndType* result, BNTypeParserError** errors, size_t* errorCount ); + BINARYNINJACOREAPI char** BNParseTypeParserOptionsText(const char* optionsText, size_t* count); + BINARYNINJACOREAPI char* BNFormatTypeParserParseErrors(BNTypeParserError* errors, size_t count); BINARYNINJACOREAPI BNTypePrinter* BNRegisterTypePrinter( const char* name, BNTypePrinterCallbacks* callbacks); diff --git a/binaryview.cpp b/binaryview.cpp index e78333d5..e8c108a0 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -3565,6 +3565,27 @@ void BinaryView::DefineUserTypes(const vector& types, std: } +void BinaryView::DefineUserTypes(const vector& types, std::function 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(); + } + + ProgressContext cb; + cb.callback = progress; + BNDefineUserAnalysisTypes(m_object, apiTypes, types.size(), ProgressCallback, &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/typeparser.cpp b/typeparser.cpp index c93c03b3..6124004c 100644 --- a/typeparser.cpp +++ b/typeparser.cpp @@ -1,8 +1,9 @@ #include "binaryninjaapi.h" +#include using namespace BinaryNinja; using namespace std; - +namespace fs = std::filesystem; TypeParser::TypeParser(const string& name) : m_nameForRegister(name) {} @@ -57,6 +58,48 @@ void TypeParser::Register(TypeParser* parser) } +std::vector TypeParser::ParseOptionsText(const std::string& optionsText) +{ + size_t count; + char** options = BNParseTypeParserOptionsText(optionsText.c_str(), &count); + + std::vector result; + for (size_t i = 0; i < count; i++) + { + result.push_back(options[i]); + } + + BNFreeStringList(options, count); + return result; +} + + +std::string TypeParser::FormatParseErrors(const std::vector& errors) +{ + std::vector apiErrors; + for (const auto& error: errors) + { + BNTypeParserError apiError; + apiError.severity = error.severity; + apiError.message = BNAllocString(error.message.c_str()); + apiError.fileName = BNAllocString(error.fileName.c_str()); + apiError.line = error.line; + apiError.column = error.column; + apiErrors.push_back(apiError); + } + + char* string = BNFormatTypeParserParseErrors(apiErrors.data(), apiErrors.size()); + + for (auto& apiError: apiErrors) + { + BNFreeString(apiError.message); + BNFreeString(apiError.fileName); + } + + return string; +} + + bool TypeParser::GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const { // Default: Don't accept anything @@ -311,6 +354,52 @@ void TypeParser::FreeErrorListCallback(void* ctxt, BNTypeParserError* errors, si } +bool TypeParser::ParseTypesFromSourceFile(const string& fileName, Ref platform, + const map& existingTypes, const vector& options, + const vector& includeDirs, const string& autoTypeSource, TypeParserResult& result, + vector& errors) +{ + if (!fs::is_regular_file(fileName)) + { + errors.push_back(TypeParserError(FatalSeverity, string("error: argument '") + fileName + "' is not a file")); + return false; + } + + // Read file contents, then parse them + FILE* fp = fopen(fileName.c_str(), "rb"); + if (!fp) + { + errors.push_back(TypeParserError(FatalSeverity, string("file '") + fileName + "' not found")); + return false; + } + + fseek(fp, 0, SEEK_END); + long size = ftell(fp); + if(size == -1) + { + errors.push_back(TypeParserError(FatalSeverity, string("error: unable to open '") + fileName)); + return false; + } + fseek(fp, 0, SEEK_SET); + + char* data = new char[size + 2]; + if (fread(data, 1, size, fp) != (size_t)size) + { + errors.push_back(TypeParserError(FatalSeverity, string("error: file '") + fileName + "' could not be read")); + delete[] data; + fclose(fp); + return false; + } + data[size++] = '\n'; + data[size] = 0; + fclose(fp); + + bool ok = ParseTypesFromSource(data, fileName, platform, existingTypes, options, includeDirs, autoTypeSource, result, errors); + delete[] data; + return ok; +} + + CoreTypeParser::CoreTypeParser(BNTypeParser* parser): TypeParser(parser) { -- cgit v1.3.1