From 850eae37ebbdd84f3802cbe15b44b1d0b56c788a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 4 Aug 2026 00:16:14 +0300 Subject: [PATCH] refactor(core): separate io from domain logic + di --- CMakeLists.txt | 3 + core/domain/CMakeLists.txt | 2 + core/domain/include/domain/Types.hpp | 109 ++++++++++++++++++ core/elfpatcher/CMakeLists.txt | 5 + .../include/elfpatcher}/ElfPatcher.hpp | 25 ++-- .../include/elfpatcher/IElfPatcher.hpp | 23 ++++ .../output => elfpatcher/src}/ElfPatcher.cpp | 50 +++----- core/io/CMakeLists.txt | 6 + core/io/include/io/FileReader.hpp | 15 +++ core/io/include/io/FileWriter.hpp | 16 +++ core/io/include/io/IFileReader.hpp | 19 +++ core/io/include/io/IFileWriter.hpp | 20 ++++ core/io/src/FileReader.cpp | 19 +++ core/io/src/FileWriter.cpp | 25 ++++ core/relinker/CMakeLists.txt | 2 +- .../relinker/domain/ICallRegistryWriter.hpp | 11 -- .../include/relinker/domain/IElfPatcher.hpp | 25 ---- .../include/relinker/domain/IElfReader.hpp | 1 + .../relinker/domain/IRelinkerPipeline.hpp | 10 +- .../domain/ISysVDynamicSectionBuilder.hpp | 8 -- .../include/relinker/domain/RelinkResult.hpp | 17 +++ .../include/relinker/domain/Types.hpp | 94 +++------------ .../include/relinker/parsing/ElfReader.hpp | 7 +- .../relinker/pipeline/RelinkerPipeline.hpp | 12 +- core/relinker/src/main.cpp | 27 +++-- core/relinker/src/parsing/ElfReader.cpp | 56 +++------ .../src/pipeline/RelinkerPipeline.cpp | 33 +----- 27 files changed, 374 insertions(+), 266 deletions(-) create mode 100644 core/domain/CMakeLists.txt create mode 100644 core/domain/include/domain/Types.hpp create mode 100644 core/elfpatcher/CMakeLists.txt rename core/{relinker/include/relinker/output => elfpatcher/include/elfpatcher}/ElfPatcher.hpp (78%) create mode 100644 core/elfpatcher/include/elfpatcher/IElfPatcher.hpp rename core/{relinker/src/output => elfpatcher/src}/ElfPatcher.cpp (84%) create mode 100644 core/io/CMakeLists.txt create mode 100644 core/io/include/io/FileReader.hpp create mode 100644 core/io/include/io/FileWriter.hpp create mode 100644 core/io/include/io/IFileReader.hpp create mode 100644 core/io/include/io/IFileWriter.hpp create mode 100644 core/io/src/FileReader.cpp create mode 100644 core/io/src/FileWriter.cpp delete mode 100644 core/relinker/include/relinker/domain/IElfPatcher.hpp create mode 100644 core/relinker/include/relinker/domain/RelinkResult.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9242a6b8b..7f3609d0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/core/domain/CMakeLists.txt b/core/domain/CMakeLists.txt new file mode 100644 index 000000000..f1fc44ad5 --- /dev/null +++ b/core/domain/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(domain INTERFACE) +target_include_directories(domain INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/core/domain/include/domain/Types.hpp b/core/domain/include/domain/Types.hpp new file mode 100644 index 000000000..fb0e44792 --- /dev/null +++ b/core/domain/include/domain/Types.hpp @@ -0,0 +1,109 @@ +#ifndef DOMAIN_TYPES_HPP +#define DOMAIN_TYPES_HPP + +#include +#include +#include +#include +#include +#include + +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 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 DynamicSegmentData; + std::vector DynSymData; + std::vector DynStrData; + std::vector RelaData; + std::vector RelaPltData; +}; + +struct CallRegistryEntry { + std::string Nid; + std::string Library; + std::string RelocationTypeString; + FileByteOffset RelocationOffset; + std::string TargetSection; + FileByteOffset TargetOffset; + std::vector CallSites; + bool CallSitesResolved; +}; + +} + +#endif diff --git a/core/elfpatcher/CMakeLists.txt b/core/elfpatcher/CMakeLists.txt new file mode 100644 index 000000000..9cd97f8ae --- /dev/null +++ b/core/elfpatcher/CMakeLists.txt @@ -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) diff --git a/core/relinker/include/relinker/output/ElfPatcher.hpp b/core/elfpatcher/include/elfpatcher/ElfPatcher.hpp similarity index 78% rename from core/relinker/include/relinker/output/ElfPatcher.hpp rename to core/elfpatcher/include/elfpatcher/ElfPatcher.hpp index be68b9bb0..e9a89855f 100644 --- a/core/relinker/include/relinker/output/ElfPatcher.hpp +++ b/core/elfpatcher/include/elfpatcher/ElfPatcher.hpp @@ -1,17 +1,16 @@ -#ifndef RELINKER_OUTPUT_ELFPATCHER_HPP -#define RELINKER_OUTPUT_ELFPATCHER_HPP +#ifndef ELFPATCHER_ELFPATCHER_HPP +#define ELFPATCHER_ELFPATCHER_HPP -#include +#include -namespace Relinker { +namespace Elfpatcher { class ElfPatcher : public IElfPatcher { public: - void PatchAndWrite( - const std::string& inputPath, - const std::string& outputPath, - const std::vector& originalHeaders, - const SysVDynamicSection& dynamicSection + std::vector Patch( + const std::vector& sourceElf, + const std::vector& 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 _buildEntryStub(std::uint64_t stubVaddr, std::uint64_t realEntryVaddr) const; void _writeInterp(std::vector& buf, std::size_t phEntOff, std::uint64_t interpOff, std::uint64_t interpSize) const; void _writeU16(std::vector& buf, std::size_t offset, std::uint16_t v) const; @@ -53,7 +52,7 @@ private: void _appendU64(std::vector& buf, std::uint64_t v) const; void _appendI64(std::vector& buf, std::int64_t v) const; void _appendDynEntry(std::vector& buf, std::int64_t tag, std::uint64_t val) const; - void _writeProgramHeader(std::vector& buf, std::size_t offset, const ProgramHeader& ph) const; + void _writeProgramHeader(std::vector& buf, std::size_t offset, const Domain::ProgramHeader& ph) const; [[nodiscard]] std::uint32_t _fixLoadFlags(std::uint32_t originalFlags) const; }; diff --git a/core/elfpatcher/include/elfpatcher/IElfPatcher.hpp b/core/elfpatcher/include/elfpatcher/IElfPatcher.hpp new file mode 100644 index 000000000..6d5ed82d9 --- /dev/null +++ b/core/elfpatcher/include/elfpatcher/IElfPatcher.hpp @@ -0,0 +1,23 @@ +#ifndef ELFPATCHER_IELFPATCHER_HPP +#define ELFPATCHER_IELFPATCHER_HPP + +#include +#include +#include + +namespace Elfpatcher { + +class IElfPatcher { +public: + virtual ~IElfPatcher() = default; + + virtual std::vector Patch( + const std::vector& sourceElf, + const std::vector& originalHeaders, + const Domain::SysVDynamicSection& dynamicSection + ) = 0; +}; + +} + +#endif diff --git a/core/relinker/src/output/ElfPatcher.cpp b/core/elfpatcher/src/ElfPatcher.cpp similarity index 84% rename from core/relinker/src/output/ElfPatcher.cpp rename to core/elfpatcher/src/ElfPatcher.cpp index bfb1ef49d..c9cc53943 100644 --- a/core/relinker/src/output/ElfPatcher.cpp +++ b/core/elfpatcher/src/ElfPatcher.cpp @@ -1,8 +1,7 @@ -#include -#include +#include #include -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& buf, std::int64_t ta _appendU64(buf, val); } -void ElfPatcher::_writeProgramHeader(std::vector& buf, std::size_t offset, const ProgramHeader& ph) const { +void ElfPatcher::_writeProgramHeader(std::vector& 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& 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& 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& buf, std::size_t phEntO _writeProgramHeader(buf, phEntOff, ph); } -std::vector ElfPatcher::_buildEntryStub(std::uint64_t stubVaddr, std::uint64_t realEntryVaddr) const { +std::vector ElfPatcher::_buildEntryStub(const std::uint64_t stubVaddr, const std::uint64_t realEntryVaddr) const { std::vector 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& originalHeaders, - const SysVDynamicSection& dynSection) +std::vector ElfPatcher::Patch( + const std::vector& sourceElf, + const std::vector& 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 buf(static_cast(fileSize)); - if (!in.read(reinterpret_cast(buf.data()), fileSize)) - throw RelinkerException("Cannot read input ELF: " + inputPath); + std::vector 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(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(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(buf.data()), static_cast(buf.size())); - if (!out) - throw RelinkerException("Failed to write output ELF: " + outputPath); + return buf; } } diff --git a/core/io/CMakeLists.txt b/core/io/CMakeLists.txt new file mode 100644 index 000000000..776bcedc4 --- /dev/null +++ b/core/io/CMakeLists.txt @@ -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) diff --git a/core/io/include/io/FileReader.hpp b/core/io/include/io/FileReader.hpp new file mode 100644 index 000000000..bf5d2fa85 --- /dev/null +++ b/core/io/include/io/FileReader.hpp @@ -0,0 +1,15 @@ +#ifndef IO_FILEREADER_HPP +#define IO_FILEREADER_HPP + +#include + +namespace Io { + +class FileReader : public IFileReader { +public: + std::vector Read(const std::string& path) override; +}; + +} + +#endif diff --git a/core/io/include/io/FileWriter.hpp b/core/io/include/io/FileWriter.hpp new file mode 100644 index 000000000..9e0f2c81b --- /dev/null +++ b/core/io/include/io/FileWriter.hpp @@ -0,0 +1,16 @@ +#ifndef IO_FILEWRITER_HPP +#define IO_FILEWRITER_HPP + +#include + +namespace Io { + +class FileWriter : public IFileWriter { +public: + void Write(const std::string& path, const std::vector& data) override; + void Write(const std::string& path, const std::string& content) override; +}; + +} + +#endif diff --git a/core/io/include/io/IFileReader.hpp b/core/io/include/io/IFileReader.hpp new file mode 100644 index 000000000..f86f68e33 --- /dev/null +++ b/core/io/include/io/IFileReader.hpp @@ -0,0 +1,19 @@ +#ifndef IO_IFILEREADER_HPP +#define IO_IFILEREADER_HPP + +#include +#include +#include + +namespace Io { + +class IFileReader { +public: + virtual ~IFileReader() = default; + + virtual std::vector Read(const std::string& path) = 0; +}; + +} + +#endif diff --git a/core/io/include/io/IFileWriter.hpp b/core/io/include/io/IFileWriter.hpp new file mode 100644 index 000000000..cdb867a81 --- /dev/null +++ b/core/io/include/io/IFileWriter.hpp @@ -0,0 +1,20 @@ +#ifndef IO_IFILEWRITER_HPP +#define IO_IFILEWRITER_HPP + +#include +#include +#include + +namespace Io { + +class IFileWriter { +public: + virtual ~IFileWriter() = default; + + virtual void Write(const std::string& path, const std::vector& data) = 0; + virtual void Write(const std::string& path, const std::string& content) = 0; +}; + +} + +#endif diff --git a/core/io/src/FileReader.cpp b/core/io/src/FileReader.cpp new file mode 100644 index 000000000..65bff45a7 --- /dev/null +++ b/core/io/src/FileReader.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +namespace Io { + +std::vector 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 buf(static_cast(size)); + if (!f.read(reinterpret_cast(buf.data()), size)) + throw Domain::RelinkerException("Cannot read file: " + path); + return buf; +} + +} diff --git a/core/io/src/FileWriter.cpp b/core/io/src/FileWriter.cpp new file mode 100644 index 000000000..f30fdae52 --- /dev/null +++ b/core/io/src/FileWriter.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +namespace Io { + +void FileWriter::Write(const std::string& path, const std::vector& data) { + std::ofstream f(path, std::ios::binary); + if (!f) + throw Domain::RelinkerException("Cannot open output file: " + path); + f.write(reinterpret_cast(data.data()), static_cast(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); +} + +} diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 880363bde..c3f8e8c49 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -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) diff --git a/core/relinker/include/relinker/domain/ICallRegistryWriter.hpp b/core/relinker/include/relinker/domain/ICallRegistryWriter.hpp index 379681daa..cc88e831c 100644 --- a/core/relinker/include/relinker/domain/ICallRegistryWriter.hpp +++ b/core/relinker/include/relinker/domain/ICallRegistryWriter.hpp @@ -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 CallSites; - bool CallSitesResolved; -}; - class ICallRegistryWriter { public: virtual ~ICallRegistryWriter() = default; diff --git a/core/relinker/include/relinker/domain/IElfPatcher.hpp b/core/relinker/include/relinker/domain/IElfPatcher.hpp deleted file mode 100644 index 7e83bc24d..000000000 --- a/core/relinker/include/relinker/domain/IElfPatcher.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef RELINKER_DOMAIN_IELFPATCHER_HPP -#define RELINKER_DOMAIN_IELFPATCHER_HPP - -#include -#include -#include -#include - -namespace Relinker { - -class IElfPatcher { -public: - virtual ~IElfPatcher() = default; - - virtual void PatchAndWrite( - const std::string& inputPath, - const std::string& outputPath, - const std::vector& originalHeaders, - const SysVDynamicSection& dynamicSection - ) = 0; -}; - -} - -#endif diff --git a/core/relinker/include/relinker/domain/IElfReader.hpp b/core/relinker/include/relinker/domain/IElfReader.hpp index 640a37f6e..f528b3945 100644 --- a/core/relinker/include/relinker/domain/IElfReader.hpp +++ b/core/relinker/include/relinker/domain/IElfReader.hpp @@ -19,6 +19,7 @@ public: [[nodiscard]] virtual std::vector ReadSection(const SectionHeader& header) const = 0; [[nodiscard]] virtual std::vector ReadSegment(const ProgramHeader& header) const = 0; [[nodiscard]] virtual std::uint64_t GetFileSize() const = 0; + [[nodiscard]] virtual const std::vector& GetRawBytes() const = 0; }; } diff --git a/core/relinker/include/relinker/domain/IRelinkerPipeline.hpp b/core/relinker/include/relinker/domain/IRelinkerPipeline.hpp index b961bfd68..33466e36b 100644 --- a/core/relinker/include/relinker/domain/IRelinkerPipeline.hpp +++ b/core/relinker/include/relinker/domain/IRelinkerPipeline.hpp @@ -1,7 +1,9 @@ #ifndef RELINKER_DOMAIN_IRELINKERPIPELINE_HPP #define RELINKER_DOMAIN_IRELINKERPIPELINE_HPP -#include +#include +#include +#include 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& sourceElf) = 0; }; } diff --git a/core/relinker/include/relinker/domain/ISysVDynamicSectionBuilder.hpp b/core/relinker/include/relinker/domain/ISysVDynamicSectionBuilder.hpp index 5253aeb8c..78337d32b 100644 --- a/core/relinker/include/relinker/domain/ISysVDynamicSectionBuilder.hpp +++ b/core/relinker/include/relinker/domain/ISysVDynamicSectionBuilder.hpp @@ -6,14 +6,6 @@ namespace Relinker { -struct SysVDynamicSection { - std::vector DynamicSegmentData; - std::vector DynSymData; - std::vector DynStrData; - std::vector RelaData; - std::vector RelaPltData; -}; - class ISysVDynamicSectionBuilder { public: virtual ~ISysVDynamicSectionBuilder() = default; diff --git a/core/relinker/include/relinker/domain/RelinkResult.hpp b/core/relinker/include/relinker/domain/RelinkResult.hpp new file mode 100644 index 000000000..088bdb697 --- /dev/null +++ b/core/relinker/include/relinker/domain/RelinkResult.hpp @@ -0,0 +1,17 @@ +#ifndef RELINKER_DOMAIN_RELINKRESULT_HPP +#define RELINKER_DOMAIN_RELINKRESULT_HPP + +#include +#include + +namespace Relinker { + +struct RelinkResult { + std::vector RegistryEntries; + std::vector OriginalHeaders; + SysVDynamicSection DynamicSection; +}; + +} + +#endif diff --git a/core/relinker/include/relinker/domain/Types.hpp b/core/relinker/include/relinker/domain/Types.hpp index 866f9f05a..0cae3ed17 100644 --- a/core/relinker/include/relinker/domain/Types.hpp +++ b/core/relinker/include/relinker/domain/Types.hpp @@ -1,89 +1,23 @@ #ifndef RELINKER_DOMAIN_TYPES_HPP #define RELINKER_DOMAIN_TYPES_HPP -#include -#include -#include -#include -#include -#include +#include 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 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; } diff --git a/core/relinker/include/relinker/parsing/ElfReader.hpp b/core/relinker/include/relinker/parsing/ElfReader.hpp index d7e750153..e3c82900a 100644 --- a/core/relinker/include/relinker/parsing/ElfReader.hpp +++ b/core/relinker/include/relinker/parsing/ElfReader.hpp @@ -8,7 +8,7 @@ namespace Relinker { class ElfReader : public IElfReader { public: - explicit ElfReader(const std::string& filePath); + explicit ElfReader(std::vector fileBuffer); ElfHeader ReadHeader() const override; std::vector ReadProgramHeaders() const override; @@ -18,12 +18,11 @@ public: std::vector ReadSection(const SectionHeader& header) const override; std::vector ReadSegment(const ProgramHeader& header) const override; std::uint64_t GetFileSize() const override; + const std::vector& GetRawBytes() const override; private: - std::string _filePath; - mutable std::vector _fileBuffer; + std::vector _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; diff --git a/core/relinker/include/relinker/pipeline/RelinkerPipeline.hpp b/core/relinker/include/relinker/pipeline/RelinkerPipeline.hpp index 8874feb9f..b29f9c4f5 100644 --- a/core/relinker/include/relinker/pipeline/RelinkerPipeline.hpp +++ b/core/relinker/include/relinker/pipeline/RelinkerPipeline.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #include namespace Relinker { @@ -24,15 +23,10 @@ public: std::shared_ptr callSiteResolver, std::shared_ptr validationPolicy, std::shared_ptr dynamicSectionBuilder, - std::shared_ptr callRegistryWriter, - std::shared_ptr elfPatcher + std::shared_ptr callRegistryWriter ); - void Run( - const std::string& inputElfPath, - const std::string& outputRegistryPath, - const std::string& outputElfPath - ) override; + RelinkResult Relink(const std::vector& sourceElf) override; private: std::shared_ptr _elfReader; @@ -42,7 +36,6 @@ private: std::shared_ptr _validationPolicy; std::shared_ptr _dynamicSectionBuilder; std::shared_ptr _callRegistryWriter; - std::shared_ptr _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; }; } diff --git a/core/relinker/src/main.cpp b/core/relinker/src/main.cpp index 7af6a889c..e003b175b 100644 --- a/core/relinker/src/main.cpp +++ b/core/relinker/src/main.cpp @@ -1,4 +1,7 @@ -#include +#include +#include +#include +#include #include #include #include @@ -7,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -23,7 +25,12 @@ int main(const int argc, char* argv[]) { const std::string outputElfPath = argv[3]; try { - auto elfReader = std::make_shared(inputPath); + Io::FileReader fileReader; + Io::FileWriter fileWriter; + + auto sourceBytes = fileReader.Read(inputPath); + + auto elfReader = std::make_shared(sourceBytes); auto programHeaders = elfReader->ReadProgramHeaders(); std::vector sceDynlibData; @@ -40,13 +47,19 @@ int main(const int argc, char* argv[]) { Relinker::MakeCallSiteResolver(), std::make_shared(), std::make_shared(), - std::make_shared(), - std::make_shared() + std::make_shared() ); - pipeline->Run(inputPath, registryPath, outputElfPath); + auto result = pipeline->Relink(sourceBytes); - } catch (const Relinker::RelinkerException& e) { + auto callRegistryWriter = std::make_shared(); + 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 << ")"; diff --git a/core/relinker/src/parsing/ElfReader.cpp b/core/relinker/src/parsing/ElfReader.cpp index 81dc80915..e45405402 100644 --- a/core/relinker/src/parsing/ElfReader.cpp +++ b/core/relinker/src/parsing/ElfReader.cpp @@ -1,34 +1,19 @@ #include #include -#include #include namespace Relinker { -ElfReader::ElfReader(const std::string& filePath) : _filePath(filePath) { +ElfReader::ElfReader(std::vector 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(_fileBuffer.data()), size)) { - throw RelinkerException("Cannot read file: " + _filePath); - } +const std::vector& 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 ElfReader::ReadProgramHeaders() const { - _loadFileIntoBuffer(); const ElfHeader header = ReadHeader(); std::vector headers; @@ -120,7 +99,6 @@ std::vector ElfReader::ReadProgramHeaders() const { } std::vector ElfReader::ReadSectionHeaders() const { - _loadFileIntoBuffer(); const ElfHeader header = ReadHeader(); std::vector headers; @@ -169,8 +147,6 @@ std::string ElfReader::_resolveShdrName(std::uint32_t nameOffset, const ElfHeade } std::vector ElfReader::ReadDynamicTags(const ProgramHeader& dynamicHeader) const { - _loadFileIntoBuffer(); - std::vector tags; FileByteOffset offset = dynamicHeader.Offset; const FileByteOffset end = dynamicHeader.Offset + dynamicHeader.FileSize; @@ -192,7 +168,6 @@ std::vector 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 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 data(_fileBuffer.begin() + header.Offset, _fileBuffer.begin() + header.Offset + header.SectionSize); - return data; + return std::vector( + _fileBuffer.begin() + header.Offset, + _fileBuffer.begin() + header.Offset + header.SectionSize); } std::vector 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 data(_fileBuffer.begin() + header.Offset, _fileBuffer.begin() + header.Offset + header.FileSize); - return data; + return std::vector( + _fileBuffer.begin() + header.Offset, + _fileBuffer.begin() + header.Offset + header.FileSize); } std::uint64_t ElfReader::GetFileSize() const { - _loadFileIntoBuffer(); return _fileBuffer.size(); } diff --git a/core/relinker/src/pipeline/RelinkerPipeline.cpp b/core/relinker/src/pipeline/RelinkerPipeline.cpp index f58eda9f5..d84cd6d6a 100644 --- a/core/relinker/src/pipeline/RelinkerPipeline.cpp +++ b/core/relinker/src/pipeline/RelinkerPipeline.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -14,8 +13,7 @@ RelinkerPipeline::RelinkerPipeline( std::shared_ptr callSiteResolver, std::shared_ptr validationPolicy, std::shared_ptr dynamicSectionBuilder, - std::shared_ptr callRegistryWriter, - std::shared_ptr elfPatcher) + std::shared_ptr 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& sourceElf) { auto programHeaders = _elfReader->ReadProgramHeaders(); std::vector 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 raw(static_cast(wholeSize)); - wholeFile.read(reinterpret_cast(raw.data()), wholeSize); + const std::vector& 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)}; } }