Merge pull request #2304 from HamidRezaLS/perf/relinker-cfg-zero-copy

perf(relinker): single-pass CFG instruction decoding and zero-copy text span
This commit is contained in:
Adrià 2026-10-10 09:51:58 +02:00 committed by GitHub
commit 2944cc3266
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 17 deletions

View File

@ -5,6 +5,7 @@
#include <relinker/analysis/UnusedNidFilter/IRelativeRelocationIndex.hpp>
#include <unordered_set>
#include <memory>
#include <span>
#include <vector>
namespace Relinker::UnusedNidFilter {
@ -16,6 +17,25 @@ public:
virtual bool IsReachable(VirtualAddress vaddr) const = 0;
};
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
std::span<const std::uint8_t> text,
VirtualAddress textVaddr,
VirtualAddress entryVaddr,
const std::vector<VirtualAddress>& extraEntries,
const IRelativeRelocationIndex& relativeRelocations,
bool followCodeAddresses = false
);
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
std::span<const std::uint8_t> text,
VirtualAddress textVaddr,
VirtualAddress entryVaddr,
const std::vector<VirtualAddress>& extraEntries,
const IRelativeRelocationIndex& relativeRelocations,
bool followCodeAddresses,
std::unordered_set<VirtualAddress>& reachable
);
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
const std::vector<std::uint8_t>& text,
VirtualAddress textVaddr,

View File

@ -9,7 +9,9 @@
#include <iterator>
#include <limits>
#include <map>
#include <span>
#include <string>
#include <unordered_set>
namespace Relinker {
@ -273,17 +275,21 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
}
}
std::size_t previousRoots = 0;
std::unordered_set<Domain::VirtualAddress> decodedInstructions;
std::vector<std::unordered_set<Domain::VirtualAddress>> sectionReachable(headers.size());
do {
previousRoots = roots.size();
for (const auto& header : headers) {
for (std::size_t i = 0; i < headers.size(); ++i) {
const auto& header = headers[i];
if (header.Type != 1 || (header.Flags & 1) == 0 || header.FileSize == 0) continue;
range(header.Offset, header.FileSize);
std::vector<std::uint64_t> entries;
for (const auto root : roots) if (root >= header.MappedAddress && root - header.MappedAddress < header.FileSize) entries.push_back(root);
if (entries.empty()) continue;
const std::vector<std::uint8_t> text(bytes.begin() + header.Offset, bytes.begin() + header.Offset + header.FileSize);
const auto graph = UnusedNidFilter::BuildControlFlowGraph(text, header.MappedAddress, entries.front(), entries, pointers, true);
const std::span<const std::uint8_t> text(bytes.data() + header.Offset, header.FileSize);
const auto graph = UnusedNidFilter::BuildControlFlowGraph(text, header.MappedAddress, entries.front(), entries, pointers, true, sectionReachable[i]);
for (const auto address : graph->ReachableVaddrs()) {
if (!decodedInstructions.insert(address).second) continue;
instructions.insert(address);
const auto offset = address - header.MappedAddress;
const auto info = decoder.DecodeInstruction(text.data() + offset, text.size() - offset);

View File

@ -9,25 +9,33 @@ namespace Relinker::UnusedNidFilter {
class ControlFlowGraph : public IControlFlowGraph {
public:
explicit ControlFlowGraph(std::unordered_set<VirtualAddress> reachable)
: _reachable(std::move(reachable)) {}
: _owned(std::move(reachable)), _borrowed(nullptr) {}
const std::unordered_set<VirtualAddress>& ReachableVaddrs() const override { return _reachable; }
bool IsReachable(VirtualAddress vaddr) const override { return _reachable.count(vaddr) > 0; }
explicit ControlFlowGraph(const std::unordered_set<VirtualAddress>* reachable)
: _borrowed(reachable) {}
const std::unordered_set<VirtualAddress>& ReachableVaddrs() const override {
return _borrowed ? *_borrowed : _owned;
}
bool IsReachable(VirtualAddress vaddr) const override {
return (_borrowed ? *_borrowed : _owned).count(vaddr) > 0;
}
private:
std::unordered_set<VirtualAddress> _reachable;
std::unordered_set<VirtualAddress> _owned;
const std::unordered_set<VirtualAddress>* _borrowed = nullptr;
};
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
const std::vector<std::uint8_t>& text,
std::span<const std::uint8_t> text,
VirtualAddress textVaddr,
VirtualAddress entryVaddr,
const std::vector<VirtualAddress>& extraEntries,
const IRelativeRelocationIndex& relativeRelocations,
bool followCodeAddresses
bool followCodeAddresses,
std::unordered_set<VirtualAddress>& reachable
) {
const Codegen::X64InstructionDecoder decoder;
std::unordered_set<VirtualAddress> reachable;
std::queue<VirtualAddress> worklist;
auto enqueue = [&](VirtualAddress va) {
@ -110,7 +118,31 @@ std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
}
}
return std::make_unique<ControlFlowGraph>(&reachable);
}
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
std::span<const std::uint8_t> text,
VirtualAddress textVaddr,
VirtualAddress entryVaddr,
const std::vector<VirtualAddress>& extraEntries,
const IRelativeRelocationIndex& relativeRelocations,
bool followCodeAddresses
) {
std::unordered_set<VirtualAddress> reachable;
BuildControlFlowGraph(text, textVaddr, entryVaddr, extraEntries, relativeRelocations, followCodeAddresses, reachable);
return std::make_unique<ControlFlowGraph>(std::move(reachable));
}
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
const std::vector<std::uint8_t>& text,
VirtualAddress textVaddr,
VirtualAddress entryVaddr,
const std::vector<VirtualAddress>& extraEntries,
const IRelativeRelocationIndex& relativeRelocations,
bool followCodeAddresses
) {
return BuildControlFlowGraph(std::span<const std::uint8_t>(text.data(), text.size()), textVaddr, entryVaddr, extraEntries, relativeRelocations, followCodeAddresses);
}
}

