Compare commits

...

4 commits

Author SHA1 Message Date
KeybadeBlox
022582003d objdiff symbol matching refinements
With name mangling, objdiff.json no longer needs massive lists of which
names to match to what.  Some function signatures were also minorly
refined.
2026-02-12 21:19:46 -05:00
KeybadeBlox
d372c17094 Miscellaneous name mangling improvements
Improved error reporting, skipping undesirable symbols like jump tables,
skipping special symbols like intrinsics, etc.
2026-02-12 21:15:52 -05:00
KeybadeBlox
78127e64ef Arguments via environment variables in delink.sh
It was really annoying having to type all that in every time.
2026-02-12 16:16:07 -05:00
KeybadeBlox
13048fef71 Tidy up compiler options
Nothing affecting codegen.
2026-02-12 16:07:14 -05:00
6 changed files with 107 additions and 221 deletions

View file

@ -3,7 +3,7 @@ all: src/JSRF/Jet2.obj
# Simple inference rule for producing object files
.SUFFIXES: .cpp .obj
.cpp.obj:
CL.EXE /nologo /Wall /TP /W3 /Ogityb0 /MT /Gf /GX /Fo$@ /c $<
CL.EXE /nologo /Wall /W4 /Ogityb0 /GfX /Fo$@ /c $<
# Header files used for each object
src/JSRF/Jet2.obj: src/JSRF/Core.hpp src/Std.hpp src/XDK/D3D.hpp\

View file

