mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
fix: reslice entry functions without container (#70)
This commit is contained in:
committed by
GitHub
parent
ea8283d034
commit
d7701f9ea7
@@ -36,6 +36,9 @@ namespace ps2recomp
|
||||
std::vector<Function> &functions,
|
||||
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions,
|
||||
const std::vector<Section> §ions);
|
||||
static size_t ResliceEntryFunctions(
|
||||
std::vector<Function> &functions,
|
||||
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions);
|
||||
|
||||
private:
|
||||
ConfigManager m_configManager;
|
||||
|
||||
@@ -511,6 +511,171 @@ namespace ps2recomp
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
bool isEntryFunctionName(const std::string &name)
|
||||
{
|
||||
return name.rfind("entry_", 0) == 0;
|
||||
}
|
||||
|
||||
size_t resliceEntryFunctionsImpl(
|
||||
std::vector<Function> &functions,
|
||||
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions)
|
||||
{
|
||||
std::vector<uint32_t> boundaryStarts;
|
||||
boundaryStarts.reserve(functions.size());
|
||||
for (const auto &function : functions)
|
||||
{
|
||||
if (!function.isRecompiled || function.isStub || function.isSkipped)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
boundaryStarts.push_back(function.start);
|
||||
}
|
||||
|
||||
std::sort(boundaryStarts.begin(), boundaryStarts.end());
|
||||
boundaryStarts.erase(std::unique(boundaryStarts.begin(), boundaryStarts.end()), boundaryStarts.end());
|
||||
|
||||
auto findContainingFunction = [&](uint32_t address) -> const Function *
|
||||
{
|
||||
const Function *best = nullptr;
|
||||
for (const auto &function : functions)
|
||||
{
|
||||
if (!function.isRecompiled || function.isStub || function.isSkipped)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isEntryFunctionName(function.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (address < function.start || address >= function.end)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto decodedIt = decodedFunctions.find(function.start);
|
||||
if (decodedIt == decodedFunctions.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &decoded = decodedIt->second;
|
||||
const bool hasAddress = std::any_of(decoded.begin(), decoded.end(),
|
||||
[&](const Instruction &candidate)
|
||||
{ return candidate.address == address; });
|
||||
if (!hasAddress)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!best || function.start > best->start)
|
||||
{
|
||||
best = &function;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
};
|
||||
|
||||
size_t reslicedCount = 0;
|
||||
|
||||
for (auto &function : functions)
|
||||
{
|
||||
if (!function.isRecompiled || function.isStub || function.isSkipped)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isEntryFunctionName(function.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const Function *containingFunction = findContainingFunction(function.start);
|
||||
uint32_t sliceEndAddress = containingFunction ? containingFunction->end : function.end;
|
||||
auto nextIt = std::upper_bound(boundaryStarts.begin(), boundaryStarts.end(), function.start);
|
||||
if (nextIt != boundaryStarts.end() && *nextIt < sliceEndAddress)
|
||||
{
|
||||
sliceEndAddress = *nextIt;
|
||||
}
|
||||
|
||||
if (sliceEndAddress <= function.start)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<Instruction> *sourceInstructions = nullptr;
|
||||
if (containingFunction)
|
||||
{
|
||||
auto containingDecodedIt = decodedFunctions.find(containingFunction->start);
|
||||
if (containingDecodedIt == decodedFunctions.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
sourceInstructions = &containingDecodedIt->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto entryDecodedIt = decodedFunctions.find(function.start);
|
||||
if (entryDecodedIt == decodedFunctions.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
sourceInstructions = &entryDecodedIt->second;
|
||||
}
|
||||
|
||||
auto sliceBeginIt = std::find_if(sourceInstructions->begin(), sourceInstructions->end(),
|
||||
[&](const Instruction &candidate)
|
||||
{ return candidate.address == function.start; });
|
||||
if (sliceBeginIt == sourceInstructions->end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sliceEndIt = std::find_if(sliceBeginIt, sourceInstructions->end(),
|
||||
[&](const Instruction &candidate)
|
||||
{ return candidate.address >= sliceEndAddress; });
|
||||
if (sliceEndIt == sliceBeginIt)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<Instruction> slicedInstructions(sliceBeginIt, sliceEndIt);
|
||||
bool changed = (function.end != sliceEndAddress);
|
||||
auto existingIt = decodedFunctions.find(function.start);
|
||||
if (existingIt == decodedFunctions.end())
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
else if (!changed)
|
||||
{
|
||||
const auto &existing = existingIt->second;
|
||||
if (existing.size() != slicedInstructions.size())
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
else if (!existing.empty())
|
||||
{
|
||||
if (existing.front().address != slicedInstructions.front().address ||
|
||||
existing.back().address != slicedInstructions.back().address)
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function.end = sliceEndAddress;
|
||||
decodedFunctions[function.start] = std::move(slicedInstructions);
|
||||
|
||||
if (changed)
|
||||
{
|
||||
++reslicedCount;
|
||||
}
|
||||
}
|
||||
|
||||
return reslicedCount;
|
||||
}
|
||||
}
|
||||
|
||||
PS2Recompiler::PS2Recompiler(const std::string &configPath)
|
||||
@@ -1088,6 +1253,13 @@ namespace ps2recomp
|
||||
<< " additional entry point(s) inside existing functions across "
|
||||
<< stats.passCount << " pass(es)." << std::endl;
|
||||
}
|
||||
|
||||
const size_t reslicedCount = resliceEntryFunctionsImpl(m_functions, m_decodedFunctions);
|
||||
if (reslicedCount > 0)
|
||||
{
|
||||
std::cout << "Resliced " << reslicedCount
|
||||
<< " entry function(s) after discovery." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool PS2Recompiler::decodeFunction(Function &function)
|
||||
@@ -1291,6 +1463,13 @@ namespace ps2recomp
|
||||
return stats.discoveredCount;
|
||||
}
|
||||
|
||||
size_t PS2Recompiler::ResliceEntryFunctions(
|
||||
std::vector<Function> &functions,
|
||||
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions)
|
||||
{
|
||||
return resliceEntryFunctionsImpl(functions, decodedFunctions);
|
||||
}
|
||||
|
||||
StubTarget PS2Recompiler::resolveStubTarget(const std::string &name)
|
||||
{
|
||||
if (!ps2_runtime_calls::resolveSyscallName(name).empty())
|
||||
|
||||
@@ -120,6 +120,148 @@ void register_ps2_recompiler_tests()
|
||||
}
|
||||
});
|
||||
|
||||
tc.Run("entry reslice trims earlier entries after late discovery", [](TestCase &t) {
|
||||
std::vector<Function> functions = {
|
||||
makeFunction("container", 0x1000u, 0x1018u),
|
||||
makeFunction("entry_1008", 0x1008u, 0x1018u),
|
||||
makeFunction("entry_100c", 0x100Cu, 0x1018u)
|
||||
};
|
||||
|
||||
std::unordered_map<uint32_t, std::vector<Instruction>> decodedFunctions;
|
||||
decodedFunctions[0x1000u] = {
|
||||
makeNopLike(0x1000u),
|
||||
makeNopLike(0x1004u),
|
||||
makeNopLike(0x1008u),
|
||||
makeNopLike(0x100Cu),
|
||||
makeNopLike(0x1010u),
|
||||
makeNopLike(0x1014u)
|
||||
};
|
||||
decodedFunctions[0x1008u] = {
|
||||
makeNopLike(0x1008u),
|
||||
makeNopLike(0x100Cu),
|
||||
makeNopLike(0x1010u),
|
||||
makeNopLike(0x1014u)
|
||||
};
|
||||
decodedFunctions[0x100Cu] = {
|
||||
makeNopLike(0x100Cu),
|
||||
makeNopLike(0x1010u),
|
||||
makeNopLike(0x1014u)
|
||||
};
|
||||
|
||||
size_t resliced = PS2Recompiler::ResliceEntryFunctions(functions, decodedFunctions);
|
||||
t.Equals(resliced, static_cast<size_t>(1),
|
||||
"expected only the earlier entry to be resliced");
|
||||
|
||||
auto findByStart = [&](uint32_t start) -> const Function* {
|
||||
auto it = std::find_if(functions.begin(), functions.end(),
|
||||
[&](const Function &fn) { return fn.start == start; });
|
||||
if (it == functions.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return &(*it);
|
||||
};
|
||||
|
||||
const Function *entry1008 = findByStart(0x1008u);
|
||||
const Function *entry100C = findByStart(0x100Cu);
|
||||
t.IsNotNull(entry1008, "entry at 0x1008 should exist");
|
||||
t.IsNotNull(entry100C, "entry at 0x100C should exist");
|
||||
if (entry1008)
|
||||
{
|
||||
t.Equals(entry1008->end, 0x100Cu,
|
||||
"entry 0x1008 should be trimmed to next entry start");
|
||||
}
|
||||
if (entry100C)
|
||||
{
|
||||
t.Equals(entry100C->end, 0x1018u,
|
||||
"entry 0x100C should still end at containing end");
|
||||
}
|
||||
|
||||
auto decoded1008It = decodedFunctions.find(0x1008u);
|
||||
auto decoded100CIt = decodedFunctions.find(0x100Cu);
|
||||
t.IsTrue(decoded1008It != decodedFunctions.end(), "decoded slice for 0x1008 should exist");
|
||||
t.IsTrue(decoded100CIt != decodedFunctions.end(), "decoded slice for 0x100C should exist");
|
||||
if (decoded1008It != decodedFunctions.end())
|
||||
{
|
||||
t.Equals(decoded1008It->second.size(), static_cast<size_t>(1),
|
||||
"entry 0x1008 slice should stop before 0x100C");
|
||||
if (!decoded1008It->second.empty())
|
||||
{
|
||||
t.Equals(decoded1008It->second.front().address, 0x1008u,
|
||||
"entry 0x1008 slice should begin at 0x1008");
|
||||
}
|
||||
}
|
||||
if (decoded100CIt != decodedFunctions.end())
|
||||
{
|
||||
t.Equals(decoded100CIt->second.size(), static_cast<size_t>(3),
|
||||
"entry 0x100C slice should keep remaining instructions");
|
||||
}
|
||||
});
|
||||
|
||||
tc.Run("entry reslice handles entries without containing function", [](TestCase &t) {
|
||||
std::vector<Function> functions = {
|
||||
makeFunction("entry_1008", 0x1008u, 0x1018u),
|
||||
makeFunction("entry_100c", 0x100Cu, 0x1018u)
|
||||
};
|
||||
|
||||
std::unordered_map<uint32_t, std::vector<Instruction>> decodedFunctions;
|
||||
decodedFunctions[0x1008u] = {
|
||||
makeNopLike(0x1008u),
|
||||
makeNopLike(0x100Cu),
|
||||
makeNopLike(0x1010u),
|
||||
makeNopLike(0x1014u)
|
||||
};
|
||||
decodedFunctions[0x100Cu] = {
|
||||
makeNopLike(0x100Cu),
|
||||
makeNopLike(0x1010u),
|
||||
makeNopLike(0x1014u)
|
||||
};
|
||||
|
||||
size_t resliced = PS2Recompiler::ResliceEntryFunctions(functions, decodedFunctions);
|
||||
t.Equals(resliced, static_cast<size_t>(1),
|
||||
"expected only the earlier entry to be resliced");
|
||||
|
||||
auto findByStart = [&](uint32_t start) -> const Function* {
|
||||
auto it = std::find_if(functions.begin(), functions.end(),
|
||||
[&](const Function &fn) { return fn.start == start; });
|
||||
if (it == functions.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return &(*it);
|
||||
};
|
||||
|
||||
const Function *entry1008 = findByStart(0x1008u);
|
||||
const Function *entry100C = findByStart(0x100Cu);
|
||||
t.IsNotNull(entry1008, "entry at 0x1008 should exist");
|
||||
t.IsNotNull(entry100C, "entry at 0x100C should exist");
|
||||
if (entry1008)
|
||||
{
|
||||
t.Equals(entry1008->end, 0x100Cu,
|
||||
"entry 0x1008 should be trimmed to next entry start");
|
||||
}
|
||||
if (entry100C)
|
||||
{
|
||||
t.Equals(entry100C->end, 0x1018u,
|
||||
"entry 0x100C should keep original end");
|
||||
}
|
||||
|
||||
auto decoded1008It = decodedFunctions.find(0x1008u);
|
||||
auto decoded100CIt = decodedFunctions.find(0x100Cu);
|
||||
t.IsTrue(decoded1008It != decodedFunctions.end(), "decoded slice for 0x1008 should exist");
|
||||
t.IsTrue(decoded100CIt != decodedFunctions.end(), "decoded slice for 0x100C should exist");
|
||||
if (decoded1008It != decodedFunctions.end())
|
||||
{
|
||||
t.Equals(decoded1008It->second.size(), static_cast<size_t>(1),
|
||||
"entry 0x1008 slice should stop before 0x100C");
|
||||
}
|
||||
if (decoded100CIt != decodedFunctions.end())
|
||||
{
|
||||
t.Equals(decoded100CIt->second.size(), static_cast<size_t>(3),
|
||||
"entry 0x100C slice should keep remaining instructions");
|
||||
}
|
||||
});
|
||||
|
||||
tc.Run("non-executable section targets are ignored", [](TestCase &t) {
|
||||
std::vector<Section> sections = {
|
||||
{".text", 0x1000u, 0x2000u, 0u, true, false, false, true, nullptr},
|
||||
|
||||
Reference in New Issue
Block a user