Expose PSPModule (so the debugger can access it later)

This commit is contained in:
Henrik Rydgård 2025-03-31 09:42:06 +02:00
parent ce87560c95
commit 644f5e4e6c
9 changed files with 342 additions and 315 deletions

View file

@ -66,6 +66,7 @@ typedef struct HWND__ *HWND;
class SymbolMap {
public:
SymbolMap() {}
void Clear();
void SortSymbols();

View file

@ -92,9 +92,7 @@ struct NativeFPL
};
//FPL - Fixed Length Dynamic Memory Pool - every item has the same length
struct FPL : public KernelObject
{
FPL() : blocks(NULL), nextBlock(0) {}
struct FPL : public KernelObject {
~FPL() {
delete [] blocks;
}
@ -148,11 +146,11 @@ struct FPL : public KernelObject
Do(p, pausedWaits);
}
NativeFPL nf;
bool *blocks;
u32 address;
int alignedSize;
int nextBlock;
NativeFPL nf{};
bool *blocks = nullptr;
u32 address = 0;
int alignedSize = 0;
int nextBlock = 0;
std::vector<FplWaitingThread> waitingThreads;
// Key is the callback id it was for, or if no callback, the thread id.
std::map<SceUID, FplWaitingThread> pausedWaits;
@ -379,8 +377,7 @@ struct SceKernelVplHeader {
}
};
struct VPL : public KernelObject
{
struct VPL : public KernelObject {
const char *GetName() override { return nv.name; }
const char *GetTypeName() override { return GetStaticTypeName(); }
static const char *GetStaticTypeName() { return "VPL"; }
@ -410,8 +407,8 @@ struct VPL : public KernelObject
}
}
SceKernelVplInfo nv;
u32 address;
SceKernelVplInfo nv{};
u32 address = 0;
std::vector<VplWaitingThread> waitingThreads;
// Key is the callback id it was for, or if no callback, the thread id.
std::map<SceUID, VplWaitingThread> pausedWaits;

View file