@ -14,11 +14,11 @@
"source_path": "src/JSRF/Jet2.cpp"
},
"symbol_mappings": {
"[.rdata-0]": "[.xdata$x-0]",
"_main_funcinfo": "$T754",
"_main_handler": "$L758",
"_main_handler_unwind1": "$L750",
"_main_unwindmap": "$T760"
"?main_funcinfo@@3UFuncInfo@@A": "$T754",
"?main_handler@@YAXPAUEHExceptionRecord@@PAKPAXPAU_xDISPATCHER_CONTEXT@@@Z": "$L758",
"?main_handler_unwind1@@YAXXZ": "$L750",
"?main_unwindmap@@3PAUUnwindMapEntry@@A": "$T760",
"[.rdata-0]": "[.xdata$x-0]"
}
},
{
@ -28,94 +28,6 @@
"metadata": {
"complete": false,
"source_path": "src/JSRF/Core.cpp"
},
"symbol_mappings": {
"DrawTree::DrawTree": "??0DrawTree@@QAE@PAUGameObj@@W4GameObjIndex@@W4GameObjFlags@@@Z",
"DrawTree::`scalar_deleting_destructor'": "??_GDrawTree@@UAEPAXI@Z",
"DrawTree::copySomeVectors": "?copySomeVectors@DrawTree@@QAEXXZ",
"DrawTree::~DrawTree": "??1DrawTree@@UAE@XZ",
"Game::Game": "??0Game@@QAE@PAII@Z",
"Game::`scalar_deleting_destructor'": "??_GGame@@UAEPAXI@Z",
"Game::addToDrawList": "?addToDrawList@Game@@QAEXPAUGameObj@@@Z",
"Game::allocObjIndex": "?allocObjIndex@Game@@QAEHW4GameObjIndex@@0@Z",
"Game::appendToDrawPriorityList": "?appendToDrawPriorityList@Game@@QAEXPAUGameObj@@@Z",
"Game::clearDrawPriorityList": "?clearDrawPriorityList@Game@@QAEXXZ",
"Game::clearScreen": "?clearScreen@Game@@QAEXXZ",
"Game::draw": "?draw@Game@@QAEXXZ",
"Game::drawList": "?drawList@Game@@QAEXW4GameObjFlags@@H@Z",
"Game::drawList_": "?drawList_@Game@@QAEXW4GameObjFlags@@HH0I00@Z",
"Game::drawObj": "?drawObj@Game@@QAEXPAUGameObj@@H@Z",
"Game::drawObjs": "?drawObjs@Game@@QAEXXZ",
"Game::drawTree1": "?drawTree1@Game@@QAEXPAUGameObj@@@Z",
"Game::enableDrawChildren": "?enableDrawChildren@Game@@QAEXXZ",
"Game::enableSkipDraw": "?enableSkipDraw@Game@@QAEXXZ",
"Game::enableSomeExtraDrawListCode": "?enableSomeExtraDrawListCode@Game@@QAEXXZ",
"Game::exec": "?exec@Game@@QAEXXZ",
"Game::fatal": "?fatal@Game@@QAEXXZ",
"Game::frame": "?frame@Game@@QAEXXZ",
"Game::getDrawPriorityListHead": "?getDrawPriorityListHead@Game@@QAEPAUGameObj@@XZ",
"Game::getGlobal": "?getGlobal@Game@@QAEIW4GlobalIndex@@@Z",
"Game::getObj": "?getObj@Game@@QAEPAUGameObj@@W4GameObjIndex@@@Z",
"Game::initRootExecObj": "?initRootExecObj@Game@@QAEXXZ",
"Game::mainLoop": "?mainLoop@Game@@QAEHXZ",
"Game::objIndexAvail": "?objIndexAvail@Game@@QAEHW4GameObjIndex@@@Z",
"Game::removeFromDrawList": "?removeFromDrawList@Game@@QAEXPAUGameObj@@@Z",
"Game::setCoveredPauseNextFrame": "?setCoveredPauseNextFrame@Game@@QAEXH@Z",
"Game::setDrawMode": "?setDrawMode@Game@@QAEXW4DrawMode@@@Z",
"Game::setEventNextFrame": "?setEventNextFrame@Game@@QAEXH@Z",
"Game::setFallbackBgColour": "?setFallbackBgColour@Game@@QAEXKH@Z",
"Game::setFreezeCamNextFrame": "?setFreezeCamNextFrame@Game@@QAEXH@Z",
"Game::setGlobal": "?setGlobal@Game@@QAEXW4GlobalIndex@@I@Z",
"Game::setLogosStarted": "?setLogosStarted@Game@@QAEXH@Z",
"Game::setObj": "?setObj@Game@@QAEXW4GameObjIndex@@PAUGameObj@@@Z",
"Game::setUncoveredPauseNextFrame": "?setUncoveredPauseNextFrame@Game@@QAEXH@Z",
"Game::sortDrawPriorityList": "?sortDrawPriorityList@Game@@QAEXXZ",
"Game::sortDrawPriorityListSingleLevel": "?sortDrawPriorityListSingleLevel@Game@@QAEXD@Z",
"Game::swapObjs": "?swapObjs@Game@@QAEXW4GameObjIndex@@0@Z",
"Game::unsetObj": "?unsetObj@Game@@QAEXW4GameObjIndex@@@Z",
"Game::~Game": "??1Game@@UAE@XZ",
"GameObj::GameObj": "??0GameObj@@QAE@PAU0@W4GameObjIndex@@W4GameObjFlags@@@Z",
"GameObj::`scalar_deleting_destructor'": "??_GGameObj@@UAEPAXI@Z",
"GameObj::`vftable'": "??_7GameObj@@6B@",
"GameObj::addToSiblings": "?addToSiblings@GameObj@@QAEXPAU1@0@Z",
"GameObj::destructChildren": "?destructChildren@GameObj@@QAEXPAU1@@Z",
"GameObj::drawListCoveredPause": "?drawListCoveredPause@GameObj@@QAEXW4GameObjFlags@@HH0I00@Z",
"GameObj::drawListDefault": "?drawListDefault@GameObj@@QAEXW4GameObjFlags@@HH0I00@Z",
"GameObj::drawListEvent": "?drawListEvent@GameObj@@QAEXW4GameObjFlags@@HH0I00@Z",
"GameObj::drawListFreezeCam": "?drawListFreezeCam@GameObj@@QAEXW4GameObjFlags@@HH0I00@Z",
"GameObj::drawListUncoveredPause": "?drawListUncoveredPause@GameObj@@QAEXW4GameObjFlags@@HH0I00@Z",
"GameObj::drawTreeCoveredPause1": "?drawTreeCoveredPause1@GameObj@@QAEXXZ",
"GameObj::drawTreeCoveredPause2": "?drawTreeCoveredPause2@GameObj@@QAEXXZ",
"GameObj::drawTreeDefault1": "?drawTreeDefault1@GameObj@@QAEXXZ",
"GameObj::drawTreeDefault2": "?drawTreeDefault2@GameObj@@QAEXXZ",
"GameObj::drawTreeEvent1": "?drawTreeEvent1@GameObj@@QAEXXZ",
"GameObj::drawTreeEvent2": "?drawTreeEvent2@GameObj@@QAEXXZ",
"GameObj::drawTreeFreezeCam1": "?drawTreeFreezeCam1@GameObj@@QAEXXZ",
"GameObj::drawTreeFreezeCam2": "?drawTreeFreezeCam2@GameObj@@QAEXXZ",
"GameObj::drawTreeUncoveredPause1": "?drawTreeUncoveredPause1@GameObj@@QAEXXZ",
"GameObj::drawTreeUncoveredPause2": "?drawTreeUncoveredPause2@GameObj@@QAEXXZ",
"GameObj::getParent": "?getParent@GameObj@@QAEPAU1@XZ",
"GameObj::nopDraw": "?drawDefault@GameObj@@UAEXH@Z",
"GameObj::nopExec": "?execDefault@GameObj@@UAEXXZ",
"GameObj::recursiveExecCoveredPause": "?recursiveExecCoveredPause@GameObj@@QAEXXZ",
"GameObj::recursiveExecDefault": "?recursiveExecDefault@GameObj@@QAEXXZ",
"GameObj::recursiveExecEvent": "?recursiveExecEvent@GameObj@@QAEXXZ",
"GameObj::recursiveExecFreezeCam": "?recursiveExecFreezeCam@GameObj@@QAEXXZ",
"GameObj::recursiveExecUncoveredPause": "?recursiveExecUncoveredPause@GameObj@@QAEXXZ",
"GameObj::recursivePostExecCoveredPause": "?recursivePostExecCoveredPause@GameObj@@QAEXXZ",
"GameObj::recursivePostExecDefault": "?recursivePostExecDefault@GameObj@@QAEXXZ",
"GameObj::recursivePostExecEvent": "?recursivePostExecEvent@GameObj@@QAEXXZ",
"GameObj::recursivePostExecFreezeCam": "?recursivePostExecFreezeCam@GameObj@@QAEXXZ",
"GameObj::recursivePostExecUncoveredPause": "?recursivePostExecUncoveredPause@GameObj@@QAEXXZ",
"GameObj::removeChildrenFromObjList": "?removeChildrenFromObjList@GameObj@@QAEXPAU1@@Z",
"GameObj::removeFromObjList": "?removeFromObjList@GameObj@@QAEXPAU1@@Z",
"GameObj::setParent": "?setParent@GameObj@@QAEXPAU1@@Z",
"GameObj::~GameObj": "??1GameObj@@UAE@XZ",
"PlayerObj::PlayerObj": "??0PlayerObj@@QAE@PAUGameObj@@W4GameObjIndex@@W4GameObjFlags@@@Z",
"RootExecObj::RootExecObj": "??0RootExecObj@@QAE@PAUGameObj@@W4GameObjIndex@@W4GameObjFlags@@@Z",
"RootExecObj::`scalar_deleting_destructor'": "??_GRootExecObj@@UAEPAXI@Z",
"RootExecObj::~RootExecObj": "??1RootExecObj@@UAE@XZ",
"removeFromObjListByIndex": "?removeFromObjListByIndex@@YAXW4GameObjIndex@@@Z"
}
},
{
@ -127,74 +39,8 @@
"source_path": "src/JSRF/GameData.cpp"
},
"symbol_mappings": {
"GameData::GameData": "??0GameData@@QAE@XZ",
"GameData::`scalar_deleting_destructor'": "??_GGameData@@UAEPAXI@Z",
"GameData::addHighScore": "?addHighScore@GameData@@QAEXIW4TestRunType@@PAUTestRunScore@@@Z",
"GameData::characterUnlocked": "?characterUnlocked@GameData@@QAEHI@Z",
"GameData::checkFlagCondition": "?checkFlagCondition@GameData@@QAEHI@Z",
"GameData::checkFlagConditionUnpacked": "?checkFlagConditionUnpacked@GameData@@QAEHW4FlagList@@I@Z",
"GameData::checkFlagConditions": "?checkFlagConditions@GameData@@QAEHPAII@Z",
"GameData::clearHeldSouls": "?clearHeldSouls@GameData@@QAEXXZ",
"GameData::clearStateFlags": "?clearStateFlags@GameData@@QAEXTFlagListOrPtr@@@Z",
"GameData::countMiscObjectives": "?countMiscObjectives@GameData@@QAEIXZ",
"GameData::decrypt": "?decrypt@GameData@@QAEHPAD@Z",
"GameData::encrypt": "?encrypt@GameData@@QAEXPAD@Z",
"GameData::eventSeen": "?eventSeen@GameData@@QAEHI@Z",
"GameData::getCustomTagSelected": "?getCustomTagSelected@GameData@@QAEHIW4TagSize@@H@Z",
"GameData::getGarageMusic": "?getGarageMusic@GameData@@QAEIXZ",
"GameData::getHeldSoulsInStage": "?getHeldSoulsInStage@GameData@@QAEII@Z",
"GameData::getHighScore": "?getHighScore@GameData@@QAEHIW4TestRunType@@IPAUTestRunScore@@@Z",
"GameData::getMiscObjective": "?getMiscObjective@GameData@@QAEHI@Z",
"GameData::getRumbleEnabled": "?getRumbleEnabled@GameData@@QAEHXZ",
"GameData::getSaveDataSize": "?getSaveDataSize@GameData@@QAEIXZ",
"GameData::getSaveDescription": "?getSaveDescription@GameData@@QAEXPAUSaveDescription@@@Z",
"GameData::getSelectedTag": "?getSelectedTag@GameData@@QAEIIW4TagSize@@H@Z",
"GameData::getSoulCollectedBySize": "?getSoulCollectedBySize@GameData@@QAEHW4TagSize@@I@Z",
"GameData::getSoulCount": "?getSoulCount@GameData@@QAEIXZ",
"GameData::getSoulHeld": "?getSoulHeld@GameData@@QAEHI@Z",
"GameData::getSoulSpawned": "?getSoulSpawned@GameData@@QAEHI@Z",
"GameData::getSpawnPosIndex": "?getSpawnPosIndex@GameData@@QAEIXZ",
"GameData::getTagState": "?getTagState@GameData@@QAEHIIH@Z",
"GameData::getTimer": "?getTimer@GameData@@QAEIW4Timer@@@Z",
"GameData::getTotalSoulsInStage": "?getTotalSoulsInStage@GameData@@QAEII@Z",
"GameData::getVolumeSettings": "?getVolumeSettings@GameData@@QAEXPAM0@Z",
"GameData::incrementChapter": "?incrementChapter@GameData@@QAEXXZ",
"GameData::incrementPlaytime": "?incrementPlaytime@GameData@@QAEXXZ",
"GameData::incrementTimer": "?incrementTimer@GameData@@QAEXW4Timer@@@Z",
"GameData::lockCharacter": "?lockCharacter@GameData@@QAEXI@Z",
"GameData::resetExceptSettings": "?resetExceptSettings@GameData@@QAEXXZ",
"GameData::resetExceptSettingsAndHighScores": "?resetExceptSettingsAndHighScores@GameData@@QAEXXZ",
"GameData::resetExceptSettingsAndSouls": "?resetExceptSettingsAndSouls@GameData@@QAEXXZ",
"GameData::resetSelectedTags": "?resetSelectedTags@GameData@@QAEXXZ",
"GameData::resetTimer": "?resetTimer@GameData@@QAEXW4Timer@@@Z",
"GameData::restoreHeldSouls": "?restoreHeldSouls@GameData@@QAEXXZ",
"GameData::setCustomTagSelected": "?setCustomTagSelected@GameData@@QAEXIW4TagSize@@HH@Z",
"GameData::setEventSeen": "?setEventSeen@GameData@@QAEXI@Z",
"GameData::setGarageMusic": "?setGarageMusic@GameData@@QAEXI@Z",
"GameData::setMiscObjective": "?setMiscObjective@GameData@@QAEXI@Z",
"GameData::setMissionDigits34": "?setMissionDigits34@GameData@@QAEXI@Z",
"GameData::setRumbleEnabled": "?setRumbleEnabled@GameData@@QAEXH@Z",
"GameData::setSelectedTag": "?setSelectedTag@GameData@@QAEXIW4TagSize@@IH@Z",
"GameData::setSoulCollected": "?setSoulCollected@GameData@@QAEXI@Z",
"GameData::setSoulSpawned": "?setSoulSpawned@GameData@@QAEXI@Z",
"GameData::setSpawnPosIndex": "?setSpawnPosIndex@GameData@@QAEXI@Z",
"GameData::setTagCovered": "?setTagCovered@GameData@@QAEXIIHI@Z",
"GameData::setTagState": "?setTagState@GameData@@QAEXIIHI@Z",
"GameData::setTimer": "?setTimer@GameData@@QAEXW4Timer@@I@Z",
"GameData::setUnusedBitfield": "?setUnusedBitfield@GameData@@QAEXI@Z",
"GameData::setUnusedPerStageBitmask": "?setUnusedPerStageBitmask@GameData@@QAEXII@Z",
"GameData::setVolumeSettings": "?setVolumeSettings@GameData@@QAEXMM@Z",
"GameData::soulSpawnedUncollected": "?soulSpawnedUncollected@GameData@@QAEHI@Z",
"GameData::stash": "?stash@GameData@@QAEXXZ",
"GameData::stashRestore": "?stashRestore@GameData@@QAEXXZ",
"GameData::stashRestoreExceptHighScores": "?stashRestoreExceptHighScores@GameData@@QAEXXZ",
"GameData::stashRestoreExceptSpecialFlags": "?stashRestoreExceptSpecialFlags@GameData@@QAEXXZ",
"GameData::unlockCharacter": "?unlockCharacter@GameData@@QAEXI@Z",
"GameData::writeStateFlag": "?writeStateFlag@GameData@@QAEXI@Z",
"GameData::writeStateFlagUnpacked": "?writeStateFlagUnpacked@GameData@@QAEXW4FlagList@@II@Z",
"GameData::writeStateFlags": "?writeStateFlags@GameData@@QAEXPAII@Z",
"finalizeGameData": "_$E2",
"initGameData": "_$E1"
"?finalizeGameData@@YAXXZ": "_$E2",
"?initGameData@@YGXXZ": "_$E1"
}
}
]

