Per-module symbol save/load, module identity via crc, GetModuleIndex fix

SymbolMap:
- Fix GetModuleIndex(): it only checked the end of an active module's range
  (via activeModuleEnds.upper_bound), never the start, so an address sitting
  in the gap before a module was silently misattributed to it. Added
  GetModuleIndexByName() as a companion lookup.
- AddModule() gains an optional crc param, stored per ModuleEntry. Reactivating
  a module by name now also requires the crc to agree when both sides know it,
  so two unrelated binaries that happen to share a name no longer get merged
  into one symbol table (addresses the old TODO at the top of SymbolMap.h).
- AddLabel()/AddFunction() gain an updateName param (default false, preserving
  existing "first writer wins" behavior) so a trusted source - like a loaded
  symbol file - can be allowed to overwrite a name that a lower-confidence
  automatic pass already assigned.
- New SaveModuleSymbols()/LoadModuleSymbols()/GetModuleSymbolsPath(): save or
  restore one module's functions/data/labels to/from a small human-editable
  text file, addressed relative to the module (so the file stays valid however
  the module ends up positioned on a later run). Keyed by
  PSP/SYSTEM/SYMBOLS/<moduleName>_<crc>.ppsym - deliberately by module+crc
  rather than by game, so it's shared by every game/homebrew that loads the
  exact same module. A "# game <id> <title>" comment records who last saved
  it, informational only.

WebSocket debugger: hle.module.saveSymbols/loadSymbols expose the above.

sceKernelModule.cpp: auto-load a module's saved symbols right after it's
registered with the symbol map (both the real ELF-load path and the
savestate-load path), and auto-save on unload (before UnloadModule(), while
its symbols are still active) - gated behind the new bAutoSaveLoadSymbols
config setting (default off), with a matching Developer Tools checkbox and
a --auto-save-load-symbols command-line override for headless use.

Includes some in-progress cleanup already staged: DescribeAddress now calls
g_symbolMap->GetDescription() directly instead of through the now-removed
MIPSDebugInterface::getDescription() wrapper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
This commit is contained in:
Henrik Rydgård
2026-08-17 16:01:23 +02:00
co-authored by Claude Sonnet 5
parent 2a1df9f0b8
commit 29a38af37e
14 changed files with 458 additions and 40 deletions
+27 -2
View File
@@ -190,6 +190,19 @@ struct PspLibStubEntry {
PSPModule::~PSPModule() {
if (memoryBlockAddr) {
if (g_Config.bAutoSaveLoadSymbols) {
// Must happen before UnloadModule() below, while this module's symbols are still
// active (SaveModuleSymbols itself doesn't care, but GetModuleIndexByName's
// active-module lookup does).
char moduleName[29] = { 0 };
truncate_cpy(moduleName, nm.name);
int idx = g_symbolMap->GetModuleIndexByName(moduleName);
if (idx > 0) {
Path path = SymbolMap::GetModuleSymbolsPath(moduleName, g_symbolMap->GetModuleCrc(idx));
g_symbolMap->SaveModuleSymbols(idx, path, g_paramSFO.GetDiscID(), g_paramSFO.GetValueString("TITLE"));
}
}
// If it's either below user memory, or using a high kernel bit, it's in kernel.
if (memoryBlockAddr < PSP_GetUserMemoryBase() || memoryBlockAddr > PSP_GetUserMemoryEnd()) {
kernelMemory.Free(memoryBlockAddr);
@@ -292,7 +305,13 @@ void PSPModule::DoState(PointerWrap &p) {
char moduleName[29] = { 0 };
truncate_cpy(moduleName, nm.name);
if (memoryBlockAddr != 0) {
g_symbolMap->AddModule(moduleName, memoryBlockAddr, memoryBlockSize);
g_symbolMap->AddModule(moduleName, memoryBlockAddr, memoryBlockSize, crc);
if (g_Config.bAutoSaveLoadSymbols) {
int idx = g_symbolMap->GetModuleIndexByName(moduleName);
if (idx > 0) {
g_symbolMap->LoadModuleSymbols(idx, SymbolMap::GetModuleSymbolsPath(moduleName, crc));
}
}
}
}
@@ -1348,7 +1367,13 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
strncpy(moduleName, modinfo->name, ARRAY_SIZE(module->nm.name));
if (module->memoryBlockAddr != 0) {
g_symbolMap->AddModule(moduleName, module->memoryBlockAddr, module->memoryBlockSize);
g_symbolMap->AddModule(moduleName, module->memoryBlockAddr, module->memoryBlockSize, module->crc);
if (g_Config.bAutoSaveLoadSymbols) {
int idx = g_symbolMap->GetModuleIndexByName(moduleName);
if (idx > 0) {
g_symbolMap->LoadModuleSymbols(idx, SymbolMap::GetModuleSymbolsPath(moduleName, module->crc));
}
}
}
SectionID textSection = reader.GetSectionByName(".text");