mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
Add GhidraClient and ImStructViewer docs
Few more code comments and misc clean up
This commit is contained in:
parent
2c49cae1e2
commit
e3e831851b
@ -108,7 +108,6 @@ bool GhidraClient::FetchTypes() {
|
||||
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);
|
||||
@ -197,7 +196,7 @@ bool GhidraClient::FetchResource(const std::string& path, std::string& outResult
|
||||
const int code = http.GET(http::RequestParams(path.c_str()), &result, &progress);
|
||||
http.Disconnect();
|
||||
if (code != 200) {
|
||||
pendingResult_.error = "unsuccessful response code";
|
||||
pendingResult_.error = "unsuccessful response code from API endpoint";
|
||||
return false;
|
||||
}
|
||||
result.TakeAll(&outResult);
|
||||
|
@ -7,6 +7,10 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@ -15,6 +19,7 @@ struct GhidraSymbol {
|
||||
std::string dataTypePathName;
|
||||
};
|
||||
|
||||
/** Possible kinds of data types, such as enum, structures or built-ins (Ghidra's primitive types). */
|
||||
enum GhidraTypeKind {
|
||||
ENUM,
|
||||
TYPEDEF,
|
||||
@ -27,12 +32,14 @@ enum GhidraTypeKind {
|
||||
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;
|
||||
@ -42,9 +49,13 @@ struct GhidraCompositeMember {
|
||||
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 name;
|
||||
std::string displayName;
|
||||
std::string pathName;
|
||||
int length = 0;
|
||||
@ -64,6 +75,23 @@ struct GhidraType {
|
||||
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,
|
||||
@ -78,12 +106,16 @@ public:
|
||||
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; }
|
||||
|
@ -216,9 +216,15 @@ void ImStructViewer::Draw(MIPSDebugInterface* mipsDebug, bool* open) {
|
||||
}
|
||||
|
||||
void ImStructViewer::DrawConnectionSetup() {
|
||||
ImGui::TextWrapped("Struct viewer visualizes data in memory using types from your Ghidra project.");
|
||||
ImGui::TextWrapped("To get started install the ghidra-rest-api plugin and start the Rest API server.");
|
||||
ImGui::TextWrapped("When ready press the connect button below.");
|
||||
ImGui::TextWrapped(R"(Struct viewer visualizes data in game memory using types from your Ghidra project.
|
||||
It also allows to set memory breakpoints and edit field values which is helpful when reverse engineering unknown types.
|
||||
To get started:
|
||||
1. In Ghidra install the ghidra-rest-api extension by Kotcrab.
|
||||
2. After installing the extension enable the RestApiPlugin in the Miscellaneous plugins configuration window.
|
||||
3. Open your Ghidra project and click "Start Rest API Server" in the "Tools" menu bar.
|
||||
4. Press the connect button below.
|
||||
)");
|
||||
ImGui::Dummy(ImVec2(1, 6));
|
||||
|
||||
ImGui::BeginDisabled(!ghidraClient_.Idle());
|
||||
ImGui::PushItemWidth(120);
|
||||
@ -552,7 +558,7 @@ void ImStructViewer::DrawType(
|
||||
|
||||
const u32 address = base + offset;
|
||||
ImGui::PushID(static_cast<int>(address));
|
||||
ImGui::PushID(watchIndex);
|
||||
ImGui::PushID(watchIndex); // We push watch index too as it's possible to have multiple watches on the same address
|
||||
|
||||
// Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing
|
||||
// to make the tree lines equal high.
|
||||
@ -711,7 +717,7 @@ void ImStructViewer::DrawType(
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// At this point there is most likely some issue in the Ghidra plugin and the type wasn't
|
||||
// At this point there is most likely some issue in the Ghidra extension and the type wasn't
|
||||
// classified to any category
|
||||
ImGui::TreeNodeEx("Field", leafFlags, "%s", name);
|
||||
DrawContextMenu(base, offset, type.alignedLength, typePathName, name, watchIndex, nullptr);
|
||||
@ -726,7 +732,7 @@ void ImStructViewer::DrawType(
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
static void CopyHexNumberToClipboard(u64 value) {
|
||||
static void CopyHexNumberToClipboard(const u64 value) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << value;
|
||||
const std::string valueString = ss.str();
|
||||
|
@ -5,6 +5,18 @@
|
||||
#include "Common/GhidraClient.h"
|
||||
#include "Core/MIPS/MIPSDebugInterface.h"
|
||||
|
||||
/**
|
||||
* Struct viewer visualizes objects data in game memory using types and symbols fetched from a Ghidra project.
|
||||
* It also allows to set memory breakpoints and edit field values which is helpful when reverse engineering unknown
|
||||
* types.
|
||||
*
|
||||
* To use this you will need to install an unofficial Ghidra extension "ghidra-rest-api" by Kotcrab.
|
||||
* (available at https://github.com/kotcrab/ghidra-rest-api). After installing the extension and starting the API
|
||||
* server in Ghidra you can open the Struct viewer window and press the "Connect" button to start using it.
|
||||
*
|
||||
* See the original pull request https://github.com/hrydgard/ppsspp/pull/19629 for a screenshot and how to test this
|
||||
* without the need to set up Ghidra.
|
||||
*/
|
||||
class ImStructViewer {
|
||||
struct Watch {
|
||||
std::string expression;
|
||||
@ -35,12 +47,12 @@ private:
|
||||
GhidraClient ghidraClient_;
|
||||
char ghidraHost_[128] = "localhost";
|
||||
int ghidraPort_ = 18489;
|
||||
bool fetchedAtLeastOnce_ = false;
|
||||
bool fetchedAtLeastOnce_ = false; // True if fetched from Ghidra successfully at least once
|
||||
|
||||
std::vector<Watch> watches_;
|
||||
int removeWatchIndex_ = -1;
|
||||
Watch addWatch_;
|
||||
NewWatch newWatch_;
|
||||
int removeWatchIndex_ = -1; // Watch index entry to be removed on next draw
|
||||
Watch addWatch_; // Temporary variable to store watch entry added from the Globals tab
|
||||
NewWatch newWatch_; // State for the new watch entry UI
|
||||
|
||||
void DrawConnectionSetup();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user