Demangle: handle CodeWarrior templates, function pointers and @-symbols

Checked against two PSP binaries that shipped with intact symbol tables,
which turned up several constructs the format's usual description doesn't
mention:

- Template arguments are written literally inside the length-prefixed name
  ("39CList<Q38hlScreen5Brwsr13CContentsUnit>"), not with a "__PT" prefix,
  and they nest. Function templates put theirs in the base name instead,
  followed by the return type.
- A family of "@"-decorated symbols for things with no C++ name: thunks
  ("@12@__dt__3SonFv"), string literals, function-local statics and their
  guard variables. Plus __vt__/__RTTI__/__sinit_, printed in the same style
  as the Itanium special names.
- Types are now built as a split declarator, so a pointer to a function
  comes out as "int (*)(int)" rather than "int (int) *".

Also stop the lenient pass from turning plain C names with a "__" in them
into nonsense - "I3dClut__FlushCache" became "I3dClut(long, ...)". It now
requires a class qualifier, which costs nothing: over ~10000 symbols the
lenient pass rescued none and only produced those false positives.

Symbol map names go from 128 to 256 characters, since a demangled name
keeps its parameters and templates make short work of 128.

docs/CodeWarriorMangling.md describes the format, marking the parts that
are inferred from cfront rather than attested in a real binary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SF5eS5QDNexLksRDeDZvwY
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-08-29 23:23:57 +02:00
1 parent f89a2d4199
commit dfc04f3578
5 files changed
+467 -28

No files matched your search

+6 -6
View File
@@ -141,9 +141,9 @@ bool SymbolMap::LoadSymbolMap(const Path &filename) {
int moduleIndex = 0;
int typeInt = ST_NONE;
SymbolType type;
char name[128] = {0};
char name[256] = {0};
if (sscanf(line, ".module %x %08x %08x %127c", (unsigned int *)&moduleIndex, &address, &size, name) >= 3) {
if (sscanf(line, ".module %x %08x %08x %255c", (unsigned int *)&moduleIndex, &address, &size, name) >= 3) {
// Found a module definition.
ModuleEntry mod;
mod.index = moduleIndex;
@@ -155,7 +155,7 @@ bool SymbolMap::LoadSymbolMap(const Path &filename) {
continue;
}
const int matched = sscanf(line, "%08x %08x %x %i %127c", &address, &size, &vaddress, &typeInt, name);
const int matched = sscanf(line, "%08x %08x %x %i %255c", &address, &size, &vaddress, &typeInt, name);
if (matched < 1)
continue;
type = (SymbolType) typeInt;
@@ -316,8 +316,8 @@ bool SymbolMap::LoadNocashSym(const Path &filename) {
return false;
while (!feof(f)) {
char line[256], value[256] = {0};
char *p = fgets(line, 256, f);
char line[512], value[256] = {0};
char *p = fgets(line, sizeof(line), f);
if (p == NULL)
break;
@@ -1262,7 +1262,7 @@ void SymbolMap::SetLabelName(const char* name, u32 address) {
auto label = labels.find(symbolKey);
if (label != labels.end()) {
truncate_cpy(label->second.name, name);
label->second.name[127] = 0;
label->second.name[255] = 0;
// Refresh the active item if it exists.
auto active = activeLabels.find(address);