From 6222101365828410769950281422115789136654 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Thu, 25 Jul 2024 18:25:29 -0400 Subject: FFI helpers in the api too now --- ffi.cpp | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ffi.h | 169 +++++++++++++++++++++++++++++++++++++ 2 files changed, 464 insertions(+) create mode 100644 ffi.cpp create mode 100644 ffi.h diff --git a/ffi.cpp b/ffi.cpp new file mode 100644 index 00000000..6dee0a9d --- /dev/null +++ b/ffi.cpp @@ -0,0 +1,295 @@ + +#include "ffi.h" +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + + +char BN_API_PTR* BinaryNinja::AllocApiString(const char* string) +{ + return strdup(string); +} + + +void BinaryNinja::AllocApiString(const char* string, char BN_API_PTR** output) +{ + *output = AllocApiString(string); +} + + +char BN_API_PTR* BinaryNinja::AllocApiString(const string& string) +{ + return AllocApiString(string.c_str()); +} + + +void BinaryNinja::AllocApiString(const string& string, char BN_API_PTR** output) +{ + AllocApiString(string.c_str(), output); +} + + +char BN_API_PTR* BN_API_PTR* BinaryNinja::AllocApiStringList(const char* const* stringList, size_t count) +{ + char BN_API_PTR* BN_API_PTR* result = new char*[count]; + for (size_t i = 0; i < count; i++) + { + result[i] = AllocApiString(stringList[i]); + } + return result; +} + + +void BinaryNinja::AllocApiStringList(const char* const* stringList, size_t count, char BN_API_PTR* BN_API_PTR** output) +{ + *output = AllocApiStringList(stringList, count); +} + + +char BN_API_PTR* BN_API_PTR* BinaryNinja::AllocApiStringList(const vector& stringList, size_t* count) +{ + *count = stringList.size(); + vector intermediate; + intermediate.reserve(*count); + for (const auto& string: stringList) + { + intermediate.push_back(string.c_str()); + } + return AllocApiStringList(intermediate.data(), *count); +} + + +void BinaryNinja::AllocApiStringList(const vector& stringList, char BN_API_PTR* BN_API_PTR** output, size_t* count) +{ + *count = stringList.size(); + vector intermediate; + intermediate.reserve(*count); + for (const auto& string: stringList) + { + intermediate.push_back(string.c_str()); + } + AllocApiStringList(intermediate.data(), *count, output); +} + + +char BN_API_PTR* BN_API_PTR* BinaryNinja::AllocApiStringList(const set& stringList, size_t* count) +{ + *count = stringList.size(); + vector intermediate; + intermediate.reserve(*count); + for (const auto& string: stringList) + { + intermediate.push_back(string.c_str()); + } + return AllocApiStringList(intermediate.data(), *count); +} + + +void BinaryNinja::AllocApiStringList(const set& stringList, char BN_API_PTR* BN_API_PTR** output, size_t* count) +{ + *count = stringList.size(); + vector intermediate; + intermediate.reserve(*count); + for (const auto& string: stringList) + { + intermediate.push_back(string.c_str()); + } + AllocApiStringList(intermediate.data(), *count, output); +} + + +char BN_API_PTR* BN_API_PTR* BinaryNinja::AllocApiStringList(const unordered_set& stringList, size_t* count) +{ + *count = stringList.size(); + vector intermediate; + intermediate.reserve(*count); + for (const auto& string: stringList) + { + intermediate.push_back(string.c_str()); + } + return AllocApiStringList(intermediate.data(), *count); +} + + +void BinaryNinja::AllocApiStringList(const unordered_set& stringList, char BN_API_PTR* BN_API_PTR** output, size_t* count) +{ + *count = stringList.size(); + vector intermediate; + intermediate.reserve(*count); + for (const auto& string: stringList) + { + intermediate.push_back(string.c_str()); + } + AllocApiStringList(intermediate.data(), *count, output); +} + + +void BinaryNinja::AllocApiStringPairList(const vector>& stringPairList, char BN_API_PTR* BN_API_PTR** outputKeys, char BN_API_PTR* BN_API_PTR** outputValues, size_t* count) +{ + *count = stringPairList.size(); + vector intermediateKeys; + vector intermediateValues; + intermediateKeys.reserve(*count); + intermediateValues.reserve(*count); + for (const auto& pair: stringPairList) + { + intermediateKeys.push_back(pair.first.c_str()); + intermediateValues.push_back(pair.second.c_str()); + } + AllocApiStringList(intermediateKeys.data(), *count, outputKeys); + AllocApiStringList(intermediateValues.data(), *count, outputValues); +} + + +void BinaryNinja::AllocApiStringPairList(const map& stringPairList, char BN_API_PTR* BN_API_PTR** outputKeys, char BN_API_PTR* BN_API_PTR** outputValues, size_t* count) +{ + *count = stringPairList.size(); + vector intermediateKeys; + vector intermediateValues; + intermediateKeys.reserve(*count); + intermediateValues.reserve(*count); + for (const auto& pair: stringPairList) + { + intermediateKeys.push_back(pair.first.c_str()); + intermediateValues.push_back(pair.second.c_str()); + } + AllocApiStringList(intermediateKeys.data(), *count, outputKeys); + AllocApiStringList(intermediateValues.data(), *count, outputValues); +} + + +void BinaryNinja::AllocApiStringPairList(const unordered_map& stringPairList, char BN_API_PTR* BN_API_PTR** outputKeys, char BN_API_PTR* BN_API_PTR** outputValues, size_t* count) +{ + *count = stringPairList.size(); + vector intermediateKeys; + vector intermediateValues; + intermediateKeys.reserve(*count); + intermediateValues.reserve(*count); + for (const auto& pair: stringPairList) + { + intermediateKeys.push_back(pair.first.c_str()); + intermediateValues.push_back(pair.second.c_str()); + } + AllocApiStringList(intermediateKeys.data(), *count, outputKeys); + AllocApiStringList(intermediateValues.data(), *count, outputValues); +} + + +string BinaryNinja::ParseString(const char* string) +{ + return string; +} + + +vector BinaryNinja::ParseStringList(const char* const* stringList, size_t count) +{ + vector result; + result.reserve(count); + for (size_t i = 0; i < count; i ++) + { + result.push_back(stringList[i]); + } + return result; +} + + +set BinaryNinja::ParseStringSet(const char* const* stringList, size_t count) +{ + set result; + for (size_t i = 0; i < count; i ++) + { + result.insert(stringList[i]); + } + return result; +} + + +unordered_set BinaryNinja::ParseStringUnorderedSet(const char* const* stringList, size_t count) +{ + unordered_set result; + result.reserve(count); + for (size_t i = 0; i < count; i ++) + { + result.insert(stringList[i]); + } + return result; +} + + +vector> BinaryNinja::ParseStringPairList(const char* const* keys, const char* const* values, size_t count) +{ + vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i ++) + { + result.push_back({keys[i], values[i]}); + } + return result; +} + + +map BinaryNinja::ParseStringMap(const char* const* keys, const char* const* values, size_t count) +{ + map result; + for (size_t i = 0; i < count; i ++) + { + result.insert({keys[i], values[i]}); + } + return result; +} + + +unordered_map BinaryNinja::ParseStringUnorderedMap(const char* const* keys, const char* const* values, size_t count) +{ + unordered_map result; + result.reserve(count); + for (size_t i = 0; i < count; i ++) + { + result.insert({keys[i], values[i]}); + } + return result; +} + + +void BinaryNinja::FreeApiString(char BN_API_PTR* string) +{ + // Allocated with strdup() + free(string); +} + + +void BinaryNinja::FreeApiStringList(char BN_API_PTR* BN_API_PTR* stringList, size_t count) +{ + for (size_t i = 0; i < count; i++) + { + FreeApiString(stringList[i]); + } + delete[] stringList; +} + + +void BinaryNinja::FreeApiStringPairList(char BN_API_PTR* BN_API_PTR* keys, char BN_API_PTR* BN_API_PTR* values, size_t count) +{ + FreeApiStringList(keys, count); + FreeApiStringList(values, count); +} + + +void BinaryNinja::FreeCoreString(char BN_CORE_PTR* string) +{ + BNFreeString(string); +} + + +void BinaryNinja::FreeCoreStringList(char BN_CORE_PTR* BN_CORE_PTR* stringList, size_t count) +{ + BNFreeStringList(stringList, count); +} + + +void BinaryNinja::FreeCoreStringPairList(char BN_CORE_PTR* BN_CORE_PTR* keys, char BN_CORE_PTR* BN_CORE_PTR* values, size_t count) +{ + FreeCoreStringList(keys, count); + FreeCoreStringList(values, count); +} diff --git a/ffi.h b/ffi.h new file mode 100644 index 00000000..f9686cb5 --- /dev/null +++ b/ffi.h @@ -0,0 +1,169 @@ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "binaryninjacore.h" + +// FFI Helpers + +#ifdef __clang__ +/*! Pointer is allocated by the core */ +#define BN_CORE_PTR __attribute__((annotate("bn_core_ptr"))) +/*! Pointer is allocated by the api */ +#define BN_API_PTR __attribute__((annotate("bn_api_ptr"))) +#else +#define BN_CORE_PTR +#define BN_API_PTR +#endif + +namespace BinaryNinja +{ + //------------------------------------------------------------------------------------ + //region string <-> char* + + char BN_API_PTR* AllocApiString(const char* string); + void AllocApiString(const char* string, char BN_API_PTR** output); + char BN_API_PTR* AllocApiString(const std::string& string); + void AllocApiString(const std::string& string, char BN_API_PTR** output); + + char BN_API_PTR* BN_API_PTR* AllocApiStringList(const char* const* stringList, size_t count); + void AllocApiStringList(const char* const* stringList, size_t count, char BN_API_PTR* BN_API_PTR** output); + char BN_API_PTR* BN_API_PTR* AllocApiStringList(const std::vector& stringList, size_t* count); + void AllocApiStringList(const std::vector& stringList, char BN_API_PTR* BN_API_PTR** output, size_t* count); + char BN_API_PTR* BN_API_PTR* AllocApiStringList(const std::set& stringList, size_t* count); + void AllocApiStringList(const std::set& stringList, char BN_API_PTR* BN_API_PTR** output, size_t* count); + char BN_API_PTR* BN_API_PTR* AllocApiStringList(const std::unordered_set& stringList, size_t* count); + void AllocApiStringList(const std::unordered_set& stringList, char BN_API_PTR* BN_API_PTR** output, size_t* count); + + void AllocApiStringPairList(const std::vector>& stringPairList, char BN_API_PTR* BN_API_PTR** outputKeys, char BN_API_PTR* BN_API_PTR** outputValues, size_t* count); + void AllocApiStringPairList(const std::map& stringPairList, char BN_API_PTR* BN_API_PTR** outputKeys, char BN_API_PTR* BN_API_PTR** outputValues, size_t* count); + void AllocApiStringPairList(const std::unordered_map& stringPairList, char BN_API_PTR* BN_API_PTR** outputKeys, char BN_API_PTR* BN_API_PTR** outputValues, size_t* count); + + std::string ParseString(const char* string); + std::vector ParseStringList(const char* const* stringList, size_t count); + std::set ParseStringSet(const char* const* stringList, size_t count); + std::unordered_set ParseStringUnorderedSet(const char* const* stringList, size_t count); + + std::vector> ParseStringPairList(const char* const* keys, const char* const* values, size_t count); + std::map ParseStringMap(const char* const* keys, const char* const* values, size_t count); + std::unordered_map ParseStringUnorderedMap(const char* const* keys, const char* const* values, size_t count); + + void FreeApiString(char BN_API_PTR* string); + void FreeApiStringList(char BN_API_PTR* BN_API_PTR* stringList, size_t count); + void FreeApiStringPairList(char BN_API_PTR* BN_API_PTR* keys, char BN_API_PTR* BN_API_PTR* values, size_t count); + + void FreeCoreString(char BN_CORE_PTR* string); + void FreeCoreStringList(char BN_CORE_PTR* BN_CORE_PTR* stringList, size_t count); + void FreeCoreStringPairList(char BN_CORE_PTR* BN_CORE_PTR* keys, char BN_CORE_PTR* BN_CORE_PTR* values, size_t count); + + //endregion + + //------------------------------------------------------------------------------------ + //region Try/Catch Helpers + + // Forward declare this, so we don't have to depend on binaryninjaapi.h + void LogError(const char*, ...); + + /*! + Wrap a throwable block in a try/catch, passing through the return value on success, and + calling a catch handler and passing through its return value on an exception + \tparam T Return type + \tparam F Throwable block + \tparam C Catch handler + \param func Throwable block to execute + \param catcher Catch handler to execute if `func` throws + \return Either the func's result or the handler's result + */ + template + T WrapThrowable(F&& func, C&& catcher) + { + try + { + return func(); + } + catch (...) + { + if constexpr (std::is_invocable::value) + { + return catcher(std::current_exception()); + } + else + { + return catcher(); + } + } + } + + /*! + Wrap a throwable block in a try/catch, passing through the return value on success. + Specialized for pointers, where nullptr will be returned if an exception is thrown. + \tparam T Return type + \tparam F Throwable block + \param func Throwable block to execute + \return Either the func's result or nullptr + */ + template + T WrapThrowablePointer(F&& func, typename std::enable_if::value, int>::type _ = 0) + { + try + { + return func(); + } + catch (const std::exception& e) + { + // TODO: How to handle this? + // g_lastExceptionMessage = e.what(); + LogError("%s", e.what()); + return nullptr; + } + catch (...) + { + return nullptr; + } + } + + /*! + Wrap a throwable block in a try/catch, passing through the return value on success. + Specialized for bool and void functions, returning false if the function throws, or + - if the function returns a bool, passing through that value. + - if the function returns void, returning true on completion. + \tparam F Throwable block + \param func Throwable block to execute + \return Bool as described above + */ + template + bool WrapThrowableBool(F&& func) + { + try + { + if constexpr (std::is_same::type, bool>::value) + { + return func(); + } + else + { + func(); + return true; + } + } + catch (const std::exception& e) + { + // TODO: How to handle this? + // g_lastExceptionMessage = e.what(); + LogError("%s", e.what()); + return false; + } + catch (...) + { + return false; + } + } + //endregion +} \ No newline at end of file -- cgit v1.3.1