View File

@ -1,15 +1,15 @@
#include <relinker/analysis/UnusedNidFilter/PltCompactor.hpp>
#include <cstring>
#include <limits>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
namespace Relinker::UnusedNidFilter {
CompactedPlt CompactPlt(const std::vector<NidReference>& originalReferences, const std::vector<NidReference>& keptReferences, const std::vector<std::uint8_t>& text, VirtualAddress textVaddr, FileByteOffset textOffset, FileByteOffset tableOffset) {
CompactedPlt result;
result.References = keptReferences;
std::map<VirtualAddress, std::uint32_t> originalSlots;
std::unordered_map<VirtualAddress, std::uint32_t> originalSlots;
for (const auto& reference : originalReferences) {
if (reference.RelocationTypeValue != 7) continue;
if (reference.RelocationTableOffset < tableOffset || (reference.RelocationTableOffset - tableOffset) % 24 != 0 || (reference.RelocationTableOffset - tableOffset) / 24 > std::numeric_limits<std::uint32_t>::max())
@ -17,22 +17,21 @@ CompactedPlt CompactPlt(const std::vector<NidReference>& originalReferences, con
if (!originalSlots.emplace(reference.RelocationAddress, static_cast<std::uint32_t>((reference.RelocationTableOffset - tableOffset) / 24)).second)
throw RelinkerException("Strict filter: duplicate original PLT slot", reference.RelocationAddress);
}
std::map<VirtualAddress, std::uint32_t> newSlots;
std::unordered_map<VirtualAddress, std::uint32_t> newSlots;
for (auto& reference : result.References) {
if (reference.RelocationTypeValue != 7) continue;
if (!originalSlots.contains(reference.RelocationAddress) || !newSlots.emplace(reference.RelocationAddress, result.SlotCount).second)
throw RelinkerException("Strict filter: invalid retained PLT slot", reference.RelocationAddress);
reference.RelocationTableOffset = tableOffset + static_cast<std::uint64_t>(result.SlotCount++) * 24;
}
std::set<VirtualAddress> found;
std::unordered_set<VirtualAddress> found;
for (std::size_t offset = 0; offset + 16 <= text.size(); ++offset) {
if (text[offset] != 0xFF || text[offset + 1] != 0x25) continue;
if (text[offset] != 0xFF || text[offset + 1] != 0x25 || text[offset + 6] != 0x68 || text[offset + 11] != 0xE9) continue;
std::int32_t displacement;
std::memcpy(&displacement, text.data() + offset + 2, sizeof(displacement));
const auto slot = textVaddr + offset + 6 + static_cast<std::uint64_t>(static_cast<std::int64_t>(displacement));
const auto original = originalSlots.find(slot);
if (original == originalSlots.end()) continue;
if (text[offset + 6] != 0x68 || text[offset + 11] != 0xE9) continue;
std::uint32_t index;
std::memcpy(&index, text.data() + offset + 7, sizeof(index));
if (index != original->second) throw RelinkerException("Strict filter: PLT thunk index disagrees with its relocation", textVaddr + offset);