Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/GhidraClient.cpp
3185 views
1
#include "Common/Data/Format/JSONReader.h"
2
#include "Common/Net/HTTPClient.h"
3
#include "Common/Thread/ThreadUtil.h"
4
5
#include "Common/GhidraClient.h"
6
7
using namespace json;
8
9
static GhidraTypeKind ResolveTypeKind(const std::string &kind) {
10
if (kind == "ENUM") return ENUM;
11
if (kind == "TYPEDEF") return TYPEDEF;
12
if (kind == "POINTER") return POINTER;
13
if (kind == "ARRAY") return ARRAY;
14
if (kind == "STRUCTURE") return STRUCTURE;
15
if (kind == "UNION") return UNION;
16
if (kind == "FUNCTION_DEFINITION") return FUNCTION_DEFINITION;
17
if (kind == "BUILT_IN") return BUILT_IN;
18
return UNKNOWN;
19
}
20
21
static bool IsBitfieldEnum(const std::vector<GhidraEnumMember> &enumMembers) {
22
u64 previousValues = 0;
23
for (const auto &member : enumMembers) {
24
if (previousValues & member.value) {
25
return false;
26
}
27
previousValues |= member.value;
28
}
29
return true;
30
}
31
32
GhidraClient::~GhidraClient() {
33
if (thread_.joinable()) {
34
thread_.join();
35
}
36
}
37
38
void GhidraClient::FetchAll(const std::string &host, const int port) {
39
std::lock_guard<std::mutex> lock(mutex_);
40
if (status_ != Status::Idle) {
41
return;
42
}
43
status_ = Status::Pending;
44
thread_ = std::thread([this, host, port] {
45
SetCurrentThreadName("GhidraClient");
46
FetchAllDo(host, port);
47
});
48
}
49
50
bool GhidraClient::FetchAllDo(const std::string &host, const int port) {
51
std::lock_guard<std::mutex> lock(mutex_);
52
host_ = host;
53
port_ = port;
54
const bool result = FetchTypes() && FetchSymbols();
55
status_ = Status::Ready;
56
return result;
57
}
58
59
void GhidraClient::UpdateResult() {
60
std::lock_guard<std::mutex> lock(mutex_);
61
if (status_ != Status::Ready) {
62
return;
63
}
64
if (thread_.joinable()) {
65
thread_.join();
66
}
67
result = std::move(pendingResult_);
68
pendingResult_ = Result();
69
status_ = Status::Idle;
70
}
71
72
bool GhidraClient::FetchSymbols() {
73
std::string json;
74
if (!FetchResource("/v1/symbols", json)) {
75
return false;
76
}
77
JsonReader reader(json.c_str(), json.size());
78
if (!reader.ok()) {
79
pendingResult_.error = "symbols parsing error";
80
return false;
81
}
82
const JsonValue entries = reader.root().getArray("symbols")->value;
83
if (entries.getTag() != JSON_ARRAY) {
84
pendingResult_.error = "symbols is not an array";
85
return false;
86
}
87
88
for (const auto pEntry : entries) {
89
JsonGet entry = pEntry->value;
90
91
GhidraSymbol symbol;
92
symbol.address = entry.getInt("address", 0);
93
symbol.name = entry.getStringOr("name", "");
94
symbol.label = strcmp(entry.getStringOr("type", ""), "Label") == 0;
95
symbol.userDefined = strcmp(entry.getStringOr("source", ""), "USER_DEFINED") == 0;
96
symbol.dataTypePathName = entry.getStringOr("dataTypePathName", "");
97
pendingResult_.symbols.emplace_back(symbol);
98
}
99
return true;
100
}
101
102
bool GhidraClient::FetchTypes() {
103
std::string json;
104
if (!FetchResource("/v1/types", json)) {
105
return false;
106
}
107
JsonReader reader(json.c_str(), json.size());
108
if (!reader.ok()) {
109
pendingResult_.error = "types parsing error";
110
return false;
111
}
112
const JsonValue entries = reader.root().getArray("types")->value;
113
if (entries.getTag() != JSON_ARRAY) {
114
pendingResult_.error = "types is not an array";
115
return false;
116
}
117
118
for (const auto pEntry : entries) {
119
const JsonGet entry = pEntry->value;
120
121
GhidraType type;
122
type.displayName = entry.getStringOr("displayName", "");
123
type.pathName = entry.getStringOr("pathName", "");
124
type.length = entry.getInt("length", 0);
125
type.alignedLength = entry.getInt("alignedLength", 0);
126
type.zeroLength = entry.getBool("zeroLength", false);
127
type.description = entry.getStringOr("description", "");
128
type.kind = ResolveTypeKind(entry.getStringOr("kind", ""));
129
130
switch (type.kind) {
131
case ENUM: {
132
const JsonNode *enumEntries = entry.getArray("members");
133
if (!enumEntries) {
134
pendingResult_.error = "missing enum members";
135
return false;
136
}
137
for (const JsonNode *pEnumEntry : enumEntries->value) {
138
JsonGet enumEntry = pEnumEntry->value;
139
GhidraEnumMember member;
140
member.name = enumEntry.getStringOr("name", "");
141
member.value = enumEntry.getInt("value", 0);
142
member.comment = enumEntry.getStringOr("comment", "");
143
type.enumMembers.push_back(member);
144
}
145
type.enumBitfield = IsBitfieldEnum(type.enumMembers);
146
break;
147
}
148
case TYPEDEF:
149
type.typedefTypePathName = entry.getStringOr("typePathName", "");
150
type.typedefBaseTypePathName = entry.getStringOr("baseTypePathName", "");
151
break;
152
case POINTER:
153
type.pointerTypePathName = entry.getStringOr("typePathName", "");
154
break;
155
case ARRAY:
156
type.arrayTypePathName = entry.getStringOr("typePathName", "");
157
type.arrayElementLength = entry.getInt("elementLength", 0);
158
type.arrayElementCount = entry.getInt("elementCount", 0);
159
break;
160
case STRUCTURE:
161
case UNION: {
162
const JsonNode *compositeEntries = entry.getArray("members");
163
if (!compositeEntries) {
164
pendingResult_.error = "missing composite members";
165
return false;
166
}
167
for (const JsonNode *pCompositeEntry : compositeEntries->value) {
168
JsonGet compositeEntry = pCompositeEntry->value;
169
GhidraCompositeMember member;
170
member.fieldName = compositeEntry.getStringOr("fieldName", "");
171
member.ordinal = compositeEntry.getInt("ordinal", 0);
172
member.offset = compositeEntry.getInt("offset", 0);
173
member.length = compositeEntry.getInt("length", 0);
174
member.typePathName = compositeEntry.getStringOr("typePathName", "");
175
member.comment = compositeEntry.getStringOr("comment", "");
176
type.compositeMembers.push_back(member);
177
}
178
break;
179
}
180
case FUNCTION_DEFINITION:
181
type.functionPrototypeString = entry.getStringOr("prototypeString", "");
182
break;
183
case BUILT_IN:
184
type.builtInGroup = entry.getStringOr("group", "");
185
break;
186
default:
187
continue;
188
}
189
190
pendingResult_.types.emplace(type.pathName, type);
191
}
192
return true;
193
}
194
195
bool GhidraClient::FetchResource(const std::string &path, std::string &outResult) {
196
http::Client http;
197
if (!http.Resolve(host_.c_str(), port_)) {
198
pendingResult_.error = "can't resolve host";
199
return false;
200
}
201
bool cancelled = false;
202
if (!http.Connect(1, 5.0, &cancelled)) {
203
pendingResult_.error = "can't connect to host";
204
return false;
205
}
206
net::RequestProgress progress(&cancelled);
207
Buffer result;
208
const int code = http.GET(http::RequestParams(path.c_str()), &result, &progress);
209
http.Disconnect();
210
if (code != 200) {
211
pendingResult_.error = "unsuccessful response code from API endpoint";
212
return false;
213
}
214
result.TakeAll(&outResult);
215
return true;
216
}
217
218