Merge pull request #6325 from amirerfantim/refactor/relinker-remove-dead-code

refactor(relinker): remove functions nothing calls
This commit is contained in:
Adrià authored and GitHub committed 2026-10-19 01:17:30 +02:00
commit cf72c02064
11 files changed
-175

No files matched your search

@@ -38,7 +38,6 @@ struct MemoryOperand {
[[nodiscard]] MemoryOperand DecodeMemoryOperand(const std::uint8_t* data, std::size_t length, std::size_t modRmOffset, std::uint8_t rex, std::vector<std::uint8_t> prefixes);
void EmitSse(std::vector<std::uint8_t>& out, std::uint8_t prefix, std::initializer_list<std::uint8_t> opcode, std::uint8_t dst, std::uint8_t src);
void EmitShiftImm(std::vector<std::uint8_t>& out, std::uint8_t extension, std::uint8_t reg, std::uint8_t imm);
void ApplyStubRelocations(std::span<std::uint8_t> body, std::span<const StubRelocation> relocations, std::uint64_t siteAddress, std::uint64_t stubAddress, Domain::FileByteOffset failureOffset);
class StubBodyBuilder {
@@ -100,10 +100,6 @@ void EmitSse(std::vector<std::uint8_t>& out, const std::uint8_t prefix, const st
_emit(out, prefix, dst, src, opcode, static_cast<std::uint8_t>(kModRmRegister | ((dst & 7) << 3) | (src & 7)));
}
void EmitShiftImm(std::vector<std::uint8_t>& out, const std::uint8_t extension, const std::uint8_t reg, const std::uint8_t imm) {
_shiftImm(out, kShiftQwords, extension, reg, imm);
}
void StubBodyBuilder::Sse(const std::uint8_t prefix, const std::initializer_list<std::uint8_t> opcode, const std::uint8_t dst, const std::uint8_t src) {
EmitSse(_bytes, prefix, opcode, dst, src);
}
@@ -10,17 +10,9 @@ class ValidationPolicy : public IValidationPolicy {
public:
ValidationPolicy() = default;
void ValidateSyscallAbsence() override;
void ValidateRelocationTypeSupported(std::uint32_t relocationTypeValue, FileByteOffset fileByteOffset) override;
void ValidateNidBelongsToLibrary(const std::string& nid, const std::string& library) override;
void ValidateSceStructureSize(ByteCount expectedSize, ByteCount actualSize, FileByteOffset fileByteOffset) override;
void ValidateDynamicFieldInterpretable(const std::string& fieldName, FileByteOffset fileByteOffset) override;
void ValidateNoSyscallInstructions(const std::vector<std::uint8_t>& codeSection, FileByteOffset codeOffset) override;
void RegisterLibraryImport(const std::string& library);
private:
std::set<std::string> _importedLibraries;
std::set<std::uint32_t> _supportedRelocationTypes;
void _initializeSupportedRelocationTypes();
@@ -16,7 +16,6 @@ public:
[[nodiscard]] virtual std::vector<SectionHeader> ReadSectionHeaders() const = 0;
[[nodiscard]] virtual std::vector<DynamicTag> ReadDynamicTags(const ProgramHeader& dynamicHeader) const = 0;
[[nodiscard]] virtual FileByteOffset TranslateVirtualAddress(VirtualAddress address) const = 0;
[[nodiscard]] virtual std::vector<std::uint8_t> ReadSection(const SectionHeader& header) const = 0;
[[nodiscard]] virtual std::vector<std::uint8_t> ReadSegment(const ProgramHeader& header) const = 0;
[[nodiscard]] virtual std::uint64_t GetFileSize() const = 0;
[[nodiscard]] virtual const std::vector<std::uint8_t>& GetRawBytes() const = 0;
@@ -9,12 +9,7 @@ class IValidationPolicy {
public:
virtual ~IValidationPolicy() = default;
virtual void ValidateSyscallAbsence() = 0;
virtual void ValidateRelocationTypeSupported(std::uint32_t relocationTypeValue, FileByteOffset fileByteOffset) = 0;
virtual void ValidateNidBelongsToLibrary(const std::string& nid, const std::string& library) = 0;
virtual void ValidateSceStructureSize(ByteCount expectedSize, ByteCount actualSize, FileByteOffset fileByteOffset) = 0;
virtual void ValidateDynamicFieldInterpretable(const std::string& fieldName, FileByteOffset fileByteOffset) = 0;
virtual void ValidateNoSyscallInstructions(const std::vector<std::uint8_t>& codeSection, FileByteOffset codeOffset) = 0;
};
}
@@ -60,7 +60,6 @@ struct GuestImage {
class GuestImageReader {
public:
std::vector<std::string> ReadModuleNames(const std::vector<std::uint8_t>& bytes) const;
GuestImage Read(const std::filesystem::path& path, std::vector<std::uint8_t> bytes, bool windows = false) const;
};
@@ -15,7 +15,6 @@ public:
std::vector<SectionHeader> ReadSectionHeaders() const override;
std::vector<DynamicTag> ReadDynamicTags(const ProgramHeader& dynamicHeader) const override;
FileByteOffset TranslateVirtualAddress(VirtualAddress address) const override;
std::vector<std::uint8_t> ReadSection(const SectionHeader& header) const override;
std::vector<std::uint8_t> ReadSegment(const ProgramHeader& header) const override;
std::uint64_t GetFileSize() const override;
const std::vector<std::uint8_t>& GetRawBytes() const override;
@@ -6,15 +6,6 @@ namespace Relinker {
using namespace Elfpatcher;
static constexpr std::uint8_t SYSCALL_BYTE0 = 0x0F;
static constexpr std::uint8_t SYSCALL_BYTE1 = 0x05;
static constexpr std::uint8_t INT80_BYTE0 = 0xCD;
static constexpr std::uint8_t INT80_BYTE1 = 0x80;
static constexpr std::uint8_t SYSENTER_BYTE0 = 0x0F;
static constexpr std::uint8_t SYSENTER_BYTE1 = 0x34;
static constexpr std::uint8_t SYSRET_BYTE0 = 0x0F;
static constexpr std::uint8_t SYSRET_BYTE1 = 0x07;
void ValidationPolicy::_initializeSupportedRelocationTypes() {
_supportedRelocationTypes = {
R_X86_64_NONE,
@@ -37,16 +28,6 @@ void ValidationPolicy::_initializeSupportedRelocationTypes() {
};
}
void ValidationPolicy::RegisterLibraryImport(const std::string& library) {
if (_supportedRelocationTypes.empty()) {
_initializeSupportedRelocationTypes();
}
_importedLibraries.insert(library);
}
void ValidationPolicy::ValidateSyscallAbsence() {
}
void ValidationPolicy::ValidateRelocationTypeSupported(const std::uint32_t relocationTypeValue, const FileByteOffset fileByteOffset) {
if (_supportedRelocationTypes.empty()) {
_initializeSupportedRelocationTypes();
@@ -59,49 +40,4 @@ void ValidationPolicy::ValidateRelocationTypeSupported(const std::uint32_t reloc
}
}
void ValidationPolicy::ValidateNidBelongsToLibrary(const std::string& Nid, const std::string& library) {
if (_importedLibraries.find(library) == _importedLibraries.end()) {
std::ostringstream msg;
msg << "NID \"" << Nid << "\" references library \"" << library
<< "\" which is not in the NEEDED list";
throw RelinkerException(msg.str(), 0);
}
}
void ValidationPolicy::ValidateSceStructureSize(const ByteCount expectedSize, const ByteCount actualSize, const FileByteOffset fileByteOffset) {
if (actualSize != expectedSize) {
std::ostringstream msg;
msg << "SCE structure size mismatch at offset 0x" << std::hex << fileByteOffset
<< ": expected " << std::dec << expectedSize << ", got " << actualSize;
throw RelinkerException(msg.str(), fileByteOffset);
}
}
void ValidationPolicy::ValidateDynamicFieldInterpretable(const std::string& fieldName, const FileByteOffset fileByteOffset) {
std::ostringstream msg;
msg << "Dynamic field \"" << fieldName
<< "\" cannot be interpreted at offset 0x" << std::hex << fileByteOffset;
throw RelinkerException(msg.str(), fileByteOffset);
}
void ValidationPolicy::ValidateNoSyscallInstructions(
const std::vector<std::uint8_t>& CodeSection, const FileByteOffset codeOffset) {
for (std::size_t i = 0; i + 1 < CodeSection.size(); ++i) {
const std::uint8_t b0 = CodeSection[i];
const std::uint8_t b1 = CodeSection[i + 1];
bool isSyscall = (b0 == SYSCALL_BYTE0 && b1 == SYSCALL_BYTE1);
bool isInt80 = (b0 == INT80_BYTE0 && b1 == INT80_BYTE1);
bool isSysenter = (b0 == SYSENTER_BYTE0 && b1 == SYSENTER_BYTE1);
bool isSysret = (b0 == SYSRET_BYTE0 && b1 == SYSRET_BYTE1);
if (isSyscall || isInt80 || isSysenter || isSysret) {
FileByteOffset instrOffset = codeOffset + i;
std::ostringstream msg;
msg << "Forbidden syscall instruction at offset 0x" << std::hex << instrOffset;
throw RelinkerException(msg.str(), instrOffset);
}
}
}
}
@@ -12,82 +12,6 @@
namespace Relinker {
std::vector<std::string> GuestImageReader::ReadModuleNames(const std::vector<std::uint8_t>& bytes) const {
if (bytes.size() < 64 || Io::ReadU32(bytes, 0) != 0x464c457f || bytes[4] != 2 || bytes[5] != 1 || bytes[6] != 1 || Io::ReadU16(bytes, 18) != 62)
throw Domain::RelinkerException("Expected little-endian ELF64 x86-64 module metadata");
const auto type = Io::ReadU16(bytes, 16);
if (type != 3 && type != 0xfe18) return {};
if (Io::ReadU16(bytes, 52) != 64 || Io::ReadU16(bytes, 54) != 56 || Io::ReadU16(bytes, 56) == 0)
throw Domain::RelinkerException("Invalid module metadata program headers");
const auto range = [&](std::uint64_t offset, std::uint64_t size) {
if (offset > bytes.size() || size > bytes.size() - offset) throw Domain::RelinkerException("Module metadata exceeds file");
};
range(Io::ReadU64(bytes, 32), static_cast<std::uint64_t>(Io::ReadU16(bytes, 56)) * 56);
ElfReader reader(bytes);
const auto headers = reader.ReadProgramHeaders();
const Domain::ProgramHeader* dynamic = nullptr;
const Domain::ProgramHeader* dynlib = nullptr;
for (const auto& header : headers) {
if (header.Type == 2) {
if (dynamic) throw Domain::RelinkerException("Duplicate module metadata dynamic segment");
dynamic = &header;
}
if (header.Type == 0x61000000) {
if (dynlib) throw Domain::RelinkerException("Duplicate module metadata dynamic data");
dynlib = &header;
}
}
if (!dynamic) throw Domain::RelinkerException("Missing module metadata dynamic segment");
std::map<std::uint64_t, std::uint64_t> strings;
std::vector<std::uint64_t> modules;
for (const auto& tag : reader.ReadDynamicTags(*dynamic)) {
if (tag.Tag == 0x6100000d || tag.Tag == 0x61000043) modules.push_back(tag.Value);
else if (tag.Tag == 5 || tag.Tag == 10 || tag.Tag == 0x61000035 || tag.Tag == 0x61000037) {
if (!strings.emplace(tag.Tag, tag.Value).second) throw Domain::RelinkerException("Duplicate module metadata string tag");
}
}
if (modules.empty()) return {};
const auto get = [&](std::uint64_t standard, std::uint64_t sce) {
if (strings.contains(standard) == strings.contains(sce)) throw Domain::RelinkerException("Missing or ambiguous module metadata string tag");
return strings.at(strings.contains(standard) ? standard : sce);
};
const auto size = get(10, 0x61000037);
const auto address = get(5, 0x61000035);
std::uint64_t offset = 0;
if (strings.contains(5)) {
bool found = false;
for (const auto& header : headers) {
if (header.Type != 1 || address < header.MappedAddress || address - header.MappedAddress > header.FileSize || size > header.FileSize - (address - header.MappedAddress)) continue;
range(header.Offset, header.FileSize);
if (found) throw Domain::RelinkerException("Ambiguous module metadata string mapping");
offset = header.Offset + address - header.MappedAddress;
found = true;
}
if (!found) throw Domain::RelinkerException("Unmapped module metadata strings");
} else {
if (!dynlib || address > dynlib->FileSize || size > dynlib->FileSize - address) throw Domain::RelinkerException("Module metadata strings exceed dynamic data");
range(dynlib->Offset, dynlib->FileSize);
offset = dynlib->Offset + address;
}
range(offset, size);
if (size == 0 || bytes[offset] != 0) throw Domain::RelinkerException("Invalid module metadata strings");
std::vector<std::string> names;
std::set<std::uint64_t> ids;
for (const auto value : modules) {
const auto nameOffset = value & 0xffffffffu;
if (nameOffset >= size) throw Domain::RelinkerException("Module metadata string offset out of bounds");
const auto start = bytes.begin() + static_cast<std::ptrdiff_t>(offset + nameOffset);
const auto end = bytes.begin() + static_cast<std::ptrdiff_t>(offset + size);
const auto zero = std::find(start, end, 0);
if (zero == end) throw Domain::RelinkerException("Unterminated module metadata string");
const std::string name(start, zero);
if (name.empty() || name.find_first_of("/\\:$\r\n") != std::string::npos) throw Domain::RelinkerException("Invalid module metadata name");
if (!ids.insert(value >> 48).second) throw Domain::RelinkerException("Duplicate module metadata export ID");
names.push_back(name);
}
return names;
}
GuestImage GuestImageReader::Read(const std::filesystem::path& path, std::vector<std::uint8_t> bytes, const bool windows) const try {
const auto fail = [&](const std::string& message) { throw Domain::RelinkerException(Io::Utf8Path(path) + ": " + message); };
const auto range = [&](std::uint64_t offset, std::uint64_t size) {
@@ -292,16 +292,6 @@ FileByteOffset ElfReader::TranslateVirtualAddress(VirtualAddress address) const
throw RelinkerException("Virtual address not mapped by any PT_LOAD segment", address);
}
std::vector<std::uint8_t> ElfReader::ReadSection(const SectionHeader& header) const {
if (!_rangeFits(header.Offset, header.SectionSize, _fileBuffer.size())) {
throw RelinkerException("Section offset out of bounds", header.Offset);
}
return std::vector<std::uint8_t>(
_fileBuffer.begin() + header.Offset,
_fileBuffer.begin() + header.Offset + header.SectionSize);
}
std::vector<std::uint8_t> ElfReader::ReadSegment(const ProgramHeader& header) const {
if (!_rangeFits(header.Offset, header.FileSize, _fileBuffer.size())) {
throw RelinkerException("Segment offset out of bounds", header.Offset);
@@ -1,6 +1,5 @@
#include <relinker/pipeline/RelinkerPipeline.hpp>
#include <elfpatcher/general/ElfConstants.hpp>
#include <relinker/analysis/ValidationPolicy.hpp>
#include <relinker/analysis/UnusedNidFilter/PltCompactor.hpp>
#include <relinker/analysis/UnusedNidFilter/EhFrameReader.hpp>
#include <relinker/analysis/SceCodeExtent.hpp>
@@ -230,7 +229,6 @@ RelinkResult RelinkerPipeline::Relink() {
std::vector<NidReference> nidRefs;
std::vector<std::string> neededLibraries;
auto policy = std::dynamic_pointer_cast<ValidationPolicy>(validationPolicy);
const std::vector<std::uint8_t>& raw = elfReader->GetRawBytes();
const std::optional<ByteCount> dynSymTabSize = hasTag(DT_OS_SYMTABSZ)
@@ -261,7 +259,6 @@ RelinkResult RelinkerPipeline::Relink() {
for (auto& [fst, snd] : neededLibraryNamesByStrOffset) {
snd = readCStr(fst);
neededLibraries.push_back(snd);
if (policy) policy->RegisterLibraryImport(snd);
}
std::map<std::uint64_t, std::string> importModules;
@@ -382,7 +379,6 @@ RelinkResult RelinkerPipeline::Relink() {
}
}
validationPolicy->ValidateSyscallAbsence();
std::vector<std::pair<ByteCount, std::string>> exportedSymbols;
std::vector<VirtualAddress> exportedFunctions;