diff --git a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp index 0b009c39f..a2ba2e2f2 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 @@ -173,6 +175,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 +184,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 +235,47 @@ 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 || 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; + } + } + 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; + if (decoder.DecodeInstruction(bytes.data() + base, bytes.size() - base).SegmentPrefix == 0x64) spanHasFsPrefix = true; + 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..825e7f543 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -235,6 +235,14 @@ def main(): overlapping = make_image("register", "unwind") overlapping[0x1200:0x1205] = b"\xe9" + struct.pack("