Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ps2xRecomp/include/ps2recomp/elf_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace ps2recomp
bool m_hasLoadedGhidraMap = false;
RecompilerReporter *m_reporter = nullptr;
std::unordered_set<uint32_t> m_ghidraMapStarts;
std::unordered_map<uint32_t, uint32_t> m_ghidraMapEnds;

void loadSections();
void loadSymbols();
Expand Down
13 changes: 11 additions & 2 deletions ps2xRecomp/src/lib/elf_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ namespace ps2recomp
// Named debug/map functions with explicit bounds are authoritative too.
for (const auto &extra : m_extraFunctions)
{
if (extra.start == 0 || extra.end <= extra.start || extra.name.empty() || IsAutoGeneratedName(extra.name))
if (extra.start == 0 || extra.end <= extra.start || extra.name.empty() ||
(IsAutoGeneratedName(extra.name) && !m_ghidraMapStarts.contains(extra.start)))
{
continue;
}
Expand All @@ -614,7 +615,12 @@ namespace ps2recomp
continue;
}

const uint32_t clampedEnd = ClampFunctionEndToSection(functionSection, extra.start, extra.end);
uint32_t clampedEnd = ClampFunctionEndToSection(functionSection, extra.start, extra.end);
auto ghidraEndIt = m_ghidraMapEnds.find(extra.start);
if (ghidraEndIt != m_ghidraMapEnds.end())
{
clampedEnd = ghidraEndIt->second;
}
if (clampedEnd <= extra.start)
{
continue;
Expand Down Expand Up @@ -965,6 +971,7 @@ namespace ps2recomp

m_hasLoadedGhidraMap = false;
m_ghidraMapStarts.clear();
m_ghidraMapEnds.clear();

std::ifstream file(mapPath);
if (!file.is_open())
Expand Down Expand Up @@ -1031,6 +1038,7 @@ namespace ps2recomp

m_extraFunctions.push_back(std::move(func));
mapStarts.insert(start);
m_ghidraMapEnds[start] = end;
count++;
}
catch (...)
Expand Down Expand Up @@ -1350,6 +1358,7 @@ namespace ps2recomp
m_extraFunctions.clear();
m_hasLoadedGhidraMap = false;
m_ghidraMapStarts.clear();
m_ghidraMapEnds.clear();

if (HasDwarfSections(*m_elf))
{
Expand Down
58 changes: 58 additions & 0 deletions ps2xTest/src/ps2_recompiler_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,64 @@ void register_ps2_recompiler_tests()
std::filesystem::remove(mapPath, removeError);
});

tc.Run("ghidra map end clamps a same-start auto function", [](TestCase &t) {
const auto uniqueSuffix = std::to_string(
static_cast<unsigned long long>(std::chrono::steady_clock::now().time_since_epoch().count()));
const std::filesystem::path elfPath =
std::filesystem::temp_directory_path() / ("ps2recomp-ghidra-clamp-" + uniqueSuffix + ".elf");
const std::filesystem::path mapPath =
std::filesystem::temp_directory_path() / ("ps2recomp-ghidra-clamp-" + uniqueSuffix + ".csv");

const bool writeOk = writeMinimalMipsElfWithJalFallbackTarget(elfPath);
t.IsTrue(writeOk, "temporary ELF should be generated");
if (!writeOk)
{
return;
}

ElfParser parser(elfPath.string());
const bool parseOk = parser.parse();
t.IsTrue(parseOk, "generated ELF should parse");
if (!parseOk)
{
std::error_code removeError;
std::filesystem::remove(elfPath, removeError);
return;
}

std::ofstream mapFile(mapPath);
t.IsTrue(static_cast<bool>(mapFile), "ghidra map file should be writable");
if (!mapFile)
{
std::error_code removeError;
std::filesystem::remove(elfPath, removeError);
return;
}
mapFile << "name,start,end,size\n";
mapFile << "FUN_00100010,0x00100010,0x00100014,0x4\n";
mapFile.close();

const bool mapLoaded = parser.loadGhidraFunctionMap(mapPath.string());
t.IsTrue(mapLoaded, "ghidra map should load");

const auto functions = parser.extractFunctions();
const auto clampedIt = std::find_if(
functions.begin(), functions.end(),
[](const Function &fn)
{ return fn.start == 0x00100010u; });
t.IsTrue(clampedIt != functions.end(),
"same-start function should survive the ghidra map merge");
if (clampedIt != functions.end())
{
t.Equals(clampedIt->end, 0x00100014u,
"ghidra end should clamp the analyzer's wider span");
}

std::error_code removeError;
std::filesystem::remove(elfPath, removeError);
std::filesystem::remove(mapPath, removeError);
});

tc.Run("runtime call resolution includes Veronica compatibility aliases", [](TestCase &t) {
t.Equals(ps2_runtime_calls::resolveSyscallName("ReleaseAlarm"), std::string_view{"ReleaseAlarm"},
"ReleaseAlarm should resolve as a syscall name");
Expand Down