mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 05:19:56 +00:00
Add GhidraClient
This commit is contained in:
parent
2402eea4b1
commit
fdf8ff7d94
@ -884,6 +884,8 @@ add_library(Common STATIC
|
||||
Common/FakeCPUDetect.cpp
|
||||
Common/ExceptionHandlerSetup.cpp
|
||||
Common/ExceptionHandlerSetup.h
|
||||
Common/GhidraClient.h
|
||||
Common/GhidraClient.cpp
|
||||
Common/Log.h
|
||||
Common/Log.cpp
|
||||
Common/Log/ConsoleListener.cpp
|
||||
|
@ -591,6 +591,7 @@
|
||||
<ClInclude Include="Crypto\sha256.h" />
|
||||
<ClInclude Include="DbgNew.h" />
|
||||
<ClInclude Include="ExceptionHandlerSetup.h" />
|
||||
<ClInclude Include="GhidraClient.h" />
|
||||
<ClInclude Include="GraphicsContext.h" />
|
||||
<ClInclude Include="Log.h" />
|
||||
<ClInclude Include="Log\LogManager.h" />
|
||||
@ -1021,6 +1022,7 @@
|
||||
<ClCompile Include="GPU\Vulkan\VulkanRenderManager.cpp" />
|
||||
<ClCompile Include="Input\GestureDetector.cpp" />
|
||||
<ClCompile Include="Input\InputState.cpp" />
|
||||
<ClCompile Include="GhidraClient.cpp" />
|
||||
<ClCompile Include="Log.cpp" />
|
||||
<ClCompile Include="Math\curves.cpp" />
|
||||
<ClCompile Include="Math\expression_parser.cpp" />
|
||||
|
@ -6,6 +6,7 @@
|
||||
<ClInclude Include="CommonFuncs.h" />
|
||||
<ClInclude Include="CommonTypes.h" />
|
||||
<ClInclude Include="CPUDetect.h" />
|
||||
<ClInclude Include="GhidraClient.h" />
|
||||
<ClInclude Include="Log.h" />
|
||||
<ClInclude Include="MemArena.h" />
|
||||
<ClInclude Include="MemoryUtil.h" />
|
||||
@ -705,6 +706,7 @@
|
||||
<Filter>Serialize</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TimeUtil.cpp" />
|
||||
<ClCompile Include="GhidraClient.cpp" />
|
||||
<ClCompile Include="Log.cpp" />
|
||||
<ClCompile Include="SysError.cpp" />
|
||||
<ClCompile Include="..\ext\libpng17\png.c">
|
||||
|
205
Common/GhidraClient.cpp
Normal file
205
Common/GhidraClient.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
#include "Common/Data/Format/JSONReader.h"
|
||||
#include "Common/Net/HTTPClient.h"
|
||||
#include "Common/Thread/ThreadUtil.h"
|
||||
|
||||
#include "Common/GhidraClient.h"
|
||||
|
||||
using namespace json;
|
||||
|
||||
static GhidraTypeKind ResolveTypeKind(const std::string& kind) {
|
||||
if (kind == "ENUM") return ENUM;
|
||||
if (kind == "TYPEDEF") return TYPEDEF;
|
||||
if (kind == "POINTER") return POINTER;
|
||||
if (kind == "ARRAY") return ARRAY;
|
||||
if (kind == "STRUCTURE") return STRUCTURE;
|
||||
if (kind == "UNION") return UNION;
|
||||
if (kind == "FUNCTION_DEFINITION") return FUNCTION_DEFINITION;
|
||||
if (kind == "BUILT_IN") return BUILT_IN;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
GhidraClient::~GhidraClient() {
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void GhidraClient::FetchAll(const std::string& host, const int port) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (status_ != Status::Idle) {
|
||||
return;
|
||||
}
|
||||
status_ = Status::Pending;
|
||||
thread_ = std::thread([this, host, port] {
|
||||
SetCurrentThreadName("GhidraClient");
|
||||
FetchAllDo(host, port);
|
||||
});
|
||||
}
|
||||
|
||||
bool GhidraClient::FetchAllDo(const std::string& host, const int port) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
host_ = host;
|
||||
port_ = port;
|
||||
const bool result = FetchTypes() && FetchSymbols();
|
||||
status_ = Status::Ready;
|
||||
return result;
|
||||
}
|
||||
|
||||
void GhidraClient::UpdateResult() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (status_ != Status::Ready) {
|
||||
return;
|
||||
}
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
result = std::move(pendingResult_);
|
||||
pendingResult_ = Result();
|
||||
status_ = Status::Idle;
|
||||
}
|
||||
|
||||
bool GhidraClient::FetchSymbols() {
|
||||
std::string json;
|
||||
if (!FetchResource("/v1/symbols", json)) {
|
||||
return false;
|
||||
}
|
||||
JsonReader reader(json.c_str(), json.size());
|
||||
if (!reader.ok()) {
|
||||
pendingResult_.error = "symbols parsing error";
|
||||
return false;
|
||||
}
|
||||
const JsonValue entries = reader.root().getArray("symbols")->value;
|
||||
if (entries.getTag() != JSON_ARRAY) {
|
||||
pendingResult_.error = "symbols is not an array";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto pEntry: entries) {
|
||||
JsonGet entry = pEntry->value;
|
||||
|
||||
GhidraSymbol symbol;
|
||||
symbol.address = entry.getInt("address", 0);
|
||||
symbol.name = entry.getStringOr("name", "");
|
||||
symbol.label = strcmp(entry.getStringOr("type", ""), "Label") == 0;
|
||||
symbol.userDefined = strcmp(entry.getStringOr("source", ""), "USER_DEFINED") == 0;
|
||||
symbol.dataTypePathName = entry.getStringOr("dataTypePathName", "");
|
||||
pendingResult_.symbols.emplace_back(symbol);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GhidraClient::FetchTypes() {
|
||||
std::string json;
|
||||
if (!FetchResource("/v1/types", json)) {
|
||||
return false;
|
||||
}
|
||||
JsonReader reader(json.c_str(), json.size());
|
||||
if (!reader.ok()) {
|
||||
pendingResult_.error = "types parsing error";
|
||||
return false;
|
||||
}
|
||||
const JsonValue entries = reader.root().getArray("types")->value;
|
||||
if (entries.getTag() != JSON_ARRAY) {
|
||||
pendingResult_.error = "types is not an array";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto pEntry: entries) {
|
||||
const JsonGet entry = pEntry->value;
|
||||
|
||||
GhidraType type;
|
||||
type.name = entry.getStringOr("name", "");
|
||||
type.displayName = entry.getStringOr("displayName", "");
|
||||
type.pathName = entry.getStringOr("pathName", "");
|
||||
type.length = entry.getInt("length", 0);
|
||||
type.alignedLength = entry.getInt("alignedLength", 0);
|
||||
type.zeroLength = entry.getBool("zeroLength", false);
|
||||
type.description = entry.getStringOr("description", "");
|
||||
type.kind = ResolveTypeKind(entry.getStringOr("kind", ""));
|
||||
|
||||
switch (type.kind) {
|
||||
case ENUM: {
|
||||
const JsonNode* enumEntries = entry.getArray("members");
|
||||
if (!enumEntries) {
|
||||
pendingResult_.error = "missing enum members";
|
||||
return false;
|
||||
}
|
||||
for (const JsonNode* pEnumEntry: enumEntries->value) {
|
||||
JsonGet enumEntry = pEnumEntry->value;
|
||||
GhidraEnumMember member;
|
||||
member.name = enumEntry.getStringOr("name", "");
|
||||
member.value = enumEntry.getInt("value", 0);
|
||||
member.comment = enumEntry.getStringOr("comment", "");
|
||||
type.enumMembers.push_back(member);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPEDEF:
|
||||
type.typedefTypePathName = entry.getStringOr("typePathName", "");
|
||||
type.typedefBaseTypePathName = entry.getStringOr("baseTypePathName", "");
|
||||
break;
|
||||
case POINTER:
|
||||
type.pointerTypePathName = entry.getStringOr("typePathName", "");
|
||||
break;
|
||||
case ARRAY:
|
||||
type.arrayTypePathName = entry.getStringOr("typePathName", "");
|
||||
type.arrayElementLength = entry.getInt("elementLength", 0);
|
||||
type.arrayElementCount = entry.getInt("elementCount", 0);
|
||||
break;
|
||||
case STRUCTURE:
|
||||
case UNION: {
|
||||
const JsonNode* compositeEntries = entry.getArray("members");
|
||||
if (!compositeEntries) {
|
||||
pendingResult_.error = "missing composite members";
|
||||
return false;
|
||||
}
|
||||
for (const JsonNode* pCompositeEntry: compositeEntries->value) {
|
||||
JsonGet compositeEntry = pCompositeEntry->value;
|
||||
GhidraCompositeMember member;
|
||||
member.fieldName = compositeEntry.getStringOr("fieldName", "");
|
||||
member.ordinal = compositeEntry.getInt("ordinal", 0);
|
||||
member.offset = compositeEntry.getInt("offset", 0);
|
||||
member.length = compositeEntry.getInt("length", 0);
|
||||
member.typePathName = compositeEntry.getStringOr("typePathName", "");
|
||||
member.comment = compositeEntry.getStringOr("comment", "");
|
||||
type.compositeMembers.push_back(member);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FUNCTION_DEFINITION:
|
||||
type.functionPrototypeString = entry.getStringOr("prototypeString", "");
|
||||
break;
|
||||
case BUILT_IN:
|
||||
type.builtInGroup = entry.getStringOr("group", "");
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
pendingResult_.types.emplace(type.pathName, type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GhidraClient::FetchResource(const std::string& path, std::string& outResult) {
|
||||
http::Client http;
|
||||
if (!http.Resolve(host_.c_str(), port_)) {
|
||||
pendingResult_.error = "can't resolve host";
|
||||
return false;
|
||||
}
|
||||
bool cancelled = false;
|
||||
if (!http.Connect(1, 5.0, &cancelled)) {
|
||||
pendingResult_.error = "can't connect to host";
|
||||
return false;
|
||||
}
|
||||
net::RequestProgress progress(&cancelled);
|
||||
Buffer result;
|
||||
const int code = http.GET(http::RequestParams(path.c_str()), &result, &progress);
|
||||
http.Disconnect();
|
||||
if (code != 200) {
|
||||
pendingResult_.error = "unsuccessful response code";
|
||||
return false;
|
||||
}
|
||||
result.TakeAll(&outResult);
|
||||
return true;
|
||||
}
|
108
Common/GhidraClient.h
Normal file
108
Common/GhidraClient.h
Normal file
@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
struct GhidraSymbol {
|
||||
u32 address = 0;
|
||||
std::string name;
|
||||
bool label;
|
||||
bool userDefined;
|
||||
std::string dataTypePathName;
|
||||
};
|
||||
|
||||
enum GhidraTypeKind {
|
||||
ENUM,
|
||||
TYPEDEF,
|
||||
POINTER,
|
||||
ARRAY,
|
||||
STRUCTURE,
|
||||
UNION,
|
||||
FUNCTION_DEFINITION,
|
||||
BUILT_IN,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
struct GhidraEnumMember {
|
||||
std::string name;
|
||||
u64 value = 0;
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
struct GhidraCompositeMember {
|
||||
std::string fieldName;
|
||||
u32 ordinal = 0;
|
||||
u32 offset = 0;
|
||||
int length = 0;
|
||||
std::string typePathName;
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
struct GhidraType {
|
||||
GhidraTypeKind kind;
|
||||
std::string name;
|
||||
std::string displayName;
|
||||
std::string pathName;
|
||||
int length = 0;
|
||||
int alignedLength = 0;
|
||||
bool zeroLength = false;
|
||||
std::string description;
|
||||
|
||||
std::vector<GhidraCompositeMember> compositeMembers;
|
||||
std::vector<GhidraEnumMember> 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;
|
||||
};
|
||||
|
||||
class GhidraClient {
|
||||
public:
|
||||
enum class Status {
|
||||
Idle,
|
||||
Pending,
|
||||
Ready,
|
||||
};
|
||||
|
||||
struct Result {
|
||||
std::vector<GhidraSymbol> symbols;
|
||||
std::unordered_map<std::string, GhidraType> types;
|
||||
std::string error;
|
||||
};
|
||||
|
||||
Result result;
|
||||
|
||||
~GhidraClient();
|
||||
|
||||
void FetchAll(const std::string& host, int port);
|
||||
|
||||
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> 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);
|
||||
};
|
@ -197,6 +197,7 @@
|
||||
<ClInclude Include="..\..\Common\Crypto\sha256.h" />
|
||||
<ClInclude Include="..\..\Common\DbgNew.h" />
|
||||
<ClInclude Include="..\..\Common\ExceptionHandlerSetup.h" />
|
||||
<ClInclude Include="..\..\Common\GhidraClient.h" />
|
||||
<ClInclude Include="..\..\Common\GraphicsContext.h" />
|
||||
<ClInclude Include="..\..\Common\Log.h" />
|
||||
<ClInclude Include="..\..\Common\Log\LogManager.h" />
|
||||
@ -360,6 +361,7 @@
|
||||
<ClCompile Include="..\..\Common\Crypto\sha1.cpp" />
|
||||
<ClCompile Include="..\..\Common\Crypto\sha256.cpp" />
|
||||
<ClCompile Include="..\..\Common\ExceptionHandlerSetup.cpp" />
|
||||
<ClCompile Include="..\..\Common\GhidraClient.cpp" />
|
||||
<ClCompile Include="..\..\Common\Log.cpp" />
|
||||
<ClCompile Include="..\..\Common\Log\LogManager.cpp" />
|
||||
<ClCompile Include="..\..\Common\LogReporting.cpp" />
|
||||
|
@ -119,6 +119,7 @@
|
||||
<ClCompile Include="..\..\Common\CPUDetect.cpp" />
|
||||
<ClCompile Include="..\..\Common\FakeCPUDetect.cpp" />
|
||||
<ClCompile Include="..\..\Common\ExceptionHandlerSetup.cpp" />
|
||||
<ClCompile Include="..\..\Common\GhidraClient.cpp" />
|
||||
<ClCompile Include="..\..\Common\Log.cpp" />
|
||||
<ClCompile Include="..\..\Common\Log\LogManager.cpp" />
|
||||
<ClCompile Include="..\..\Common\LogReporting.cpp" />
|
||||
@ -538,6 +539,7 @@
|
||||
<ClInclude Include="..\..\Common\CPUDetect.h" />
|
||||
<ClInclude Include="..\..\Common\DbgNew.h" />
|
||||
<ClInclude Include="..\..\Common\ExceptionHandlerSetup.h" />
|
||||
<ClInclude Include="..\..\Common\GhidraClient.h" />
|
||||
<ClInclude Include="..\..\Common\GraphicsContext.h" />
|
||||
<ClInclude Include="..\..\Common\Log.h" />
|
||||
<ClInclude Include="..\..\Common\Log\LogManager.h" />
|
||||
|
@ -373,6 +373,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/Common/CPUDetect.cpp \
|
||||
$(SRC)/Common/ExceptionHandlerSetup.cpp \
|
||||
$(SRC)/Common/FakeCPUDetect.cpp \
|
||||
$(SRC)/Common/GhidraClient.cpp \
|
||||
$(SRC)/Common/Log.cpp \
|
||||
$(SRC)/Common/Log/LogManager.cpp \
|
||||
$(SRC)/Common/LogReporting.cpp \
|
||||
|
@ -483,6 +483,7 @@ SOURCES_CXX += \
|
||||
$(COMMONDIR)/Log/StdioListener.cpp \
|
||||
$(COMMONDIR)/ExceptionHandlerSetup.cpp \
|
||||
$(COMMONDIR)/FakeCPUDetect.cpp \
|
||||
$(COMMONDIR)/GhidraClient.cpp \
|
||||
$(COMMONDIR)/Log.cpp \
|
||||
$(COMMONDIR)/Log/LogManager.cpp \
|
||||
$(COMMONDIR)/OSVersion.cpp \
|
||||
|
Loading…
Reference in New Issue
Block a user