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:
commit
2944cc3266
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue