diff --git a/core/relinker/include/relinker/output/ElfPatcher.hpp b/core/relinker/include/relinker/output/ElfPatcher.hpp index b49d1b2a1..681382666 100644 --- a/core/relinker/include/relinker/output/ElfPatcher.hpp +++ b/core/relinker/include/relinker/output/ElfPatcher.hpp @@ -17,6 +17,7 @@ public: private: static constexpr std::uint32_t PT_LOAD = 1; static constexpr std::uint32_t PT_DYNAMIC = 2; + static constexpr std::uint32_t PT_INTERP = 3; static constexpr std::uint32_t PT_GNU_RELRO = 0x6474e552; static constexpr std::uint32_t PF_X = 0x1; static constexpr std::uint32_t PF_W = 0x2; @@ -42,6 +43,8 @@ private: [[nodiscard]] bool _isSceSpecificSegment(std::uint32_t type) const; [[nodiscard]] bool _isNullPageLoad(const ProgramHeader& ph) const; [[nodiscard]] 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; void _writeU32(std::vector& buf, std::size_t offset, std::uint32_t v) const; void _writeU64(std::vector& buf, std::size_t offset, std::uint64_t v) const; diff --git a/core/relinker/src/output/ElfPatcher.cpp b/core/relinker/src/output/ElfPatcher.cpp index 98407c706..5ae2bc514 100644 --- a/core/relinker/src/output/ElfPatcher.cpp +++ b/core/relinker/src/output/ElfPatcher.cpp @@ -15,11 +15,11 @@ void ElfPatcher::_writeU16(std::vector& buf, std::size_t offset, s std::memcpy(buf.data() + offset, &v, 2); } -void ElfPatcher::_writeU32(std::vector& buf, std::size_t offset, std::uint32_t v) const { +void ElfPatcher::_writeU32(std::vector& buf, const std::size_t offset, std::uint32_t v) const { std::memcpy(buf.data() + offset, &v, 4); } -void ElfPatcher::_writeU64(std::vector& buf, std::size_t offset, std::uint64_t v) const { +void ElfPatcher::_writeU64(std::vector& buf, const std::size_t offset, std::uint64_t v) const { std::memcpy(buf.data() + offset, &v, 8); } @@ -56,7 +56,7 @@ bool ElfPatcher::_isNullPageLoad(const ProgramHeader& ph) const { ProgramHeader ElfPatcher::_makeLoadHeader(std::uint64_t offset, std::uint64_t size) const { ProgramHeader ph{}; ph.Type = PT_LOAD; - ph.Flags = PF_R; + ph.Flags = PF_R | PF_X; ph.Offset = offset; ph.MappedAddress = offset; ph.PhysicalAddress = offset; @@ -66,6 +66,40 @@ ProgramHeader ElfPatcher::_makeLoadHeader(std::uint64_t offset, std::uint64_t si return ph; } +void ElfPatcher::_writeInterp(std::vector& buf, std::size_t phEntOff, std::uint64_t interpOff, std::uint64_t interpSize) const { + ProgramHeader ph{}; + ph.Type = PT_INTERP; + ph.Flags = PF_R; + ph.Offset = interpOff; + ph.MappedAddress = interpOff; + ph.PhysicalAddress = interpOff; + ph.FileSize = interpSize; + ph.MemorySize = interpSize; + ph.Alignment = 1; + _writeProgramHeader(buf, phEntOff, ph); +} + +std::vector ElfPatcher::_buildEntryStub(std::uint64_t stubVaddr, std::uint64_t realEntryVaddr) const { + std::vector s; + s.push_back(0x58); + s.push_back(0x48); s.push_back(0x89); s.push_back(0xe3); + s.push_back(0x48); s.push_back(0x83); s.push_back(0xec); s.push_back(0x30); + s.push_back(0x48); s.push_back(0x83); s.push_back(0xe4); s.push_back(0xf0); + s.push_back(0x89); s.push_back(0x04); s.push_back(0x24); + s.push_back(0x48); s.push_back(0x89); s.push_back(0x5c); s.push_back(0x24); s.push_back(0x08); + s.push_back(0x48); s.push_back(0x89); s.push_back(0xe7); + s.push_back(0x48); s.push_back(0x31); s.push_back(0xf6); + const std::uint64_t jmpInsnVaddr = stubVaddr + s.size(); + const std::uint64_t jmpNextVaddr = jmpInsnVaddr + 5; + const std::int32_t rel32 = static_cast(realEntryVaddr - jmpNextVaddr); + s.push_back(0xe9); + s.push_back(static_cast(rel32 & 0xff)); + s.push_back(static_cast((rel32 >> 8) & 0xff)); + s.push_back(static_cast((rel32 >> 16) & 0xff)); + s.push_back(static_cast((rel32 >> 24) & 0xff)); + return s; +} + std::uint32_t ElfPatcher::_fixLoadFlags(std::uint32_t originalFlags) const { std::uint32_t flags = originalFlags; if ((flags & (PF_R | PF_W | PF_X)) == 0) @@ -140,17 +174,42 @@ void ElfPatcher::PatchAndWrite( for (std::uint8_t b : dynSegBuf) buf.push_back(b); + const std::uint64_t realEntryVaddr = *reinterpret_cast(buf.data() + 24); + const std::uint64_t stubOff = static_cast(buf.size()); + const auto stubBytes = _buildEntryStub(stubOff, realEntryVaddr); + for (std::uint8_t b : stubBytes) + buf.push_back(b); + _writeU64(buf, 24, stubOff); + + static constexpr char kInterp[] = "/lib64/ld-linux-x86-64.so.2"; + const std::uint64_t interpOff = static_cast(buf.size()); + constexpr std::uint64_t interpSize = sizeof(kInterp); + for (std::size_t i = 0; i < interpSize; i++) + buf.push_back(static_cast(kInterp[i])); + const std::uint64_t extraBlockOff = dynStrOff; const std::uint64_t extraBlockSize = static_cast(buf.size()) - extraBlockOff; + std::uint16_t keptCount = 0; + for (const auto& ph : originalHeaders) { + if (_isSceSpecificSegment(ph.Type)) + continue; + if (ph.Type == PT_DYNAMIC) + continue; + keptCount++; + } + const std::uint16_t neededPh = keptCount + 3; + if (neededPh > phNum) + throw RelinkerException( + "Not enough program header slots: need " + std::to_string(neededPh) + + ", available " + std::to_string(phNum)); + std::uint16_t writtenPh = 0; for (const auto& ph : originalHeaders) { if (_isSceSpecificSegment(ph.Type)) continue; if (ph.Type == PT_DYNAMIC) continue; - if (writtenPh >= phNum) - break; const std::size_t phEntOff = static_cast(phOff) + writtenPh * phEntSize; if (ph.Type == PT_LOAD) { ProgramHeader fixed = ph; @@ -183,6 +242,12 @@ void ElfPatcher::PatchAndWrite( writtenPh++; } + { + const std::size_t phEntOff = static_cast(phOff) + writtenPh * phEntSize; + _writeInterp(buf, phEntOff, interpOff, interpSize); + writtenPh++; + } + _writeU16(buf, 56, writtenPh); std::ofstream out(outputPath, std::ios::binary);