View file

@ -196,7 +196,7 @@ void GameObj::removeChildrenFromObjList(GameObj * firstChild) {
// Address: 0x00011C80
// Matching: yes
void GameObj::drawDefault(int unknown) {}
void GameObj::drawDefault(unsigned unknown) {}
// Address: 0x00011C90
// Matching: yes
@ -212,7 +212,7 @@ void GameObj::execEvent() {}
void GameObj::postExecEvent() {}
// Eliminated by link time code generation (aliased with 0x00011C80)
void GameObj::drawEvent(int unknown) {}
void GameObj::drawEvent(unsigned unknown) {}
// Eliminated by link time code generation (aliased with 0x00011C90)
void GameObj::execCoveredPause() {}
@ -221,7 +221,7 @@ void GameObj::execCoveredPause() {}
void GameObj::postExecCoveredPause() {}
// Eliminated by link time code generation (aliased with 0x00011C80)
void GameObj::drawCoveredPause(int unknown) {}
void GameObj::drawCoveredPause(unsigned unknown) {}
// Eliminated by link time code generation (aliased with 0x00011C90)
void GameObj::execFreezeCam() {}
@ -230,7 +230,7 @@ void GameObj::execFreezeCam() {}
void GameObj::postExecFreezeCam() {}
// Eliminated by link time code generation (aliased with 0x00011C80)
void GameObj::drawFreezeCam(int unknown) {}
void GameObj::drawFreezeCam(unsigned unknown) {}
// Eliminated by link time code generation (aliased with 0x00011C90)
void GameObj::execUncoveredPause() {}
@ -239,7 +239,7 @@ void GameObj::execUncoveredPause() {}
void GameObj::postExecUncoveredPause() {}
// Eliminated by link time code generation (aliased with 0x00011C80)
void GameObj::drawUncoveredPause(int unknown) {}
void GameObj::drawUncoveredPause(unsigned unknown) {}
// Address: 0x00011CA0
// Matching: no
@ -336,7 +336,7 @@ void Game::exec() {
// Address: 0x00012580
// Matching: no
void Game::drawObj(GameObj * obj, int unknown) {
void Game::drawObj(GameObj * obj, unsigned unknown) {
}
// Address: 0x000125E0
@ -510,7 +510,7 @@ void Game::appendToDrawPriorityList(GameObj * obj) {
// Address: 0x00012A20
// Matching: no
void Game::sortDrawPriorityListSingleLevel(char sortKeyBitOffset) {
void Game::sortDrawPriorityListSingleLevel(unsigned char sortKeyBitOffset) {
for (
unsigned i = 0;
i < sizeof this->drawPriorityListsByKeyHeads/sizeof *this->drawPriorityListsByKeyHeads;

View file

@ -146,23 +146,23 @@ struct GameObj {
// frame.
virtual void execDefault();
virtual void postExecDefault();
virtual void drawDefault(int);
virtual void drawDefault(unsigned);
virtual void execEvent();
virtual void postExecEvent();
virtual void drawEvent(int);
virtual void drawEvent(unsigned);
virtual void execCoveredPause();
virtual void postExecCoveredPause();
virtual void drawCoveredPause(int);
virtual void drawCoveredPause(unsigned);
virtual void execFreezeCam();
virtual void postExecFreezeCam();
virtual void drawFreezeCam(int);
virtual void drawFreezeCam(unsigned);
virtual void execUncoveredPause();
virtual void postExecUncoveredPause();
virtual void drawUncoveredPause(int);
virtual void drawUncoveredPause(unsigned);
void recursivePostExecDefault();
void recursivePostExecEvent();
@ -297,7 +297,7 @@ struct Game {
Game(unsigned *, unsigned);
virtual ~Game();
void exec();
void drawObj(GameObj * obj, int);
void drawObj(GameObj * obj, unsigned);
void drawList_(
GameObjFlags flagFilterAny1,
int drawArg1,
@ -339,7 +339,7 @@ struct Game {
void clearDrawPriorityList();
GameObj * getDrawPriorityListHead();
void appendToDrawPriorityList(GameObj * obj);
void sortDrawPriorityListSingleLevel(char sortKeyBitOffset);
void sortDrawPriorityListSingleLevel(unsigned char sortKeyBitOffset);
void setFallbackBgColour(D3DCOLOR colour, BOOL useFallback);
void initRootExecObj();

View file

@ -3,11 +3,21 @@
# objects.csv and a Ghidra project via boricj's delinker extension
main() {
if [ $# -ne 3 ]; then usage; fi
ghidra_path=$1
project_path=$2
project_name=$3
if \
[ $# -eq 0 ] &&\
[ -n "${GHIDRA_HOME-}" ] &&\
[ -n "${JSRFDECOMP_PROJECTPATH-}" ] &&\
[ -n "${JSRFDECOMP_PROJECTNAME-}" ]
then
ghidra_path=$GHIDRA_HOME
project_path=$JSRFDECOMP_PROJECTPATH
project_name=$JSRFDECOMP_PROJECTNAME
elif [ $# -eq 3 ]; then
ghidra_path=$1
project_path=$2
project_name=$3
else usage
fi
printf '=== Delinking object files into ../decompile/target/ ===\n'
@ -39,6 +49,9 @@ usage() {
' GHIDRA_PATH is the path to your Ghidra installation'\
' PROJECT_PATH is the path to your JSRF Ghidra project'\
' PROJECT_NAME is the name of your JSRF Ghidra project'\
'Alternatively, the environment variables $GHIDRA_HOME, $JSRFDECOMP_PROJECTPATH,'\
'and $JSRFDECOMP_PROJECTNAME can be set, and the script can be called with no'\
'arguments.'\
''\
'Populates the target/ directory with delinked object files using the address'\
'ranges given in objects.csv.' >& 2

View file

@ -107,7 +107,10 @@ public class MSVC7Mangle extends GhidraScript{
final Reference[] refs = ins.getReferencesFrom();
for (int i = 0; i < refs.length; i++) {
final Symbol symbol = getSymbolAt(refs[i].getToAddress());
if (symbol != null) mangle(symbol);
if ( // Guard against spurious references to nonexisting things
symbol != null &&
symbol.getObject() != null
) mangle(symbol);
}
}
}
@ -116,13 +119,17 @@ public class MSVC7Mangle extends GhidraScript{
private void mangle(final Symbol s) throws Exception {
/* Set the given symbol's name to its mangled version */
// Skip if already mangled
if (s.getName().charAt(0) == '?') return;
// Skip if already mangled; skip jump tables
final String name = s.getName(true);
if (
name.charAt(0) == '?' ||
name.startsWith("switchD_")
) return;
// Get mangled name
final String mangled = switch (s.getObject()) {
case Function f -> mangleFn (f);
case Data d -> mangleData(d, s.getName(true));
case Data d -> mangleData(d, name);
default -> null;
};
@ -145,17 +152,21 @@ public class MSVC7Mangle extends GhidraScript{
private String mangleFn(final Function f) throws Exception {
/* Generate a mangled name for a function */
// Special cases for main()
if (f.getName(true).equals("main" )) return "_main";
if (f.getName(true).equals("___CxxFrameHandler")) return "___CxxFrameHandler";
final String nameRaw = f.getName(true);
// Special case for main()
if (nameRaw.equals("main")) return "_main";
// Special symbols like intrinsics aren't mangled
if (nameRaw.startsWith("__")) return nameRaw;
final ArrayList<String> dict = new ArrayList<>();
final List<String> nameParts = Arrays.asList(f.getName(true).split("::"));
final List<String> nameParts = Arrays.asList(nameRaw.split("::"));
Collections.reverse(nameParts);
final boolean isMethod = f.getCallingConventionName().equals("__thiscall") &&
nameParts.size() >= 2;
final String name = mangleIdentifier(f.getName(true), isMethod, f.getReturnType(), dict);
final String name = mangleIdentifier(nameRaw, isMethod, f.getReturnType(), dict);
// Special methods with unique formats
if (isMethod) {
@ -163,14 +174,16 @@ public class MSVC7Mangle extends GhidraScript{
final String clsName = nameParts.get(1);
if (unqualified.equals( clsName)) { // Constructor
return "?" + name + "QAE@" + mangleArgs(f.getSignature(true), dict) + "Z";
return "?" + name + "QAE@" +
mangleArgs(f.getSignature(true), dict, nameRaw + "()") +
"Z";
} else if (unqualified.equals("~" + clsName)) { // Destructor
return "?" + name + (isVirtual(f) ? "UAE" : "QAE") + "@XZ";
}
}
return "?" + name + mangleFnAttrs(f, nameParts) +
mangleFnType(f.getSignature(true), dict);
mangleFnType(f.getSignature(true), dict, nameRaw + "()");
}
private static String mangleIdentifier(
@ -272,7 +285,9 @@ public class MSVC7Mangle extends GhidraScript{
final List<T> dict
) {
/* Produce a backreference string if x is found in dict */
switch (Integer.valueOf(dict.indexOf(x))) {
if (x instanceof String s && s.startsWith("?"))
return Optional.empty(); // No matching special names
else switch (Integer.valueOf(dict.indexOf(x))) {
case -1:
dict.add(x);
return Optional.empty();
@ -303,12 +318,15 @@ public class MSVC7Mangle extends GhidraScript{
final Function func = getFunctionContaining(refs[i].getFromAddress());
if (data != null) {
final String name = getSymbolAt(data.getRoot()
.getAddress()).getName(false);
if (
name.equals("`vftable'") ||
name.startsWith("??_7")
) return true;
final Symbol s = getSymbolAt(data.getRoot()
.getAddress());
if (s != null) {
final String name = s.getName(false);
if (
name.equals("`vftable'") ||
name.startsWith("??_7")
) return true;
}
} else if (func != null) {
final String name = func.getName(false);
if (
@ -334,12 +352,13 @@ public class MSVC7Mangle extends GhidraScript{
private static String mangleFnType(
final FunctionSignature f,
final List<String> dict
final List<String> dict,
final String loc
) throws Exception {
/* Mangle everything in f but its name and visibility/linkage */
return mangleCallC(f) + mangleType(f.getReturnType(), dict) +
mangleArgs(f, dict) + "Z";
return mangleCallC(f) +
mangleType(f.getReturnType(), dict, loc) +
mangleArgs(f, dict, loc) + "Z";
}
private static String mangleCallC(final FunctionSignature f) throws Exception {
@ -358,15 +377,16 @@ public class MSVC7Mangle extends GhidraScript{
private static String mangleType(
final DataType t,
final List<String> dict
final List<String> dict,
final String loc
) throws Exception {
/* Mangle a data type in a function name
All types are assumed to have no CV qualifiers.
*/
if (t == null) throw new Exception (
"A data type was reported as null. Ensure that all " +
"data types in the code/data to mangle have been " +
"defined."
"A data type at " + loc + " was reported as null. " +
"Ensure that all data types in the code/data to " +
"mangle have been defined."
);
return switch(t) {
@ -384,7 +404,7 @@ public class MSVC7Mangle extends GhidraScript{
case LongDoubleDataType _ -> "O";
case Pointer p -> "P" +
(p.getDataType() instanceof FunctionSignature ? "6" : "A") +
mangleType(p.getDataType(), dict);
mangleType(p.getDataType(), dict, loc);
case Union u -> "T" + mangleIdentifier(u.getName(), false, null, dict);
case Structure s -> "U" + mangleIdentifier(s.getName(), false, null, dict);
case Enum e -> "W4" + mangleIdentifier(e.getName(), false, null, dict);
@ -393,12 +413,12 @@ public class MSVC7Mangle extends GhidraScript{
case UnsignedLongLongDataType _ -> "_K";
case BooleanDataType _ -> "_N";
case WideCharDataType _ -> "_W";
case Array a -> "PA" + mangleArrDims(a) + mangleType(arrType(a), dict);
case FunctionSignature f -> mangleFnType(f, dict);
case TypeDef d -> mangleType(d.getBaseDataType(), dict);
case DefaultDataType _ -> throw new Exception ("Encountered data marked \"undefined\". Ensure that all data types in the code/data to mangle have been defined.");
case Undefined _ -> throw new Exception ("Encountered data marked \"undefined\". Ensure that all data types in the code/data to mangle have been defined.");
default -> throw new Exception ("Unknown type \"" + t.getClass().getName() + "\"");
case Array a -> "PA" + mangleArrDims(a) + mangleType(arrType(a), dict, loc);
case FunctionSignature f -> mangleFnType(f, dict, "function typedef \"" + f.getName() + "\"");
case TypeDef d -> mangleType(d.getBaseDataType(), dict, "typedef \"" + d.getName() + "\"");
case DefaultDataType _ -> throw new Exception ("Encountered data marked \"undefined\" at " + loc + ". Ensure that all data types in the code/data to mangle have been defined.");
case Undefined _ -> throw new Exception ("Encountered data marked \"undefined\" at " + loc + ". Ensure that all data types in the code/data to mangle have been defined.");
default -> throw new Exception ("Unknown type \"" + t.getClass().getName() + "\" at " + loc);
};
}
@ -446,7 +466,8 @@ public class MSVC7Mangle extends GhidraScript{
private static String mangleArgs(
final FunctionSignature f,
final List<String> dict
final List<String> dict,
final String loc
) throws Exception {
/* Mangle the arguments for a function */
final DataType[] args = Arrays.stream(f.getArguments())
@ -469,10 +490,15 @@ public class MSVC7Mangle extends GhidraScript{
// It turns out that academic-sounding stuff everyone
// freaks out at is actually useful (and Optional still
// helped us out here)
String mangled = "";
for (int i = 0; i < args.length; i++)
mangled += backref(args[i], argDict).orElse(mangleType(args[i], dict));
return mangled + (f.hasVarArgs() ? "Z" : "@");
String mangledArgs = "";
for (int i = 0; i < args.length; i++) {
final String mangledArg = mangleType(args[i], dict, loc);
mangledArgs += mangledArg.length() == 1 ?
mangledArg :
backref(args[i], argDict).orElse(mangledArg);
}
return mangledArgs + (f.hasVarArgs() ? "Z" : "@");
}
}
@ -495,7 +521,8 @@ public class MSVC7Mangle extends GhidraScript{
// vtable
if (ident.startsWith("?_7")) return "?" + ident + "6B@";
return "?" + ident + "3" + mangleType(d.getDataType(), dict) +
return "?" + ident + "3" +
mangleType(d.getDataType(), dict, "0x" + d.getAddress().toString()) +
"A";
}