diff --git a/core/relinker/relinker/src/analysis/SyscallScanner.cpp b/core/relinker/relinker/src/analysis/SyscallScanner.cpp index cffd899a5c..bbe6db0f4c 100644 --- a/core/relinker/relinker/src/analysis/SyscallScanner.cpp +++ b/core/relinker/relinker/src/analysis/SyscallScanner.cpp @@ -375,27 +375,7 @@ static std::vector DiscoverRoutines(const std::vector& pos += info.Length; } } - for (std::size_t pos = 0; pos < limit;) { - if (ContainsOffset(unwind, pos)) { - const auto after = std::upper_bound(unwind.begin(), unwind.end(), pos, [](const std::size_t value, const CodeRange& range) { return value < range.Begin; }); - if (after != unwind.begin()) - pos = static_cast(std::prev(after)->End); - continue; - } - Codegen::DecodedInstructionInfo info; - try { - info = decoder.DecodeInstruction(code.data() + pos, limit - pos); - } catch (const Codegen::CodegenException&) { - ++pos; - continue; - } - if (info.Length == 0 || info.Length > limit - pos) { - ++pos; - continue; - } - QueueInstructionTargets(pending, visited, unwind, info, code, pos, limit); - pos += info.Length; - } + std::set explored; while (!pending.empty()) { const auto root = pending.back(); diff --git a/core/relinker/relinker/tests/CodeDataScanningTests.cpp b/core/relinker/relinker/tests/CodeDataScanningTests.cpp index 8ef5d809be..b0b9a87d9f 100644 --- a/core/relinker/relinker/tests/CodeDataScanningTests.cpp +++ b/core/relinker/relinker/tests/CodeDataScanningTests.cpp @@ -98,16 +98,29 @@ void CheckNoUnwindIndirectJumpSyscall(Domain::FileByteOffset origin) { void CheckIndirectOnlyDynamicSyscall(Domain::FileByteOffset origin) { const Bytes code = { - 0xC3, - 0x0F, 0x78, - 0x90, 0x90, + 0xEB, 0x03, + 0x0F, 0x78, 0x00, 0x48, 0x8D, 0x05, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x8B, 0x07, 0x0F, 0x05, 0xC3, }; - CheckForbiddenInstruction(code, origin, 16, {}, {{0, 1}}); + CheckForbiddenInstruction(code, origin, 16, {}, {{0, 2}}); +} + +void CheckGapBranchToSyscall(Domain::FileByteOffset origin) { + const Bytes code = { + 0xC3, + 0xEB, 0x03, + 0x00, 0x00, 0x00, + 0x8B, 0x07, + 0x0F, 0x05, + 0xC3, + }; + TestProgress progress; + const auto scanner = Relinker::MakeSyscallScanner(progress); + Require(scanner->ScanCodeSectionForSyscalls(code, origin, code.size(), {}, {{0, 1}}).empty(), "Data gap branch falsely created a routine and rejected 0F 05"); } void CheckDirectCallAfterIndirectJump(Domain::FileByteOffset origin) { @@ -175,6 +188,7 @@ int main() { CheckDirectCallAfterIndirectJump(origin); CheckConditionalBranchAcrossGap(origin); CheckUnwindGapAndBoundedCallee(origin); + CheckGapBranchToSyscall(origin); } std::cout << "Embedded-code-data known-gap and reachable-code controls passed\n"; } catch (const std::exception& error) { diff --git a/docs/dev/debt/relinker.md b/docs/dev/debt/relinker.md index a29eea4a05..6243194829 100644 --- a/docs/dev/debt/relinker.md +++ b/docs/dev/debt/relinker.md @@ -6,7 +6,7 @@ - [Packed relative relocations](../../../core/relinker/relinker/src/parsing/RelrRelocations.cpp) require the table to fit in one file-backed PT_LOAD segment and each 8-byte storage unit to fit in one load segment. Units spanning load segments, overlapping RELR units and overlaps with RELA relocations are rejected; relocation composition is not implemented. -- The [syscall scanner](../../../core/relinker/relinker/src/analysis/SyscallScanner.cpp) discovers routine bounds from unwind ranges, direct branch targets, and address-taken `lea` targets ([#7547](https://github.com/boykopovar/AnyPS5/issues/7547)) so undecodable bytes in an inter-routine gap are not reported as an illegal instruction. Each unwind range is decoded from its start through its end, and direct branch and `lea` targets from it and from inter-routine gaps are followed until the next return, unconditional jump, indirect jump, or trap. Inside discovered routines, all syscalls are rejected, including dynamic RAX loads. In inter-routine gaps outside all discovered routines, embedded data does not trigger decode failures, and raw syscall bytes without a constant number are skipped and counted; stubs with constant syscall numbers in gaps remain detected. With no unwind ranges, discovery is not performed and every syscall byte pattern is rejected. +- The [syscall scanner](../../../core/relinker/relinker/src/analysis/SyscallScanner.cpp) discovers routine bounds from unwind ranges, direct branch targets, and address-taken `lea` targets ([#7547](https://github.com/boykopovar/AnyPS5/issues/7547)) so undecodable bytes in an inter-routine gap are not reported as an illegal instruction. Each unwind range is decoded from its start through its end, and direct branch and `lea` targets reached from it are followed until the next return, unconditional jump, indirect jump, or trap. Inside discovered routines, all syscalls are rejected, including dynamic RAX loads. In inter-routine gaps outside all discovered routines, embedded data does not trigger decode failures, and raw syscall bytes without a constant number are skipped and counted; stubs with constant syscall numbers in gaps remain detected. With no unwind ranges, discovery is not performed and every syscall byte pattern is rejected. - [Executable guest imports](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) - on Linux and macOS, a relocated executable import resolved to one guest provider requires that module and its dependencies at startup, even when it was discovered through `--module-dir` without a DT_NEEDED entry. Unreferenced symbols and no-op relocations do not require an otherwise deferred module. Console module-start ordering is unmeasured.