refactor(core): separate io from domain logic + di

This commit is contained in:
boykopovar 2026-08-04 00:16:14 +03:00
parent e451fde738
commit 850eae37eb
27 changed files with 374 additions and 266 deletions

View File

@ -4,4 +4,7 @@ project(Relinker CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(core/domain)
add_subdirectory(core/elfpatcher)
add_subdirectory(core/io)
add_subdirectory(core/relinker)

View File

@ -0,0 +1,2 @@
add_library(domain INTERFACE)
target_include_directories(domain INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)

View File

@ -0,0 +1,109 @@
#ifndef DOMAIN_TYPES_HPP
#define DOMAIN_TYPES_HPP
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <stdexcept>
namespace Domain {
using FileByteOffset = std::uint64_t;
using VirtualAddress = std::uint64_t;
using ByteCount = std::uint64_t;
struct ElfHeader {
std::uint16_t Machine;
std::uint16_t Type;
std::uint8_t OsAbi;
std::uint8_t AbiVersion;
std::uint64_t EntryPoint;
std::uint64_t ProgramHeaderOffset;
std::uint64_t SectionHeaderOffset;
std::uint32_t ProgramHeaderEntrySize;
std::uint16_t ProgramHeaderCount;
std::uint32_t SectionHeaderEntrySize;
std::uint16_t SectionHeaderCount;
std::uint16_t SectionHeaderStringIndex;
};
struct ProgramHeader {
std::uint32_t Type;
std::uint32_t Flags;
FileByteOffset Offset;
VirtualAddress MappedAddress;
VirtualAddress PhysicalAddress;
ByteCount FileSize;
ByteCount MemorySize;
std::uint64_t Alignment;
};
struct SectionHeader {
std::string Name;
std::uint32_t Type;
std::uint64_t Flags;
VirtualAddress MappedAddress;
FileByteOffset Offset;
ByteCount SectionSize;
std::uint32_t Link;
std::uint32_t Info;
std::uint64_t EntrySize;
};
struct NidReference {
std::string Nid;
std::string Library;
std::uint32_t RelocationTypeValue;
FileByteOffset RelocationTableOffset;
VirtualAddress RelocationAddress;
};
struct CallSiteInfo {
std::vector<FileByteOffset> Sites;
bool Resolved;
};
struct SymbolExport {
std::string Nid;
std::string Library;
FileByteOffset GotOffset;
FileByteOffset PltOffset;
CallSiteInfo CallSites;
};
struct DynamicTag {
std::int64_t Tag;
std::uint64_t Value;
};
struct RelinkerException : std::runtime_error {
FileByteOffset FailureOffset;
explicit RelinkerException(const std::string& message, const FileByteOffset failureOffset = 0)
: std::runtime_error(message), FailureOffset(failureOffset) {}
};
struct SysVDynamicSection {
std::vector<std::uint8_t> DynamicSegmentData;
std::vector<std::uint8_t> DynSymData;
std::vector<std::uint8_t> DynStrData;
std::vector<std::uint8_t> RelaData;
std::vector<std::uint8_t> RelaPltData;
};
struct CallRegistryEntry {
std::string Nid;
std::string Library;
std::string RelocationTypeString;
FileByteOffset RelocationOffset;
std::string TargetSection;
FileByteOffset TargetOffset;
std::vector<FileByteOffset> CallSites;
bool CallSitesResolved;
};
}
#endif

View File

@ -0,0 +1,5 @@
add_library(elfpatcher STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/ElfPatcher.cpp
)
target_include_directories(elfpatcher PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(elfpatcher PRIVATE domain)

View File

@ -1,17 +1,16 @@
#ifndef RELINKER_OUTPUT_ELFPATCHER_HPP
#define RELINKER_OUTPUT_ELFPATCHER_HPP
#ifndef ELFPATCHER_ELFPATCHER_HPP
#define ELFPATCHER_ELFPATCHER_HPP
#include <relinker/domain/IElfPatcher.hpp>
#include <elfpatcher/IElfPatcher.hpp>
namespace Relinker {
namespace Elfpatcher {
class ElfPatcher : public IElfPatcher {
public:
void PatchAndWrite(
const std::string& inputPath,
const std::string& outputPath,
const std::vector<ProgramHeader>& originalHeaders,
const SysVDynamicSection& dynamicSection
std::vector<std::uint8_t> Patch(
const std::vector<std::uint8_t>& sourceElf,
const std::vector<Domain::ProgramHeader>& originalHeaders,
const Domain::SysVDynamicSection& dynamicSection
) override;
private:
@ -42,9 +41,9 @@ private:
static constexpr std::int64_t DT_PLTREL = 20;
[[nodiscard]] bool _isSceSpecificSegment(std::uint32_t type) const;
[[nodiscard]] bool _isNullPageLoad(const ProgramHeader& ph) const;
[[nodiscard]] bool _shouldSkip(const ProgramHeader& ph) const;
[[nodiscard]] ProgramHeader _makeLoadHeader(std::uint64_t offset, std::uint64_t size) const;
[[nodiscard]] bool _isNullPageLoad(const Domain::ProgramHeader& ph) const;
[[nodiscard]] bool _shouldSkip(const Domain::ProgramHeader& ph) const;
[[nodiscard]] Domain::ProgramHeader _makeLoadHeader(std::uint64_t offset, std::uint64_t size) const;
[[nodiscard]] std::vector<std::uint8_t> _buildEntryStub(std::uint64_t stubVaddr, std::uint64_t realEntryVaddr) const;
void _writeInterp(std::vector<std::uint8_t>& buf, std::size_t phEntOff, std::uint64_t interpOff, std::uint64_t interpSize) const;
void _writeU16(std::vector<std::uint8_t>& buf, std::size_t offset, std::uint16_t v) const;
@ -53,7 +52,7 @@ private:
void _appendU64(std::vector<std::uint8_t>& buf, std::uint64_t v) const;
void _appendI64(std::vector<std::uint8_t>& buf, std::int64_t v) const;
void _appendDynEntry(std::vector<std::uint8_t>& buf, std::int64_t tag, std::uint64_t val) const;
void _writeProgramHeader(std::vector<std::uint8_t>& buf, std::size_t offset, const ProgramHeader& ph) const;
void _writeProgramHeader(std::vector<std::uint8_t>& buf, std::size_t offset, const Domain::ProgramHeader& ph) const;
[[nodiscard]] std::uint32_t _fixLoadFlags(std::uint32_t originalFlags) const;
};

View File

@ -0,0 +1,23 @@
#ifndef ELFPATCHER_IELFPATCHER_HPP
#define ELFPATCHER_IELFPATCHER_HPP
#include <domain/Types.hpp>
#include <cstdint>
#include <vector>
namespace Elfpatcher {
class IElfPatcher {
public:
virtual ~IElfPatcher() = default;
virtual std::vector<std::uint8_t> Patch(
const std::vector<std::uint8_t>& sourceElf,
const std::vector<Domain::ProgramHeader>& originalHeaders,
const Domain::SysVDynamicSection& dynamicSection
) = 0;
};
}
#endif

View File

@ -1,8 +1,7 @@
#include <relinker/output/ElfPatcher.hpp>
#include <fstream>
#include <elfpatcher/ElfPatcher.hpp>
#include <cstring>
namespace Relinker {
namespace Elfpatcher {
bool ElfPatcher::_isSceSpecificSegment(std::uint32_t type) const {
return type == PT_SCE_DYNLIBDATA
@ -11,7 +10,7 @@ bool ElfPatcher::_isSceSpecificSegment(std::uint32_t type) const {
|| (type >= 0x61000000 && type <= 0x6fffffff);
}
bool ElfPatcher::_isNullPageLoad(const ProgramHeader& ph) const {
bool ElfPatcher::_isNullPageLoad(const Domain::ProgramHeader& ph) const {
return ph.Type == PT_LOAD && ph.MappedAddress == 0;
}
@ -42,7 +41,7 @@ void ElfPatcher::_appendDynEntry(std::vector<std::uint8_t>& buf, std::int64_t ta
_appendU64(buf, val);
}
void ElfPatcher::_writeProgramHeader(std::vector<std::uint8_t>& buf, std::size_t offset, const ProgramHeader& ph) const {
void ElfPatcher::_writeProgramHeader(std::vector<std::uint8_t>& buf, std::size_t offset, const Domain::ProgramHeader& ph) const {
_writeU32(buf, offset + 0, ph.Type);
_writeU32(buf, offset + 4, ph.Flags);
_writeU64(buf, offset + 8, ph.Offset);
@ -53,8 +52,8 @@ void ElfPatcher::_writeProgramHeader(std::vector<std::uint8_t>& buf, std::size_t
_writeU64(buf, offset + 48, ph.Alignment);
}
ProgramHeader ElfPatcher::_makeLoadHeader(std::uint64_t offset, std::uint64_t size) const {
ProgramHeader ph{};
Domain::ProgramHeader ElfPatcher::_makeLoadHeader(std::uint64_t offset, std::uint64_t size) const {
Domain::ProgramHeader ph{};
ph.Type = PT_LOAD;
ph.Flags = PF_R;
ph.Offset = offset;
@ -67,7 +66,7 @@ ProgramHeader ElfPatcher::_makeLoadHeader(std::uint64_t offset, std::uint64_t si
}
void ElfPatcher::_writeInterp(std::vector<std::uint8_t>& buf, std::size_t phEntOff, std::uint64_t interpOff, std::uint64_t interpSize) const {
ProgramHeader ph{};
Domain::ProgramHeader ph{};
ph.Type = PT_INTERP;
ph.Flags = PF_R;
ph.Offset = interpOff;
@ -79,7 +78,7 @@ void ElfPatcher::_writeInterp(std::vector<std::uint8_t>& buf, std::size_t phEntO
_writeProgramHeader(buf, phEntOff, ph);
}
std::vector<std::uint8_t> ElfPatcher::_buildEntryStub(std::uint64_t stubVaddr, std::uint64_t realEntryVaddr) const {
std::vector<std::uint8_t> ElfPatcher::_buildEntryStub(const std::uint64_t stubVaddr, const std::uint64_t realEntryVaddr) const {
std::vector<std::uint8_t> s;
s.push_back(0x58);
s.push_back(0x48); s.push_back(0x89); s.push_back(0xe3);
@ -104,7 +103,7 @@ std::uint32_t ElfPatcher::_fixLoadFlags(std::uint32_t originalFlags) const {
return originalFlags | PF_R;
}
bool ElfPatcher::_shouldSkip(const ProgramHeader& ph) const {
bool ElfPatcher::_shouldSkip(const Domain::ProgramHeader& ph) const {
if (_isSceSpecificSegment(ph.Type)) return true;
if (ph.Type == PT_DYNAMIC) return true;
if (_isNullPageLoad(ph)) return true;
@ -112,20 +111,12 @@ bool ElfPatcher::_shouldSkip(const ProgramHeader& ph) const {
return false;
}
void ElfPatcher::PatchAndWrite(
const std::string& inputPath,
const std::string& outputPath,
const std::vector<ProgramHeader>& originalHeaders,
const SysVDynamicSection& dynSection)
std::vector<std::uint8_t> ElfPatcher::Patch(
const std::vector<std::uint8_t>& sourceElf,
const std::vector<Domain::ProgramHeader>& originalHeaders,
const Domain::SysVDynamicSection& dynSection)
{
std::ifstream in(inputPath, std::ios::binary | std::ios::ate);
if (!in)
throw RelinkerException("Cannot open input ELF: " + inputPath);
const std::streamsize fileSize = in.tellg();
in.seekg(0);
std::vector<std::uint8_t> buf(static_cast<std::size_t>(fileSize));
if (!in.read(reinterpret_cast<char*>(buf.data()), fileSize))
throw RelinkerException("Cannot read input ELF: " + inputPath);
std::vector<std::uint8_t> buf = sourceElf;
buf[7] = 0;
buf[8] = 0;
@ -211,7 +202,7 @@ void ElfPatcher::PatchAndWrite(
}
const std::uint16_t neededPh = keptCount + 3;
if (neededPh > phNum)
throw RelinkerException(
throw Domain::RelinkerException(
"Not enough program header slots: need " + std::to_string(neededPh) +
", available " + std::to_string(phNum));
@ -221,7 +212,7 @@ void ElfPatcher::PatchAndWrite(
continue;
const std::size_t phEntOff = static_cast<std::size_t>(phOff) + writtenPh * phEntSize;
if (ph.Type == PT_LOAD) {
ProgramHeader fixed = ph;
Domain::ProgramHeader fixed = ph;
fixed.Flags = _fixLoadFlags(ph.Flags);
_writeProgramHeader(buf, phEntOff, fixed);
} else {
@ -238,7 +229,7 @@ void ElfPatcher::PatchAndWrite(
{
const std::size_t phEntOff = static_cast<std::size_t>(phOff) + writtenPh * phEntSize;
ProgramHeader dynPh{};
Domain::ProgramHeader dynPh{};
dynPh.Type = PT_DYNAMIC;
dynPh.Flags = PF_R | PF_W;
dynPh.Offset = dynSegOff;
@ -259,12 +250,7 @@ void ElfPatcher::PatchAndWrite(
_writeU16(buf, 56, writtenPh);
std::ofstream out(outputPath, std::ios::binary);
if (!out)
throw RelinkerException("Cannot open output ELF: " + outputPath);
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
if (!out)
throw RelinkerException("Failed to write output ELF: " + outputPath);
return buf;
}
}

6
core/io/CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
add_library(io STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/FileReader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/FileWriter.cpp
)
target_include_directories(io PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(io PRIVATE domain)

View File

@ -0,0 +1,15 @@
#ifndef IO_FILEREADER_HPP
#define IO_FILEREADER_HPP
#include <io/IFileReader.hpp>
namespace Io {
class FileReader : public IFileReader {
public:
std::vector<std::uint8_t> Read(const std::string& path) override;
};
}
#endif

View File

@ -0,0 +1,16 @@
#ifndef IO_FILEWRITER_HPP
#define IO_FILEWRITER_HPP
#include <io/IFileWriter.hpp>
namespace Io {
class FileWriter : public IFileWriter {
public:
void Write(const std::string& path, const std::vector<std::uint8_t>& data) override;
void Write(const std::string& path, const std::string& content) override;
};
}
#endif

View File

@ -0,0 +1,19 @@
#ifndef IO_IFILEREADER_HPP
#define IO_IFILEREADER_HPP
#include <cstdint>
#include <string>
#include <vector>
namespace Io {
class IFileReader {
public:
virtual ~IFileReader() = default;
virtual std::vector<std::uint8_t> Read(const std::string& path) = 0;
};
}
#endif

View File

@ -0,0 +1,20 @@
#ifndef IO_IFILEWRITER_HPP
#define IO_IFILEWRITER_HPP
#include <cstdint>
#include <string>
#include <vector>
namespace Io {
class IFileWriter {
public:
virtual ~IFileWriter() = default;
virtual void Write(const std::string& path, const std::vector<std::uint8_t>& data) = 0;
virtual void Write(const std::string& path, const std::string& content) = 0;
};
}
#endif

View File

@ -0,0 +1,19 @@
#include <io/FileReader.hpp>
#include <domain/Types.hpp>
#include <fstream>
namespace Io {
std::vector<std::uint8_t> FileReader::Read(const std::string& path) {
std::ifstream f(path, std::ios::binary | std::ios::ate);
if (!f)
throw Domain::RelinkerException("Cannot open file: " + path);
const std::streamsize size = f.tellg();
f.seekg(0);
std::vector<std::uint8_t> buf(static_cast<std::size_t>(size));
if (!f.read(reinterpret_cast<char*>(buf.data()), size))
throw Domain::RelinkerException("Cannot read file: " + path);
return buf;
}
}

View File

@ -0,0 +1,25 @@
#include <io/FileWriter.hpp>
#include <domain/Types.hpp>
#include <fstream>
namespace Io {
void FileWriter::Write(const std::string& path, const std::vector<std::uint8_t>& data) {
std::ofstream f(path, std::ios::binary);
if (!f)
throw Domain::RelinkerException("Cannot open output file: " + path);
f.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
if (!f)
throw Domain::RelinkerException("Failed to write file: " + path);
}
void FileWriter::Write(const std::string& path, const std::string& content) {
std::ofstream f(path);
if (!f)
throw Domain::RelinkerException("Cannot open output file: " + path);
f << content;
if (!f)
throw Domain::RelinkerException("Failed to write file: " + path);
}
}

View File

@ -10,9 +10,9 @@ set(RELINKER_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/analysis/ValidationPolicy.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/output/SysVDynamicSectionBuilder.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/output/CallRegistryWriter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/output/ElfPatcher.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pipeline/RelinkerPipeline.cpp
)
add_executable(relinker ${RELINKER_SOURCES})
target_include_directories(relinker PRIVATE ${RELINKER_INCLUDE_DIR})
target_link_libraries(relinker PRIVATE domain elfpatcher io)

View File

@ -7,17 +7,6 @@
namespace Relinker {
struct CallRegistryEntry {
std::string Nid;
std::string Library;
std::string RelocationTypeString;
FileByteOffset RelocationOffset;
std::string TargetSection;
FileByteOffset TargetOffset;
std::vector<FileByteOffset> CallSites;
bool CallSitesResolved;
};
class ICallRegistryWriter {
public:
virtual ~ICallRegistryWriter() = default;

View File

@ -1,25 +0,0 @@
#ifndef RELINKER_DOMAIN_IELFPATCHER_HPP
#define RELINKER_DOMAIN_IELFPATCHER_HPP
#include <relinker/domain/Types.hpp>
#include <relinker/domain/ISysVDynamicSectionBuilder.hpp>
#include <string>
#include <vector>
namespace Relinker {
class IElfPatcher {
public:
virtual ~IElfPatcher() = default;
virtual void PatchAndWrite(
const std::string& inputPath,
const std::string& outputPath,
const std::vector<ProgramHeader>& originalHeaders,
const SysVDynamicSection& dynamicSection
) = 0;
};
}
#endif

View File

@ -19,6 +19,7 @@ public:
[[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;
};
}

View File

@ -1,7 +1,9 @@
#ifndef RELINKER_DOMAIN_IRELINKERPIPELINE_HPP
#define RELINKER_DOMAIN_IRELINKERPIPELINE_HPP
#include <string>
#include <relinker/domain/RelinkResult.hpp>
#include <cstdint>
#include <vector>
namespace Relinker {
@ -9,11 +11,7 @@ class IRelinkerPipeline {
public:
virtual ~IRelinkerPipeline() = default;
virtual void Run(
const std::string& inputElfPath,
const std::string& outputRegistryPath,
const std::string& outputElfPath
) = 0;
virtual RelinkResult Relink(const std::vector<std::uint8_t>& sourceElf) = 0;
};
}

View File

@ -6,14 +6,6 @@
namespace Relinker {
struct SysVDynamicSection {
std::vector<std::uint8_t> DynamicSegmentData;
std::vector<std::uint8_t> DynSymData;
std::vector<std::uint8_t> DynStrData;
std::vector<std::uint8_t> RelaData;
std::vector<std::uint8_t> RelaPltData;
};
class ISysVDynamicSectionBuilder {
public:
virtual ~ISysVDynamicSectionBuilder() = default;

View File

@ -0,0 +1,17 @@
#ifndef RELINKER_DOMAIN_RELINKRESULT_HPP
#define RELINKER_DOMAIN_RELINKRESULT_HPP
#include <relinker/domain/Types.hpp>
#include <vector>
namespace Relinker {
struct RelinkResult {
std::vector<CallRegistryEntry> RegistryEntries;
std::vector<ProgramHeader> OriginalHeaders;
SysVDynamicSection DynamicSection;
};
}
#endif

View File

@ -1,89 +1,23 @@
#ifndef RELINKER_DOMAIN_TYPES_HPP
#define RELINKER_DOMAIN_TYPES_HPP
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <stdexcept>
#include <domain/Types.hpp>
namespace Relinker {
using FileByteOffset = std::uint64_t;
using VirtualAddress = std::uint64_t;
using ByteCount = std::uint64_t;
struct ElfHeader {
std::uint16_t Machine;
std::uint16_t Type;
std::uint8_t OsAbi;
std::uint8_t AbiVersion;
std::uint64_t EntryPoint;
std::uint64_t ProgramHeaderOffset;
std::uint64_t SectionHeaderOffset;
std::uint32_t ProgramHeaderEntrySize;
std::uint16_t ProgramHeaderCount;
std::uint32_t SectionHeaderEntrySize;
std::uint16_t SectionHeaderCount;
std::uint16_t SectionHeaderStringIndex;
};
struct ProgramHeader {
std::uint32_t Type;
std::uint32_t Flags;
FileByteOffset Offset;
VirtualAddress MappedAddress;
VirtualAddress PhysicalAddress;
ByteCount FileSize;
ByteCount MemorySize;
std::uint64_t Alignment;
};
struct SectionHeader {
std::string Name;
std::uint32_t Type;
std::uint64_t Flags;
VirtualAddress MappedAddress;
FileByteOffset Offset;
ByteCount SectionSize;
std::uint32_t Link;
std::uint32_t Info;
std::uint64_t EntrySize;
};
struct NidReference {
std::string Nid;
std::string Library;
std::uint32_t RelocationTypeValue;
FileByteOffset RelocationTableOffset;
VirtualAddress RelocationAddress;
};
struct CallSiteInfo {
std::vector<FileByteOffset> Sites;
bool Resolved;
};
struct SymbolExport {
std::string Nid;
std::string Library;
FileByteOffset GotOffset;
FileByteOffset PltOffset;
CallSiteInfo CallSites;
};
struct DynamicTag {
std::int64_t Tag;
std::uint64_t Value;
};
struct RelinkerException : std::runtime_error {
FileByteOffset FailureOffset;
explicit RelinkerException(const std::string& message, const FileByteOffset failureOffset = 0)
: std::runtime_error(message), FailureOffset(failureOffset) {}
};
using FileByteOffset = Domain::FileByteOffset;
using VirtualAddress = Domain::VirtualAddress;
using ByteCount = Domain::ByteCount;
using ElfHeader = Domain::ElfHeader;
using ProgramHeader = Domain::ProgramHeader;
using SectionHeader = Domain::SectionHeader;
using NidReference = Domain::NidReference;
using CallSiteInfo = Domain::CallSiteInfo;
using SymbolExport = Domain::SymbolExport;
using DynamicTag = Domain::DynamicTag;
using RelinkerException = Domain::RelinkerException;
using SysVDynamicSection = Domain::SysVDynamicSection;
using CallRegistryEntry = Domain::CallRegistryEntry;
}

View File

@ -8,7 +8,7 @@ namespace Relinker {
class ElfReader : public IElfReader {
public:
explicit ElfReader(const std::string& filePath);
explicit ElfReader(std::vector<std::uint8_t> fileBuffer);
ElfHeader ReadHeader() const override;
std::vector<ProgramHeader> ReadProgramHeaders() const override;
@ -18,12 +18,11 @@ public:
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;
private:
std::string _filePath;
mutable std::vector<std::uint8_t> _fileBuffer;
std::vector<std::uint8_t> _fileBuffer;
void _loadFileIntoBuffer() const;
std::uint64_t _readU64At(FileByteOffset offset) const;
std::uint32_t _readU32At(FileByteOffset offset) const;
std::uint16_t _readU16At(FileByteOffset offset) const;

View File

@ -10,7 +10,6 @@
#include <relinker/domain/IValidationPolicy.hpp>
#include <relinker/domain/ISysVDynamicSectionBuilder.hpp>
#include <relinker/domain/ICallRegistryWriter.hpp>
#include <relinker/domain/IElfPatcher.hpp>
#include <memory>
namespace Relinker {
@ -24,15 +23,10 @@ public:
std::shared_ptr<ICallSiteResolver> callSiteResolver,
std::shared_ptr<IValidationPolicy> validationPolicy,
std::shared_ptr<ISysVDynamicSectionBuilder> dynamicSectionBuilder,
std::shared_ptr<ICallRegistryWriter> callRegistryWriter,
std::shared_ptr<IElfPatcher> elfPatcher
std::shared_ptr<ICallRegistryWriter> callRegistryWriter
);
void Run(
const std::string& inputElfPath,
const std::string& outputRegistryPath,
const std::string& outputElfPath
) override;
RelinkResult Relink(const std::vector<std::uint8_t>& sourceElf) override;
private:
std::shared_ptr<IElfReader> _elfReader;
@ -42,7 +36,6 @@ private:
std::shared_ptr<IValidationPolicy> _validationPolicy;
std::shared_ptr<ISysVDynamicSectionBuilder> _dynamicSectionBuilder;
std::shared_ptr<ICallRegistryWriter> _callRegistryWriter;
std::shared_ptr<IElfPatcher> _elfPatcher;
static constexpr std::uint32_t PT_LOAD = 1;
static constexpr std::uint32_t PT_DYNAMIC = 2;
@ -64,7 +57,6 @@ private:
static constexpr std::int64_t DT_SYMTAB = 0x00000006;
static std::string _relocationTypeName(std::uint32_t type);
void _writeFile(const std::string& path, const std::string& content) const;
};
}

View File

@ -1,4 +1,7 @@
#include <relinker/domain/Types.hpp>
#include <domain/Types.hpp>
#include <io/FileReader.hpp>
#include <io/FileWriter.hpp>
#include <elfpatcher/ElfPatcher.hpp>
#include <relinker/parsing/ElfReader.hpp>
#include <relinker/parsing/SceDynlibParser.hpp>
#include <relinker/parsing/SdkRevisionProfile.hpp>
@ -7,7 +10,6 @@
#include <relinker/analysis/CallSiteResolver.hpp>
#include <relinker/output/SysVDynamicSectionBuilder.hpp>
#include <relinker/output/CallRegistryWriter.hpp>
#include <relinker/output/ElfPatcher.hpp>
#include <relinker/pipeline/RelinkerPipeline.hpp>
#include <iostream>
#include <memory>
@ -23,7 +25,12 @@ int main(const int argc, char* argv[]) {
const std::string outputElfPath = argv[3];
try {
auto elfReader = std::make_shared<Relinker::ElfReader>(inputPath);
Io::FileReader fileReader;
Io::FileWriter fileWriter;
auto sourceBytes = fileReader.Read(inputPath);
auto elfReader = std::make_shared<Relinker::ElfReader>(sourceBytes);
auto programHeaders = elfReader->ReadProgramHeaders();
std::vector<std::uint8_t> sceDynlibData;
@ -40,13 +47,19 @@ int main(const int argc, char* argv[]) {
Relinker::MakeCallSiteResolver(),
std::make_shared<Relinker::ValidationPolicy>(),
std::make_shared<Relinker::SysVDynamicSectionBuilder>(),
std::make_shared<Relinker::CallRegistryWriter>(),
std::make_shared<Relinker::ElfPatcher>()
std::make_shared<Relinker::CallRegistryWriter>()
);
pipeline->Run(inputPath, registryPath, outputElfPath);
auto result = pipeline->Relink(sourceBytes);
} catch (const Relinker::RelinkerException& e) {
auto callRegistryWriter = std::make_shared<Relinker::CallRegistryWriter>();
fileWriter.Write(registryPath, callRegistryWriter->WriteCallRegistry(result.RegistryEntries));
Elfpatcher::ElfPatcher elfPatcher;
auto patchedElf = elfPatcher.Patch(sourceBytes, result.OriginalHeaders, result.DynamicSection);
fileWriter.Write(outputElfPath, patchedElf);
} catch (const Domain::RelinkerException& e) {
std::cerr << "FAIL: " << e.what();
if (e.FailureOffset != 0)
std::cerr << " (offset 0x" << std::hex << e.FailureOffset << ")";

View File

@ -1,34 +1,19 @@
#include <relinker/parsing/ElfReader.hpp>
#include <relinker/domain/Types.hpp>
#include <fstream>
#include <cstring>
namespace Relinker {
ElfReader::ElfReader(const std::string& filePath) : _filePath(filePath) {
ElfReader::ElfReader(std::vector<std::uint8_t> fileBuffer)
: _fileBuffer(std::move(fileBuffer))
{
}
void ElfReader::_loadFileIntoBuffer() const {
if (!_fileBuffer.empty()) {
return;
}
std::ifstream file(_filePath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
throw RelinkerException("Cannot open file: " + _filePath);
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
_fileBuffer.resize(size);
if (!file.read(reinterpret_cast<char*>(_fileBuffer.data()), size)) {
throw RelinkerException("Cannot read file: " + _filePath);
}
const std::vector<std::uint8_t>& ElfReader::GetRawBytes() const {
return _fileBuffer;
}
std::uint8_t ElfReader::_readU8At(FileByteOffset fileByteOffset) const {
_loadFileIntoBuffer();
if (fileByteOffset >= _fileBuffer.size()) {
throw RelinkerException("FileByteOffset out of bounds", fileByteOffset);
}
@ -36,7 +21,6 @@ std::uint8_t ElfReader::_readU8At(FileByteOffset fileByteOffset) const {
}
std::uint16_t ElfReader::_readU16At(FileByteOffset fileByteOffset) const {
_loadFileIntoBuffer();
if (fileByteOffset + 2 > _fileBuffer.size()) {
throw RelinkerException("FileByteOffset out of bounds", fileByteOffset);
}
@ -46,7 +30,6 @@ std::uint16_t ElfReader::_readU16At(FileByteOffset fileByteOffset) const {
}
std::uint32_t ElfReader::_readU32At(FileByteOffset fileByteOffset) const {
_loadFileIntoBuffer();
if (fileByteOffset + 4 > _fileBuffer.size()) {
throw RelinkerException("FileByteOffset out of bounds", fileByteOffset);
}
@ -56,7 +39,6 @@ std::uint32_t ElfReader::_readU32At(FileByteOffset fileByteOffset) const {
}
std::uint64_t ElfReader::_readU64At(FileByteOffset fileByteOffset) const {
_loadFileIntoBuffer();
if (fileByteOffset + 8 > _fileBuffer.size()) {
throw RelinkerException("FileByteOffset out of bounds", fileByteOffset);
}
@ -66,17 +48,15 @@ std::uint64_t ElfReader::_readU64At(FileByteOffset fileByteOffset) const {
}
ElfHeader ElfReader::ReadHeader() const {
_loadFileIntoBuffer();
if (_fileBuffer.size() < 20) {
throw RelinkerException("File too small for ELF header");
}
if (_fileBuffer[0] != 0x7f || _fileBuffer[1] != 'E' ||
if (_fileBuffer[0] != 0x7f || _fileBuffer[1] != 'E' ||
_fileBuffer[2] != 'L' || _fileBuffer[3] != 'F') {
throw RelinkerException("Invalid ELF magic number");
}
ElfHeader header{};
header.Machine = _readU16At(0x12);
header.Type = _readU16At(0x10);
@ -95,7 +75,6 @@ ElfHeader ElfReader::ReadHeader() const {
}
std::vector<ProgramHeader> ElfReader::ReadProgramHeaders() const {
_loadFileIntoBuffer();
const ElfHeader header = ReadHeader();
std::vector<ProgramHeader> headers;
@ -120,7 +99,6 @@ std::vector<ProgramHeader> ElfReader::ReadProgramHeaders() const {
}
std::vector<SectionHeader> ElfReader::ReadSectionHeaders() const {
_loadFileIntoBuffer();
const ElfHeader header = ReadHeader();
std::vector<SectionHeader> headers;
@ -169,8 +147,6 @@ std::string ElfReader::_resolveShdrName(std::uint32_t nameOffset, const ElfHeade
}
std::vector<DynamicTag> ElfReader::ReadDynamicTags(const ProgramHeader& dynamicHeader) const {
_loadFileIntoBuffer();
std::vector<DynamicTag> tags;
FileByteOffset offset = dynamicHeader.Offset;
const FileByteOffset end = dynamicHeader.Offset + dynamicHeader.FileSize;
@ -192,7 +168,6 @@ std::vector<DynamicTag> ElfReader::ReadDynamicTags(const ProgramHeader& dynamicH
}
FileByteOffset ElfReader::TranslateVirtualAddress(VirtualAddress address) const {
_loadFileIntoBuffer();
const ElfHeader header = ReadHeader();
FileByteOffset offset = header.ProgramHeaderOffset;
@ -216,29 +191,26 @@ FileByteOffset ElfReader::TranslateVirtualAddress(VirtualAddress address) const
}
std::vector<std::uint8_t> ElfReader::ReadSection(const SectionHeader& header) const {
_loadFileIntoBuffer();
if (header.Offset + header.SectionSize > _fileBuffer.size()) {
throw RelinkerException("Section offset out of bounds", header.Offset);
}
std::vector<std::uint8_t> data(_fileBuffer.begin() + header.Offset, _fileBuffer.begin() + header.Offset + header.SectionSize);
return data;
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 {
_loadFileIntoBuffer();
if (header.Offset + header.FileSize > _fileBuffer.size()) {
throw RelinkerException("Segment offset out of bounds", header.Offset);
}
std::vector<std::uint8_t> data(_fileBuffer.begin() + header.Offset, _fileBuffer.begin() + header.Offset + header.FileSize);
return data;
return std::vector<std::uint8_t>(
_fileBuffer.begin() + header.Offset,
_fileBuffer.begin() + header.Offset + header.FileSize);
}
std::uint64_t ElfReader::GetFileSize() const {
_loadFileIntoBuffer();
return _fileBuffer.size();
}

View File

@ -1,6 +1,5 @@
#include <relinker/pipeline/RelinkerPipeline.hpp>
#include <relinker/analysis/ValidationPolicy.hpp>
#include <fstream>
#include <sstream>
#include <iostream>
#include <cstring>
@ -14,8 +13,7 @@ RelinkerPipeline::RelinkerPipeline(
std::shared_ptr<ICallSiteResolver> callSiteResolver,
std::shared_ptr<IValidationPolicy> validationPolicy,
std::shared_ptr<ISysVDynamicSectionBuilder> dynamicSectionBuilder,
std::shared_ptr<ICallRegistryWriter> callRegistryWriter,
std::shared_ptr<IElfPatcher> elfPatcher)
std::shared_ptr<ICallRegistryWriter> callRegistryWriter)
: _elfReader(std::move(elfReader))
, _dynlibParser(std::move(dynlibParser))
, _syscallScanner(std::move(syscallScanner))
@ -23,7 +21,6 @@ RelinkerPipeline::RelinkerPipeline(
, _validationPolicy(std::move(validationPolicy))
, _dynamicSectionBuilder(std::move(dynamicSectionBuilder))
, _callRegistryWriter(std::move(callRegistryWriter))
, _elfPatcher(std::move(elfPatcher))
{}
std::string RelinkerPipeline::_relocationTypeName(std::uint32_t type) {
@ -40,18 +37,7 @@ std::string RelinkerPipeline::_relocationTypeName(std::uint32_t type) {
}
}
void RelinkerPipeline::_writeFile(const std::string& path, const std::string& content) const {
std::ofstream f(path);
if (!f)
throw RelinkerException("Cannot open output file: " + path);
f << content;
}
void RelinkerPipeline::Run(
const std::string& inputElfPath,
const std::string& outputRegistryPath,
const std::string& outputElfPath)
{
RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf) {
auto programHeaders = _elfReader->ReadProgramHeaders();
std::vector<std::uint8_t> sceDynlibData;
@ -128,11 +114,7 @@ void RelinkerPipeline::Run(
_validationPolicy->ValidateNidBelongsToLibrary(ref.Nid, ref.Library);
}
} else if (dynStrTabOffset != 0) {
std::ifstream wholeFile(inputElfPath, std::ios::binary | std::ios::ate);
const std::streamsize wholeSize = wholeFile.tellg();
wholeFile.seekg(0);
std::vector<std::uint8_t> raw(static_cast<std::size_t>(wholeSize));
wholeFile.read(reinterpret_cast<char*>(raw.data()), wholeSize);
const std::vector<std::uint8_t>& raw = _elfReader->GetRawBytes();
auto readCStr = [&](FileByteOffset strOff) -> std::string {
std::string result;
@ -142,7 +124,7 @@ void RelinkerPipeline::Run(
return result;
};
for (auto&[fst, snd] : neededLibraryNamesByStrOffset) {
for (auto& [fst, snd] : neededLibraryNamesByStrOffset) {
snd = readCStr(fst);
neededLibraries.push_back(snd);
if (policy) policy->RegisterLibraryImport(snd);
@ -220,12 +202,9 @@ void RelinkerPipeline::Run(
entries.push_back(std::move(entry));
}
_writeFile(outputRegistryPath, _callRegistryWriter->WriteCallRegistry(entries));
_elfPatcher->PatchAndWrite(inputElfPath, outputElfPath, programHeaders, dynSection);
std::cout << "OK: " << entries.size() << " NID references processed\n";
std::cout << "Registry: " << outputRegistryPath << "\n";
std::cout << "Output ELF: " << outputElfPath << "\n";
return RelinkResult{std::move(entries), std::move(programHeaders), std::move(dynSection)};
}
}