From 09c85a868bad7e6e1b1b117eefc2c2aa9aaef4be Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Wed, 16 Feb 2022 17:54:15 -0500 Subject: Clang+TypeParser APIs --- typeparser.cpp | 513 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 typeparser.cpp (limited to 'typeparser.cpp') 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> TypeParser::GetList() +{ + size_t count; + BNTypeParser** list = BNGetTypeParserList(&count); + vector> result; + for (size_t i = 0; i < count; i++) + result.push_back(new CoreTypeParser(list[i])); + BNFreeTypeParserList(list); + return result; +} + + +Ref TypeParser::GetByName(const string& name) +{ + BNTypeParser* result = BNGetTypeParserByName(name.c_str()); + if (!result) + return nullptr; + return new CoreTypeParser(result); +} + + +Ref TypeParser::GetDefault() +{ + string name = Settings::Instance()->Get("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 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 optionsCpp; + for (size_t i = 0; i < optionCount; i ++) + { + optionsCpp.push_back(options[i]); + } + + vector includeDirsCpp; + for (size_t i = 0; i < includeDirCount; i ++) + { + includeDirsCpp.push_back(includeDirs[i]); + } + + std::string outputCpp; + vector 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 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 optionsCpp; + for (size_t i = 0; i < optionCount; i ++) + { + optionsCpp.push_back(options[i]); + } + + vector includeDirsCpp; + for (size_t i = 0; i < includeDirCount; i ++) + { + includeDirsCpp.push_back(includeDirs[i]); + } + + TypeParserResult resultCpp; + vector 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 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 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, const std::map& existingTypes, + const std::vector& options, const std::vector& includeDirs, + std::string& output, std::vector& 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, const std::map& existingTypes, + const std::vector& options, const std::vector& includeDirs, + const std::string& autoTypeSource, TypeParserResult& result, std::vector& 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, + const std::map& existingTypes, + QualifiedNameAndType& result, std::vector& 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; +} -- cgit v1.3.1