diff --git a/rpcsx-os/linker.cpp b/rpcsx-os/linker.cpp index 97bbc201d..fc4dc60af 100644 --- a/rpcsx-os/linker.cpp +++ b/rpcsx-os/linker.cpp @@ -7,8 +7,10 @@ #include "orbis/uio.hpp" #include "vfs.hpp" #include "vm.hpp" +#include #include #include +#include #include #include #include @@ -17,6 +19,76 @@ using orbis::utils::Ref; +static std::vector unself(const std::byte *image, std::size_t size) { + struct [[gnu::packed]] Header { + std::uint32_t magic; + std::uint32_t unk; + std::uint8_t category; + std::uint8_t programType; + std::uint16_t padding; + std::uint16_t headerSize; + std::uint16_t signSize; + std::uint32_t fileSize; + std::uint32_t padding2; + std::uint16_t segmentCount; + std::uint16_t unk1; + std::uint32_t padding3; + }; + + static_assert(0x18 == 24); + + static_assert(sizeof(Header) == 0x20); + struct [[gnu::packed]] Segment { + std::uint64_t flags; + std::uint64_t offset; + std::uint64_t encryptedSize; + std::uint64_t decryptedSize; + }; + static_assert(sizeof(Segment) == 0x20); + + auto header = std::bit_cast
(image); + auto segments = std::bit_cast(image + sizeof(Header)); + + auto elfOffset = sizeof(Header) + sizeof(Segment) * header->segmentCount; + std::vector result; + result.reserve(header->fileSize); + result.resize(sizeof(Elf64_Ehdr)); + + auto ehdr = std::bit_cast(image + elfOffset); + auto phdrs = std::bit_cast(image + elfOffset + ehdr->e_phoff); + std::memcpy(result.data(), ehdr, sizeof(Elf64_Ehdr)); + + auto phdrEndOffset = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum; + if (result.size() < phdrEndOffset) { + result.resize(phdrEndOffset); + } + + for (std::size_t i = 0; i < ehdr->e_phnum; ++i) { + std::memcpy(result.data() + ehdr->e_phoff + ehdr->e_phentsize * i, + image + elfOffset + ehdr->e_phoff + ehdr->e_phentsize * i, + sizeof(Elf64_Phdr)); + } + + for (std::size_t i = 0; i < header->segmentCount; ++i) { + auto &segment = segments[i]; + if (~segment.flags & 0x800) { + continue; + } + + auto &phdr = phdrs[segment.flags >> 20]; + + auto endOffset = phdr.p_offset + segment.decryptedSize; + if (result.size() < endOffset) { + result.resize(endOffset); + } + + std::memcpy(result.data() + phdr.p_offset, image + segment.offset, + segment.decryptedSize); + } + + return result; +} + std::uint64_t rx::linker::encodeFid(std::string_view fid) { static const char suffix[] = "\x51\x8D\x64\xA6\x35\xDE\xD8\xC1\xE6\xB0\x39\xB1\xC3\xE5\x52\x30"; @@ -882,6 +954,11 @@ Ref rx::linker::loadModuleFile(std::string_view path, } } + if (image[0] != std::byte{'\x7f'} || image[1] != std::byte{'E'} || + image[2] != std::byte{'L'} || image[3] != std::byte{'F'}) { + image = unself(image.data(), image.size()); + } + return loadModule(image, thread->tproc); }