@ -134,93 +134,27 @@ static const char * const blacklistedModules[] = {
"sceMemab",
};
struct WriteVarSymbolState;
struct VarSymbolImport {
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 nid;
u32 stubAddr;
u8 type;
};
struct VarSymbolExport {
bool Matches(const VarSymbolImport &other) const {
return nid == other.nid && !strncmp(moduleName, other.moduleName, KERNELOBJECT_MAX_NAME_LENGTH);
const char *NativeModuleStatusToString(NativeModuleStatus status) {
switch (status) {
case MODULE_STATUS_STARTING: return "STARTING";
case MODULE_STATUS_STARTED: return "STARTED";
case MODULE_STATUS_STOPPING: return "STOPPING";
case MODULE_STATUS_STOPPED: return "STOPPED";
case MODULE_STATUS_UNLOADING: return "UNLOADING";
default: return "(err)";
}
}
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 nid;
u32 symAddr;
};
static void ImportVarSymbol(WriteVarSymbolState &state, const VarSymbolImport &var);
static void ExportVarSymbol(const VarSymbolExport &var);
static void UnexportVarSymbol(const VarSymbolExport &var);
struct FuncSymbolImport {
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 stubAddr;
u32 nid;
};
static void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char *importingModule);
static void ExportFuncSymbol(const FuncSymbolExport &func);
static void UnexportFuncSymbol(const FuncSymbolExport &func);
struct FuncSymbolExport {
bool Matches(const FuncSymbolImport &other) const {
return nid == other.nid && !strncmp(moduleName, other.moduleName, KERNELOBJECT_MAX_NAME_LENGTH);
}
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 symAddr;
u32 nid;
};
void ImportVarSymbol(WriteVarSymbolState &state, const VarSymbolImport &var);
void ExportVarSymbol(const VarSymbolExport &var);
void UnexportVarSymbol(const VarSymbolExport &var);
void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char *importingModule);
void ExportFuncSymbol(const FuncSymbolExport &func);
void UnexportFuncSymbol(const FuncSymbolExport &func);
class PSPModule;
static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, bool reimporting = false);
struct NativeModule {
u32_le next;
u16_le attribute;
u8 version[2];
char name[28];
u32_le status;
u32_le unk1;
u32_le modid; // 0x2C
u32_le usermod_thid;
u32_le memid;
u32_le mpidtext;
u32_le mpiddata;
u32_le ent_top;
u32_le ent_size;
u32_le stub_top;
u32_le stub_size;
u32_le module_start_func;
u32_le module_stop_func;
u32_le module_bootstart_func;
u32_le module_reboot_before_func;
u32_le module_reboot_phase_func;
u32_le entry_addr;
u32_le gp_value;
u32_le text_addr;
u32_le text_size;
u32_le data_size;
u32_le bss_size;
u32_le nsegment;
u32_le segmentaddr[4];
u32_le segmentsize[4];
u32_le module_start_thread_priority;
u32_le module_start_thread_stacksize;
u32_le module_start_thread_attr;
u32_le module_stop_thread_priority;
u32_le module_stop_thread_stacksize;
u32_le module_stop_thread_attr;
u32_le module_reboot_before_thread_priority;
u32_le module_reboot_before_thread_stacksize;
u32_le module_reboot_before_thread_attr;
};
// by QueryModuleInfo
struct ModuleInfo {
SceSize_le size;
@ -238,26 +172,7 @@ struct ModuleInfo {
char name[28];
};
struct ModuleWaitingThread {
SceUID threadID;
u32 statusPtr;
};
enum NativeModuleStatus {
MODULE_STATUS_STARTING = 4,
MODULE_STATUS_STARTED = 5,
MODULE_STATUS_STOPPING = 6,
MODULE_STATUS_STOPPED = 7,
MODULE_STATUS_UNLOADING = 8,
};
class PSPModule : public KernelObject {
public:
PSPModule() {
modulePtr.ptr = 0;
}
~PSPModule() {
PSPModule::~PSPModule() {
if (memoryBlockAddr) {
// If it's either below user memory, or using a high kernel bit, it's in kernel.
if (memoryBlockAddr < PSP_GetUserMemoryBase() || memoryBlockAddr > PSP_GetUserMemoryEnd()) {
@ -272,32 +187,13 @@ public:
//Only alloc at kernel memory.
kernelMemory.Free(modulePtr.ptr);
}
}
const char *GetName() override { return nm.name; }
const char *GetTypeName() override { return GetStaticTypeName(); }
static const char *GetStaticTypeName() { return "Module"; }
void GetQuickInfo(char *ptr, int size) override {
snprintf(ptr, size, "%d.%d %sname=%s gp=%08x entry=%08x",
nm.version[1], nm.version[0],
isFake ? "(faked) " : "",
nm.name,
nm.gp_value,
nm.entry_addr);
}
void GetLongInfo(char *ptr, int bufSize) const override;
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MODULE; }
static int GetStaticIDType() { return PPSSPP_KERNEL_TMID_Module; }
int GetIDType() const override { return PPSSPP_KERNEL_TMID_Module; }
}
u32 GetDataAddr() const {
return nm.text_addr + nm.text_size;
}
u32 GetBSSAddr() const {
return nm.text_addr + nm.text_size + nm.data_size;
}
u32 PSPModule::GetMissingErrorCode() {
return SCE_KERNEL_ERROR_UNKNOWN_MODULE;
}
void DoState(PointerWrap &p) override
{
void PSPModule::DoState(PointerWrap &p) {
auto s = p.Section("Module", 1, 6);
if (!s)
return;
@ -343,15 +239,15 @@ public:
Do(p, modulePtr.ptr);
}
ModuleWaitingThread mwt = {0};
ModuleWaitingThread mwt = { 0 };
Do(p, waitingThreads, mwt);
FuncSymbolExport fsx = {{0}};
FuncSymbolExport fsx = { {0} };
Do(p, exportedFuncs, fsx);
FuncSymbolImport fsi = {{0}};
FuncSymbolImport fsi = { {0} };
Do(p, importedFuncs, fsi);
VarSymbolExport vsx = {{0}};
VarSymbolExport vsx = { {0} };
Do(p, exportedVars, vsx);
VarSymbolImport vsi = {{0}};
VarSymbolImport vsi = { {0} };
Do(p, importedVars, vsi);
if (p.mode == p.MODE_READ) {
@ -377,7 +273,7 @@ public:
}
}
char moduleName[29] = {0};
char moduleName[29] = { 0 };
truncate_cpy(moduleName, nm.name);
if (memoryBlockAddr != 0) {
g_symbolMap->AddModule(moduleName, memoryBlockAddr, memoryBlockSize);
@ -387,12 +283,9 @@ public:
HLEPlugins::DoState(p);
RebuildImpExpModuleNames();
}
}
// We don't do this in the destructor to avoid annoying messages on game shutdown.
void Cleanup();
void ImportFunc(const FuncSymbolImport &func, bool reimporting) {
void PSPModule::ImportFunc(const FuncSymbolImport &func, bool reimporting) {
if (!Memory::IsValidAddress(func.stubAddr)) {
WARN_LOG_REPORT(Log::Loader, "Invalid address for syscall stub %s %08x", func.moduleName, func.nid);
return;
@ -403,81 +296,47 @@ public:
// Add the symbol to the symbol map for debugging.
char temp[256];
snprintf(temp, sizeof(temp), "zz_%s", GetFuncName(func.moduleName, func.nid));
g_symbolMap->AddFunction(temp,func.stubAddr,8);
g_symbolMap->AddFunction(temp, func.stubAddr, 8);
// Keep track and actually hook it up if possible.
importedFuncs.push_back(func);
impExpModuleNames.insert(func.moduleName);
ImportFuncSymbol(func, reimporting, GetName());
}
}
void ImportVar(WriteVarSymbolState &state, const VarSymbolImport &var) {
void PSPModule::ImportVar(WriteVarSymbolState &state, const VarSymbolImport &var) {
// Keep track and actually hook it up if possible.
importedVars.push_back(var);
impExpModuleNames.insert(var.moduleName);
ImportVarSymbol(state, var);
}
}
void ExportFunc(const FuncSymbolExport &func) {
void PSPModule::ExportFunc(const FuncSymbolExport &func) {
if (isFake) {
return;
}
exportedFuncs.push_back(func);
impExpModuleNames.insert(func.moduleName);
ExportFuncSymbol(func);
}
}
void ExportVar(const VarSymbolExport &var) {
void PSPModule::ExportVar(const VarSymbolExport &var) {
if (isFake) {
return;
}
exportedVars.push_back(var);
impExpModuleNames.insert(var.moduleName);
ExportVarSymbol(var);
}
}
template <typename T>
void RebuildImpExpList(const std::vector<T> &list) {
for (size_t i = 0; i < list.size(); ++i) {
impExpModuleNames.insert(list[i].moduleName);
}
}
void RebuildImpExpModuleNames() {
impExpModuleNames.clear();
RebuildImpExpList(exportedFuncs);
RebuildImpExpList(importedFuncs);
RebuildImpExpList(exportedVars);
RebuildImpExpList(importedVars);
}
bool ImportsOrExportsModuleName(const std::string &moduleName) {
return impExpModuleNames.find(moduleName) != impExpModuleNames.end();
}
NativeModule nm{};
std::vector<ModuleWaitingThread> waitingThreads;
std::vector<FuncSymbolExport> exportedFuncs;
std::vector<FuncSymbolImport> importedFuncs;
std::vector<VarSymbolExport> exportedVars;
std::vector<VarSymbolImport> importedVars;
std::set<std::string> impExpModuleNames;
// Keep track of the code region so we can throw out analysis results
// when unloaded.
u32 textStart = 0;
u32 textEnd = 0;
// Keep track of the libstub pointers so we can recheck on load state.
u32 libstub = 0;
u32 libstubend = 0;
u32 memoryBlockAddr = 0;
u32 memoryBlockSize = 0;
u32 crc = 0;
PSPPointer<NativeModule> modulePtr;
bool isFake = false;
};
void PSPModule::GetQuickInfo(char *ptr, int size) {
snprintf(ptr, size, "%d.%d %sname=%s gp=%08x entry=%08x",
nm.version[1], nm.version[0],
isFake ? "(faked) " : "",
nm.name,
nm.gp_value,
nm.entry_addr);
}
void PSPModule::GetLongInfo(char *ptr, int bufSize) const {
StringWriter w(ptr, bufSize);

View file

@ -20,7 +20,13 @@
#include <string>
#include <string_view>
#include <vector>
#include <set>
#include "Core/HLE/sceKernel.h"
#include "Core/MemMap.h"
class PointerWrap;
struct SceKernelSMOption;
struct PspModuleInfo {
u16_le moduleAttrs; //0x0000 User Mode, 0x1000 Kernel Mode
@ -34,8 +40,169 @@ struct PspModuleInfo {
u32_le libstubend; // ptr to end of .lib.stub section
};
class PointerWrap;
struct SceKernelSMOption;
enum NativeModuleStatus {
MODULE_STATUS_STARTING = 4,
MODULE_STATUS_STARTED = 5,
MODULE_STATUS_STOPPING = 6,
MODULE_STATUS_STOPPED = 7,
MODULE_STATUS_UNLOADING = 8,
};
const char *NativeModuleStatusToString(NativeModuleStatus status);
struct NativeModule {
u32_le next;
u16_le attribute;
u8 version[2];
char name[28];
u32_le status;
u32_le unk1;
u32_le modid; // 0x2C
u32_le usermod_thid;
u32_le memid;
u32_le mpidtext;
u32_le mpiddata;
u32_le ent_top;
u32_le ent_size;
u32_le stub_top;
u32_le stub_size;
u32_le module_start_func;
u32_le module_stop_func;
u32_le module_bootstart_func;
u32_le module_reboot_before_func;
u32_le module_reboot_phase_func;
u32_le entry_addr;
u32_le gp_value;
u32_le text_addr;
u32_le text_size;
u32_le data_size;
u32_le bss_size;
u32_le nsegment;
u32_le segmentaddr[4];
u32_le segmentsize[4];
u32_le module_start_thread_priority;
u32_le module_start_thread_stacksize;
u32_le module_start_thread_attr;
u32_le module_stop_thread_priority;
u32_le module_stop_thread_stacksize;
u32_le module_stop_thread_attr;
u32_le module_reboot_before_thread_priority;
u32_le module_reboot_before_thread_stacksize;
u32_le module_reboot_before_thread_attr;
};
struct VarSymbolImport {
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 nid;
u32 stubAddr;
u8 type;
};
struct VarSymbolExport {
bool Matches(const VarSymbolImport &other) const {
return nid == other.nid && !strncmp(moduleName, other.moduleName, KERNELOBJECT_MAX_NAME_LENGTH);
}
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 nid;
u32 symAddr;
};
struct FuncSymbolImport {
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 stubAddr;
u32 nid;
};
struct FuncSymbolExport {
bool Matches(const FuncSymbolImport &other) const {
return nid == other.nid && !strncmp(moduleName, other.moduleName, KERNELOBJECT_MAX_NAME_LENGTH);
}
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 symAddr;
u32 nid;
};
struct WriteVarSymbolState;
struct ModuleWaitingThread {
SceUID threadID;
u32 statusPtr;
};
class PSPModule : public KernelObject {
public:
~PSPModule();
const char *GetName() override { return nm.name; }
const char *GetTypeName() override { return GetStaticTypeName(); }
static const char *GetStaticTypeName() { return "Module"; }
void GetQuickInfo(char *ptr, int size) override;
void GetLongInfo(char *ptr, int bufSize) const override;
static u32 GetMissingErrorCode();
static int GetStaticIDType() { return PPSSPP_KERNEL_TMID_Module; }
int GetIDType() const override { return PPSSPP_KERNEL_TMID_Module; }
u32 GetDataAddr() const {
return nm.text_addr + nm.text_size;
}
u32 GetBSSAddr() const {
return nm.text_addr + nm.text_size + nm.data_size;
}
void DoState(PointerWrap &p) override;
// We don't do this in the destructor to avoid annoying messages on game shutdown.
void Cleanup();
void ImportFunc(const FuncSymbolImport &func, bool reimporting);
void ImportVar(WriteVarSymbolState &state, const VarSymbolImport &var);
void ExportFunc(const FuncSymbolExport &func);
void ExportVar(const VarSymbolExport &var);
template <typename T>
void RebuildImpExpList(const std::vector<T> &list) {
for (size_t i = 0; i < list.size(); ++i) {
impExpModuleNames.insert(list[i].moduleName);
}
}
void RebuildImpExpModuleNames() {
impExpModuleNames.clear();
RebuildImpExpList(exportedFuncs);
RebuildImpExpList(importedFuncs);
RebuildImpExpList(exportedVars);
RebuildImpExpList(importedVars);
}
bool ImportsOrExportsModuleName(const std::string &moduleName) {
return impExpModuleNames.find(moduleName) != impExpModuleNames.end();
}
NativeModule nm{};
std::vector<ModuleWaitingThread> waitingThreads;
std::vector<FuncSymbolExport> exportedFuncs;
std::vector<FuncSymbolImport> importedFuncs;
std::vector<VarSymbolExport> exportedVars;
std::vector<VarSymbolImport> importedVars;
std::set<std::string> impExpModuleNames;
// Keep track of the code region so we can throw out analysis results
// when unloaded.
u32 textStart = 0;
u32 textEnd = 0;
// Keep track of the libstub pointers so we can recheck on load state.
u32 libstub = 0;
u32 libstubend = 0;
u32 memoryBlockAddr = 0;
u32 memoryBlockSize = 0;
u32 crc = 0;
PSPPointer<NativeModule> modulePtr{};
bool isFake = false;
};
KernelObject *__KernelModuleObject();
void __KernelModuleDoState(PointerWrap &p);

View file

@ -86,7 +86,7 @@ const WaitTypeNames waitTypeNames[] = {
{ WAITTYPE_USB, "USB" },
};
const char *getWaitTypeName(WaitType type) {
const char *WaitTypeToString(WaitType type) {
for (WaitTypeNames info : waitTypeNames) {
if (info.type == type)
return info.name;

View file

@ -115,7 +115,7 @@ enum WaitType : int
NUM_WAITTYPES
};
const char *getWaitTypeName(WaitType type);
const char *WaitTypeToString(WaitType type);
// Suspend wait and timeout while a thread enters a callback.
typedef void (* WaitBeginCallbackFunc)(SceUID threadID, SceUID prevCallbackId);

View file

@ -406,6 +406,9 @@ inline const u8* GetPointerOrNull(const u32 address) {
// Avoiding a global include for NotifyMemInfo.
void PSPPointerNotifyRW(int rw, uint32_t ptr, uint32_t bytes, const char *tag, size_t tagLen);
// TODO: These are actually quite annoying because they can't be followed in the MSVC debugger...
// Need to find a solution for that. Can't just change the internal representation though, because
// these can be present in PSP-native structs.
template <typename T>
struct PSPPointer
{

View file

@ -411,7 +411,7 @@ void DrawThreadView(ImConfig &cfg, ImControl &control) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(ThreadStatusToString(thread.status));
ImGui::TableNextColumn();
ImGui::TextUnformatted(getWaitTypeName(thread.waitType));
ImGui::TextUnformatted(WaitTypeToString(thread.waitType));
ImGui::TableNextColumn();
char temp[64];
WaitIDToString(thread.waitType, thread.waitID, temp, sizeof(temp));

View file

@ -217,7 +217,7 @@ void CtrlThreadList::GetColumnText(wchar_t* dest, size_t destSize, int row, int
}
break;
case TL_WAITTYPE:
wcscpy(dest, ConvertUTF8ToWString(getWaitTypeName(threads[row].waitType)).c_str());
wcscpy(dest, ConvertUTF8ToWString(WaitTypeToString(threads[row].waitType)).c_str());
break;
}
}