better analyzer and integrating sce-symbol-scanner (#130)

* feat: modularize elf analyzer
feat: added experimental sce symbol scanner
feat: change analyzer order
feat: small optimizations on analyzer

* feat: remove example_config.toml because its causing confusion on some people

* feat: embed sce symbol but leave optional import path
feat: killed skip function on analyzer but leave it so you can skip manual if you want

* feat: pin elfio tag

* feat: manually create string view with size

* feat: update ghidra script
This commit is contained in:
Ranieri
2026-06-06 23:37:12 -03:00
committed by GitHub
parent ed8b3ebee1
commit 7562ec14c9
21 changed files with 4137 additions and 1434 deletions
+20 -57
View File
@@ -1,10 +1,10 @@
#include "MiniTest.h"
#include "ps2recomp/elf_analyzer.h"
#include "ps2recomp/function_classifier.h"
#include "ps2recomp/instructions.h"
#include "ps2recomp/types.h"
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace ps2recomp;
@@ -47,9 +47,24 @@ void register_elf_analyzer_tests()
t.IsFalse(analyzer.isLibrarySymbolNameForHeuristics("bhEne13_Brain"),
"named game function should not be classified as library");
t.IsFalse(analyzer.isLibrarySymbolNameForHeuristics("ScenePrerender"),
"game functions beginning with Scene should not be classified as sce SDK APIs");
t.IsFalse(analyzer.isLibrarySymbolNameForHeuristics("sub_00100C00"),
"unreliable auto-generated names should not be classified as library"); });
tc.Run("runtime handler filter keeps unsupported SDK names informational", [](TestCase &t)
{
t.IsTrue(FunctionClassifier::hasRuntimeHandler("sceCdRead"),
"sceCdRead should resolve to a known runtime stub handler");
t.IsTrue(FunctionClassifier::hasRuntimeHandler("_printf"),
"runtime handler resolution should accept leading underscore aliases");
t.IsTrue(FunctionClassifier::hasRuntimeHandler("__ieee754_rem_pio2f"),
"double-underscore libm helpers should be active stubs only when the runtime knows them");
t.IsTrue(FunctionClassifier::hasRuntimeHandler("__kernel_cosf"),
"runtime-known libm kernel helpers should resolve exactly");
t.IsFalse(FunctionClassifier::hasRuntimeHandler("scePP1_Kick"),
"SDK functions without runtime handlers should not be active stubs"); });
tc.Run("reliable-symbol heuristic filters autogenerated names", [](TestCase &t)
{
t.IsTrue(ElfAnalyzer::isReliableSymbolNameForHeuristics("bhEne13_Brain"),
@@ -70,37 +85,6 @@ void register_elf_analyzer_tests()
t.IsFalse(ElfAnalyzer::isReliableSymbolNameForHeuristics("0x00100ABC"),
"pure hex-style symbol should be treated as unreliable"); });
tc.Run("system-symbol heuristic is strict to system patterns", [](TestCase &t)
{
t.IsTrue(ElfAnalyzer::isSystemSymbolNameForHeuristics("__main"),
"__main should be classified as system");
t.IsTrue(ElfAnalyzer::isSystemSymbolNameForHeuristics("_start"),
"_start should be classified as system");
t.IsTrue(ElfAnalyzer::isSystemSymbolNameForHeuristics(".text.startup"),
".text.* should be classified as system");
t.IsFalse(ElfAnalyzer::isSystemSymbolNameForHeuristics("bhObj001"),
"game symbol should not be classified as system");
t.IsFalse(ElfAnalyzer::isSystemSymbolNameForHeuristics("SetupSoundDriver"),
"engine/game symbol should not be classified as system");
t.IsFalse(ElfAnalyzer::isSystemSymbolNameForHeuristics("sub_00100C00"),
"unreliable names should not be considered system by this classifier"); });
tc.Run("system skip keeps forced entry names recompiled", [](TestCase &t)
{
std::unordered_set<std::string> forcedNames{"_start", "_init"};
t.IsFalse(ElfAnalyzer::shouldSkipSystemSymbolForHeuristics("_start", forcedNames),
"forced entry name _start should not be skipped");
t.IsFalse(ElfAnalyzer::shouldSkipSystemSymbolForHeuristics("_init", forcedNames),
"forced entry name _init should not be skipped");
t.IsTrue(ElfAnalyzer::shouldSkipSystemSymbolForHeuristics("__main", forcedNames),
"system symbol not marked as forced should still be skipped");
t.IsTrue(ElfAnalyzer::shouldSkipSystemSymbolForHeuristics("__divdi3", {}),
"compiler helper __divdi3 should be skippable as system/runtime");
t.IsFalse(ElfAnalyzer::shouldSkipSystemSymbolForHeuristics("ps2___divdi3", {}),
"generated ps2_ wrapper names should not be treated as system"); });
tc.Run("entry-point mapping handles exact inside and fallback", [](TestCase &t)
{
Function f1;
@@ -138,7 +122,7 @@ void register_elf_analyzer_tests()
t.Equals(ElfAnalyzer::findFallbackEntryFunctionIndexForHeuristics(fallbackOnly), 0,
"fallback should also accept 0x80100000"); });
tc.Run("signal-based skip heuristics keep reliable names and skip unreliable/system", [](TestCase &t)
tc.Run("risk signal detection reports hardware io mmi and self modifying code", [](TestCase &t)
{
// Hardware I/O signal via LUI upper address in I/O region.
Instruction hw = makeInstruction(0x1000, OPCODE_LUI);
@@ -173,30 +157,9 @@ void register_elf_analyzer_tests()
const bool hasSelfModifying = ElfAnalyzer::hasSelfModifyingSignalForHeuristics(smcInst, sections);
t.IsTrue(hasSelfModifying, "self-modifying signal should be detected");
// Decision behavior by name reliability/system-ness.
t.IsFalse(hasHardwareIO && ElfAnalyzer::shouldAutoSkipNameForHeuristics("bhEne13_Brain"),
"reliable game symbol should not auto-skip from hardware signal alone");
t.IsTrue(hasHardwareIO && ElfAnalyzer::shouldAutoSkipNameForHeuristics("sub_00100C00"),
"unreliable symbol should auto-skip when risky signals exist");
t.IsTrue(hasLargeComplexMMI && ElfAnalyzer::shouldAutoSkipNameForHeuristics("__main"),
"system symbol should auto-skip when risky signals exist");
t.IsFalse(hasSelfModifying && ElfAnalyzer::shouldAutoSkipNameForHeuristics("topThread"),
"do-not-skip list should override auto-skip"); });
tc.Run("patch-density threshold behavior", [](TestCase &t)
{
t.IsTrue(ElfAnalyzer::shouldSkipForPatchDensityForHeuristics("sub_00100C00", 100, 6, false),
"high-density patches on unreliable names should skip");
t.IsFalse(ElfAnalyzer::shouldSkipForPatchDensityForHeuristics("sub_00100C00", 200, 6, false),
"density below threshold should not skip");
t.IsFalse(ElfAnalyzer::shouldSkipForPatchDensityForHeuristics("sub_00100C00", 100, 5, false),
"patch count <= 5 should not skip");
t.IsFalse(ElfAnalyzer::shouldSkipForPatchDensityForHeuristics("printf", 100, 6, true),
"library functions should not be auto-skipped by patch density");
t.IsFalse(ElfAnalyzer::shouldSkipForPatchDensityForHeuristics("bhEne13_Brain", 100, 6, false),
"reliable game function should not be auto-skipped by patch density");
t.IsFalse(ElfAnalyzer::shouldSkipForPatchDensityForHeuristics("topThread", 100, 6, false),
"do-not-skip names should never be auto-skipped"); });
(void)hasHardwareIO;
(void)hasLargeComplexMMI;
(void)hasSelfModifying; });
tc.Run("jump-table detection finds canonical sltiu/bne/lw/jr pattern", [](TestCase &t)
{