#pragma once #include #include #include #include #include #include /** * Represents symbol from a Ghidra's program. * A symbol can be for example a data label, instruction label or a function. */ struct GhidraSymbol { u32 address = 0; std::string name; bool label; bool userDefined; std::string dataTypePathName; }; /** Possible kinds of data types, such as enum, structures or built-ins (Ghidra's primitive types). */ enum GhidraTypeKind { ENUM, TYPEDEF, POINTER, ARRAY, STRUCTURE, UNION, FUNCTION_DEFINITION, BUILT_IN, UNKNOWN, }; /** Describes single member of an enum type. */ struct GhidraEnumMember { std::string name; u64 value = 0; std::string comment; }; /** Describes single member of a composite (structure or union) type. */ struct GhidraCompositeMember { std::string fieldName; u32 ordinal = 0; u32 offset = 0; int length = 0; std::string typePathName; std::string comment; }; /** * Describes data type from Ghidra. Note that some fields of this structure will only be populated depending on the * type's kind. Each type has a display name that is suitable for displaying to the user and a path name that * unambiguously identifies this type. */ struct GhidraType { GhidraTypeKind kind; std::string displayName; std::string pathName; int length = 0; int alignedLength = 0; bool zeroLength = false; std::string description; std::vector compositeMembers; std::vector enumMembers; std::string pointerTypePathName; std::string typedefTypePathName; std::string typedefBaseTypePathName; std::string arrayTypePathName; int arrayElementLength = 0; u32 arrayElementCount = 0; std::string functionPrototypeString; std::string builtInGroup; }; /** * GhidraClient implements fetching data (such as symbols or types) from a remote Ghidra project. * * This client uses unofficial API provided by the third party "ghidra-rest-api" extension. The extension is * available at https://github.com/kotcrab/ghidra-rest-api. * * This class doesn't fetch data from every possible endpoint, only those that are actually used by PPSSPP. * * How to use: * 1. The client is created in the Idle status. * 2. To start fetching data call the FetchAll() method. The client goes to Pending status and the data is fetched * in a background thread so your code remains unblocked. * 3. Periodically check with the Ready() method is the operation has completed. (i.e. check if the client is in the Ready status) * 4. If the client is ready call UpdateResult() to update result field with new data. * 5. The client is now back to Idle status, and you can call FetchAll() again later if needed. */ class GhidraClient { enum class Status { Idle, Pending, Ready, }; public: struct Result { std::vector symbols; std::unordered_map types; std::string error; }; /** Current result of the client. Your thread is safe to access this regardless of client status. */ Result result; ~GhidraClient(); /** If client is idle then asynchronously starts fetching data from Ghidra. */ void FetchAll(const std::string& host, int port); /** If client is ready then updates the result field with newly fetched data. This must be called from the thread * using the result. */ void UpdateResult(); bool Idle() const { return status_ == Status::Idle; } bool Ready() const { return status_ == Status::Ready; } bool Failed() const { return !result.error.empty(); } private: std::thread thread_; std::mutex mutex_; std::atomic status_; Result pendingResult_; std::string host_; int port_ = 0; bool FetchAllDo(const std::string& host, int port); bool FetchSymbols(); bool FetchTypes(); bool FetchResource(const std::string& path, std::string& outResult); };