From 52587f8cf100dc42847a8ca04e80d5d0021066ed Mon Sep 17 00:00:00 2001 From: Pouare514 Date: Fri, 2 Oct 2026 01:30:56 +0200 Subject: [PATCH 1/3] fix(relinker): tolerate trailing data in TLS scan --- .../src/analysis/CodeInstructionCollector.cpp | 58 +++++++++++++++++-- .../tests/test_tls_function_coverage.py | 2 +- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp index 0b009c39f..d87d8eea8 100644 --- a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp +++ b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp @@ -173,6 +173,8 @@ std::set CodeInstructionCollector::Collect(const std::ve if (roots.empty()) throw Domain::RelinkerException("Code analysis: no code entry points"); std::set instructions; const Codegen::X64InstructionDecoder decoder; + std::vector> skipped; + std::set staticTargets; for (const auto& [begin, end] : functions) { const auto offset = fileOffset(begin, end - begin); for (auto address = begin; address < end;) { @@ -180,16 +182,36 @@ std::set CodeInstructionCollector::Collect(const std::ve try { info = decoder.DecodeInstruction(bytes.data() + offset + address - begin, end - address); } catch (const Codegen::CodegenException& error) { - throw Domain::RelinkerException(std::string("Code analysis: ") + error.what(), address); + const auto tailBytes = end - address; + bool tailHasFsPrefix = false; + for (auto probe = address; probe < end; ++probe) { + if (bytes[offset + probe - begin] == 0x64) { + tailHasFsPrefix = true; + break; + } + } + if (tailBytes > 15 || tailHasFsPrefix) + throw Domain::RelinkerException(std::string("Code analysis: ") + error.what(), address); + skipped.emplace_back(address, end); + break; } if (info.Length == 0 || info.Length > end - address) throw Domain::RelinkerException("Code analysis: instruction crosses function boundary", address); instructions.insert(address); - if (info.HasBranchTarget && !info.HasRipRelativeDisp) - addRoot(address + info.Length + static_cast(info.BranchDisp)); + if (info.HasBranchTarget && !info.HasRipRelativeDisp) { + const auto target = address + info.Length + static_cast(info.BranchDisp); + staticTargets.insert(target); + addRoot(target); + } address += info.Length; } } + for (const auto target : staticTargets) { + for (const auto& [skipBegin, skipEnd] : skipped) { + if (skipBegin <= target && target < skipEnd) + throw Domain::RelinkerException("Code analysis: branch into skipped range tail", target); + } + } std::size_t previousRoots = 0; do { previousRoots = roots.size(); @@ -211,7 +233,35 @@ std::set CodeInstructionCollector::Collect(const std::ve } while (roots.size() != previousRoots); std::uint64_t previousEnd = 0; for (const auto address : instructions) { - if (address < previousEnd) throw Domain::RelinkerException("Code analysis: overlapping instruction boundaries", address); + if (address < previousEnd) { + std::uint64_t spanEnd = previousEnd; + try { + for (const auto& header : headers) { + if (header.Type != 1 || (header.Flags & 1) == 0 || address < header.MappedAddress || address - header.MappedAddress >= header.FileSize) continue; + const auto offset = address - header.MappedAddress; + const auto length = decoder.DecodeInstruction(bytes.data() + header.Offset + offset, header.FileSize - offset).Length; + if (length == 0 || length > header.FileSize - offset) break; + spanEnd = std::max(spanEnd, address + length); + break; + } + } catch (const Codegen::CodegenException&) { + } + bool spanHasFsPrefix = false; + for (const auto& header : headers) { + if (header.Type != 1 || (header.Flags & 1) == 0 || previousEnd < header.MappedAddress || previousEnd - header.MappedAddress >= header.FileSize) continue; + const auto base = header.Offset + previousEnd - header.MappedAddress; + for (auto probe = base; probe < base + (spanEnd - previousEnd) && probe < bytes.size(); ++probe) { + if (bytes[probe] == 0x64) { + spanHasFsPrefix = true; + break; + } + } + break; + } + if (spanHasFsPrefix) throw Domain::RelinkerException("Code analysis: overlapping instruction boundaries", address); + previousEnd = spanEnd; + continue; + } for (const auto& header : headers) { if (header.Type != 1 || (header.Flags & 1) == 0 || address < header.MappedAddress || address - header.MappedAddress >= header.FileSize) continue; const auto offset = address - header.MappedAddress; diff --git a/core/relinker/relinker/tests/test_tls_function_coverage.py b/core/relinker/relinker/tests/test_tls_function_coverage.py index 31606aa11..2c3623e8b 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -234,7 +234,7 @@ def main(): convert(metadata + "-outside", make_image("register", metadata, 0x1000), "Code analysis: function exceeds executable segment") overlapping = make_image("register", "unwind") overlapping[0x1200:0x1205] = b"\xe9" + struct.pack(" Date: Sun, 4 Oct 2026 15:41:17 +0200 Subject: [PATCH 2/3] fix(relinker): scan overlapping span from address and cover FS-prefixed previous instruction --- .../src/analysis/CodeInstructionCollector.cpp | 26 ++++++++++++++++--- .../tests/test_tls_function_coverage.py | 6 ++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp index d87d8eea8..27b580af7 100644 --- a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp +++ b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -248,9 +250,9 @@ std::set CodeInstructionCollector::Collect(const std::ve } bool spanHasFsPrefix = false; for (const auto& header : headers) { - if (header.Type != 1 || (header.Flags & 1) == 0 || previousEnd < header.MappedAddress || previousEnd - header.MappedAddress >= header.FileSize) continue; - const auto base = header.Offset + previousEnd - header.MappedAddress; - for (auto probe = base; probe < base + (spanEnd - previousEnd) && probe < bytes.size(); ++probe) { + if (header.Type != 1 || (header.Flags & 1) == 0 || address < header.MappedAddress || address - header.MappedAddress >= header.FileSize) continue; + const auto base = header.Offset + address - header.MappedAddress; + for (auto probe = base; probe < base + (spanEnd - address) && probe < bytes.size(); ++probe) { if (bytes[probe] == 0x64) { spanHasFsPrefix = true; break; @@ -258,6 +260,24 @@ std::set CodeInstructionCollector::Collect(const std::ve } break; } + if (!spanHasFsPrefix) { + const auto previous = instructions.lower_bound(address); + if (previous != instructions.begin()) { + const auto previousStart = *std::prev(previous); + for (const auto& header : headers) { + if (header.Type != 1 || (header.Flags & 1) == 0 || previousStart < header.MappedAddress || previousStart - header.MappedAddress >= header.FileSize) continue; + const auto base = header.Offset + previousStart - header.MappedAddress; + const auto prefixEnd = std::min({base + 4, base + (previousEnd - previousStart), bytes.size()}); + for (auto probe = base; probe < prefixEnd; ++probe) { + if (bytes[probe] == 0x64) { + spanHasFsPrefix = true; + break; + } + } + break; + } + } + } if (spanHasFsPrefix) throw Domain::RelinkerException("Code analysis: overlapping instruction boundaries", address); previousEnd = spanEnd; continue; diff --git a/core/relinker/relinker/tests/test_tls_function_coverage.py b/core/relinker/relinker/tests/test_tls_function_coverage.py index 2c3623e8b..a16497c73 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -234,7 +234,11 @@ def main(): convert(metadata + "-outside", make_image("register", metadata, 0x1000), "Code analysis: function exceeds executable segment") overlapping = make_image("register", "unwind") overlapping[0x1200:0x1205] = b"\xe9" + struct.pack(" Date: Mon, 5 Oct 2026 00:56:11 +0200 Subject: [PATCH 3/3] fix(relinker): detect FS-prefixed previous instruction via SegmentPrefix decode --- core/libs/prx/libSceAudioIn/Export.cpp | 2 +- .../relinker/src/analysis/CodeInstructionCollector.cpp | 8 +------- .../relinker/relinker/tests/test_tls_function_coverage.py | 4 ++++ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceAudioIn/Export.cpp b/core/libs/prx/libSceAudioIn/Export.cpp index 3c31aaa40..cdd232455 100644 --- a/core/libs/prx/libSceAudioIn/Export.cpp +++ b/core/libs/prx/libSceAudioIn/Export.cpp @@ -122,7 +122,7 @@ int APS5_VABI sceAudioInInput(int handle, void* dest) { if (device != 0) { captured = capture(device, out, bytes, deadline); } else { - std::this_thread::sleep_until(wake); + while (Clock::now() < wake) std::this_thread::sleep_until(wake); } std::memset(out + captured, 0, bytes - captured); lock.lock(); diff --git a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp index 27b580af7..a2ba2e2f2 100644 --- a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp +++ b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp @@ -267,13 +267,7 @@ std::set CodeInstructionCollector::Collect(const std::ve for (const auto& header : headers) { if (header.Type != 1 || (header.Flags & 1) == 0 || previousStart < header.MappedAddress || previousStart - header.MappedAddress >= header.FileSize) continue; const auto base = header.Offset + previousStart - header.MappedAddress; - const auto prefixEnd = std::min({base + 4, base + (previousEnd - previousStart), bytes.size()}); - for (auto probe = base; probe < prefixEnd; ++probe) { - if (bytes[probe] == 0x64) { - spanHasFsPrefix = true; - break; - } - } + if (decoder.DecodeInstruction(bytes.data() + base, bytes.size() - base).SegmentPrefix == 0x64) spanHasFsPrefix = true; break; } } diff --git a/core/relinker/relinker/tests/test_tls_function_coverage.py b/core/relinker/relinker/tests/test_tls_function_coverage.py index a16497c73..825e7f543 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -239,6 +239,10 @@ def main(): fs_overlap[0x1209:0x1211] = bytes.fromhex("48 8b 04 25 64 00 00 00") fs_overlap[0x1211:0x1216] = b"\xe8" + struct.pack("