perf(relinker): use a flat sorted call-site index
This commit is contained in:
parent
59584d58b1
commit
3687b2e214
|
|
@ -161,6 +161,22 @@ if(BUILD_TESTING)
|
|||
add_test(NAME elf_reader_bounds COMMAND elf_reader_bounds_tests)
|
||||
set_tests_properties(elf_reader_bounds PROPERTIES TIMEOUT 20)
|
||||
|
||||
add_test_executable(call_site_index_tests
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/CallSiteIndexTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/CallSiteResolver.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/InstructionScanner.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/x86/X64InstructionDecoder.cpp
|
||||
)
|
||||
target_include_directories(call_site_index_tests PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/domain/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/codegen/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/io/include
|
||||
)
|
||||
add_test(NAME call_site_index COMMAND call_site_index_tests)
|
||||
set_tests_properties(call_site_index PROPERTIES TIMEOUT 20)
|
||||
|
||||
add_test_executable(autorun_exit_tests
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cli/tests/AutorunExitTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cli/src/Autorun.cpp
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace Relinker {
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ public:
|
|||
std::vector<FileByteOffset> ResolveCallSites(const std::vector<std::uint8_t>& textSection, FileByteOffset textSectionVAddr, VirtualAddress targetGotOrPltAddress, ByteCount targetGotOrPltSize) override;
|
||||
private:
|
||||
const std::vector<std::uint8_t>* _cachedTextPtr = nullptr;
|
||||
std::multimap<VirtualAddress, FileByteOffset> _cachedTargetToInstr;
|
||||
std::vector<std::pair<VirtualAddress, FileByteOffset>> _cachedTargetToInstr;
|
||||
FileByteOffset _cachedTextVAddr = 0;
|
||||
};
|
||||
|
||||
|
|
@ -82,6 +83,10 @@ std::vector<FileByteOffset> CallSiteResolver::ResolveCallSites(const std::vector
|
|||
auto scanner = Codegen::MakeInstructionScanner();
|
||||
auto instructions = scanner->ScanCodeSection(textSection, 0, textSection.size());
|
||||
_cachedTargetToInstr.clear();
|
||||
const auto referenceCount = std::count_if(instructions.begin(), instructions.end(), [&](const auto& match) {
|
||||
return isRipRelativeFF(textSection, match) || isRipRelativeMov64(textSection, match);
|
||||
});
|
||||
_cachedTargetToInstr.reserve(static_cast<std::size_t>(referenceCount));
|
||||
for (const auto& m : instructions) {
|
||||
std::size_t dispOff = 0;
|
||||
if (isRipRelativeFF(textSection, m))
|
||||
|
|
@ -94,15 +99,20 @@ std::vector<FileByteOffset> CallSiteResolver::ResolveCallSites(const std::vector
|
|||
std::int32_t disp = readDisp32(textSection, dispOff);
|
||||
VirtualAddress instrEnd = textSectionVAddr + m.Offset + static_cast<VirtualAddress>(m.Length);
|
||||
VirtualAddress target = static_cast<VirtualAddress>(static_cast<std::int64_t>(instrEnd) + disp);
|
||||
_cachedTargetToInstr.emplace(target, m.Offset);
|
||||
_cachedTargetToInstr.emplace_back(target, m.Offset);
|
||||
}
|
||||
std::sort(_cachedTargetToInstr.begin(), _cachedTargetToInstr.end());
|
||||
_cachedTextPtr = &textSection;
|
||||
_cachedTextVAddr = textSectionVAddr;
|
||||
}
|
||||
|
||||
std::vector<FileByteOffset> sites;
|
||||
auto lower = _cachedTargetToInstr.lower_bound(targetGotOrPltAddress);
|
||||
auto upper = _cachedTargetToInstr.lower_bound(targetGotOrPltAddress + targetGotOrPltSize);
|
||||
const auto findTarget = [&](VirtualAddress target) {
|
||||
return std::lower_bound(_cachedTargetToInstr.begin(), _cachedTargetToInstr.end(),
|
||||
std::pair<VirtualAddress, FileByteOffset>{target, 0});
|
||||
};
|
||||
auto lower = findTarget(targetGotOrPltAddress);
|
||||
auto upper = findTarget(targetGotOrPltAddress + targetGotOrPltSize);
|
||||
for (auto it = lower; it != upper; ++it)
|
||||
sites.push_back(textSectionVAddr + it->second);
|
||||
return sites;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
#include <relinker/analysis/CallSiteResolver.hpp>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
static void Require(bool ok) { if (!ok) throw std::runtime_error("Incorrect call-site index"); }
|
||||
|
||||
int main() {
|
||||
try {
|
||||
std::vector<std::uint8_t> code;
|
||||
const std::vector<std::uint64_t> targets{0x2000, 0x1000, 0x2000, 0x3000, 0x1008};
|
||||
for (const auto target : targets) {
|
||||
const auto offset = code.size();
|
||||
code.insert(code.end(), {0xFF, 0x15, 0, 0, 0, 0});
|
||||
const auto disp = static_cast<std::int32_t>(target - (0x100 + offset + 6));
|
||||
std::memcpy(code.data() + offset + 2, &disp, 4);
|
||||
}
|
||||
auto resolver = Relinker::MakeCallSiteResolver();
|
||||
Require(resolver->ResolveCallSites(code, 0x100, 0x2000, 8) == std::vector<std::uint64_t>{0x100, 0x10C});
|
||||
Require(resolver->ResolveCallSites(code, 0x100, 0x1000, 8) == std::vector<std::uint64_t>{0x106});
|
||||
Require(resolver->ResolveCallSites(code, 0x100, 0x1000, 9) == std::vector<std::uint64_t>{0x106, 0x118});
|
||||
Require(resolver->ResolveCallSites(code, 0x100, 0x2000, 0).empty());
|
||||
Require(resolver->ResolveCallSites(code, 0x100, 0x4000, 8).empty());
|
||||
auto copy = code;
|
||||
Require(resolver->ResolveCallSites(copy, 0x100, 0x2000, 8) == std::vector<std::uint64_t>{0x100, 0x10C});
|
||||
Require(resolver->ResolveCallSites(copy, 0x200, 0x2100, 8) == std::vector<std::uint64_t>{0x200, 0x20C});
|
||||
std::vector<std::uint8_t> mov{0x48, 0x8B, 0x05, 0xF9, 0x0F, 0, 0};
|
||||
Require(resolver->ResolveCallSites(mov, 0x1000, 0x2000, 8) == std::vector<std::uint64_t>{0x1000});
|
||||
Require(resolver->ResolveCallSites({}, 0, 0, 1).empty());
|
||||
bool rejected = false;
|
||||
try { resolver->ResolveCallSites(code, 0, std::numeric_limits<std::uint64_t>::max(), 1); }
|
||||
catch (const std::exception&) { rejected = true; }
|
||||
Require(rejected);
|
||||
std::cout << "10 call-site index cases passed\n";
|
||||
} catch (const std::exception& error) { std::cerr << error.what() << '\n'; return 1; }
|
||||
}
|
||||
Loading…
Reference in New Issue