#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 Generic API Objects /*! Helper class to determine if a type is "API-able" aka has the following interface: struct Foo { BNFoo GetAPIObject() const; static Foo FromAPIObject(const BNFoo* obj); static void FreeAPIObject(BNFoo* obj); }; If you get weird compiler errors around here, make sure you've implemented the above interface correctly (with the `const`s too!). */ template< typename T, // Grab the type for TAPI from the return type of GetAPIObject() // Store into template argument for easier lookup typename TAPI_ = decltype(std::declval().GetAPIObject()) > // Subtype of bool_constant to allow std::enable_if usage struct APIAble : std::bool_constant< // Make sure T::FromAPIObject(TAPI*) actually works std::is_invocable_v // Make sure T::FromAPIObject(TAPI*) returns T && std::is_same_v()))> // Make sure T::FreeAPIObject(TAPI*) actually works && std::is_invocable_v > { // For reference by users of APIAble typedef TAPI_ TAPI; }; template::value, void>> void AllocAPIObjectList(const std::vector& objects, typename APIAble::TAPI BN_API_PTR** output, size_t* count) { *count = objects.size(); *output = new typename APIAble::TAPI[objects.size()]; size_t i = 0; for (const auto& o: objects) { (*output)[i] = o.GetAPIObject(); i ++; } } template::value, void>> typename APIAble::TAPI BN_API_PTR* AllocAPIObjectList(const std::vector& objects, size_t* count) { typename APIAble::TAPI* result; AllocAPIObjectList(objects, &result, count); return result; } template::value, void>> std::vector ParseAPIObjectList(const typename APIAble::TAPI* objects, size_t count) { std::vector result; result.reserve(count); for (size_t i = 0; i < count; i ++) { result.push_back(T::FromAPIObject(&objects[i])); } return result; } template::value, void>> void FreeAPIObjectList(typename APIAble::TAPI BN_API_PTR* objects, size_t count) { for (size_t i = 0; i < count; i ++) { T::FreeAPIObject(&objects[i]); } delete[] objects; } //endregion //------------------------------------------------------------------------------------ //region Try/Catch Helpers // Forward declare this, so we don't have to depend on binaryninjaapi.h void LogErrorForException(const std::exception& e, 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(); LogErrorForException(e, "%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(); LogErrorForException(e, "%s", e.what()); return false; } catch (...) { return false; } } //endregion }