#pragma once #include "kernel.hpp" #include "nid.hpp" #include "util.hpp" #include #include #include extern "C" { #include } class RtldMeta; // namespace to hold these commonly named classes // to prevent api conflict in the future namespace rtld { class ElfSymbolTable; class ElfSymbol; class ElfStringTable; } namespace { static UniquePtr newRtldMeta(uintptr_t imageBase, uintptr_t addr); static UniquePtr newSymbolTable(const RtldMeta *meta); static UniquePtr newStringTable(const RtldMeta *meta); static constexpr size_t SHARED_LIB_SECTION_SIZE = 0x18; static constexpr size_t SHARED_LIB_SIZE = 0x200; static constexpr size_t SHARED_OBJECT_SIZE = 0x188; static constexpr size_t RTLD_META_SIZE = 0x118; } enum class SectionType : uint32_t { UNKNOWN = 0, TEXT = 1, XOTEXT = 2, RODATA = 4, RELRO = 8, DATA = 16, INVALID = (uint32_t)-1 }; class SharedLibSection : public KernelObject { friend class SharedLib; static constexpr size_t TYPE_OFFSET = 8; static constexpr size_t ADDRESS_OFFSET = 8; static constexpr size_t LENGTH_OFFSET = 16; mutable SectionType _type; public: SharedLibSection() = default; SharedLibSection(uintptr_t addr) : KernelObject(addr), _type(SectionType::INVALID) {} SectionType type() const { if (_type == SectionType::INVALID) { _type = kread(get() + TYPE_OFFSET); } return _type; } uintptr_t start() const { return get(); } uintptr_t end() const { return start() + sectionLength(); } size_t sectionLength() const { return get(); } }; struct EhFrameInfo { uintptr_t hdr; size_t hdrSize; uintptr_t frame; size_t frameSize; }; class SharedLib : public KernelObject { static constexpr size_t DYNLIB_OFFSET = 0x148; static constexpr size_t HANDLE_OFFSET = 0x28; static constexpr size_t PATH_OFFSET = 0x8; static constexpr size_t NUM_SECTIONS_OFFSET = 0x48; static constexpr size_t SECTIONS_OFFSET = 0x40; static constexpr size_t IMAGEBASE_OFFSET = 0x30; static constexpr size_t EHFRAME_OFFSET = 0xf8; static constexpr size_t EHFRAME_HDR_SIZE_OFFSET = 0x100; static constexpr size_t EHFRAME_HDR_OFFSET = 0x108; static constexpr size_t EHFRAME_SIZE_OFFSET = 0x110; mutable String path; mutable Array sections; mutable UniquePtr meta; public: int pid; SharedLib(uintptr_t addr, int pid) : KernelObject(addr), path(nullptr), sections(nullptr), meta(nullptr), pid(pid) {} int handle() const { return get(); } StringView getPath() const { if (path.length() == 0) [[unlikely]] { path = getString(); } return {path.c_str(), path.length()}; } const Array &getSections() const { if (sections == nullptr) [[unlikely]] { size_t numSections = get(); uintptr_t ptr = get(); sections = Array{numSections}; for (size_t i = 0; i < numSections; i++) { sections[i] = SharedLibSection{ptr + (i * SharedLibSection::length)}; } } return sections; } const SharedLibSection *getTextSection() const { for (SharedLibSection §ion : getSections()) { SectionType type = section.type(); if (type == SectionType::XOTEXT || type == SectionType::TEXT) { return §ion; } } return nullptr; } void setTextAddress(uintptr_t addr) { const SharedLibSection *section = getTextSection(); kwrite(section->address() + SharedLibSection::ADDRESS_OFFSET, addr); } void setRoDataAddress(uintptr_t addr) { const SharedLibSection *section = getRoDataSection(); kwrite(section->address() + SharedLibSection::ADDRESS_OFFSET, addr); } const SharedLibSection *getDataSection() const { for (SharedLibSection §ion : getSections()) { if (section.type() == SectionType::DATA) { return §ion; } } return nullptr; } const SharedLibSection *getRoDataSection() const { for (SharedLibSection §ion : getSections()) { if (section.type() == SectionType::RODATA) { return §ion; } } return nullptr; } uintptr_t imagebase() const { return get(); } void imagebase(uintptr_t base) { kwrite(address() + IMAGEBASE_OFFSET, base); } RtldMeta *getMetaData() const { if (meta == nullptr) [[unlikely]] { uintptr_t addr = getMetaDataAddress(); if (addr) { meta = newRtldMeta(imagebase(), addr); } else { puts("null metadata"); } } return meta.get(); } uintptr_t getMetaDataAddress() const { return get(); } EhFrameInfo getEhFrameInfo() const { return get(); } void setEhFrameInfo(const EhFrameInfo &info) const { kwrite(address() + EHFRAME_OFFSET, info); } uintptr_t getEhFrameHdr() const { return get(); } void setEhFrameHdr(uintptr_t value) { set(value); } size_t getEhFrameHdrSize() const { return get(); } void setEhFrameHdrSize(uintptr_t value) { set(value); } uintptr_t getEhFrame() const { return get(); } void setEhFrame(uintptr_t value) { set(value); } size_t getEhFrameSize() const { return get(); } void setEhFrameSize(uintptr_t value) { set(value); } }; class SharedLibIterable { uintptr_t addr; int pid; public: SharedLibIterable(decltype(nullptr)) : addr(), pid() {} SharedLibIterable(uintptr_t addr, int pid) : addr(addr), pid(pid) {} bool operator!=(decltype(nullptr)) const { return addr != 0; } UniquePtr operator*() { return new SharedLib{addr, pid}; } SharedLibIterable &operator++() { uintptr_t ptr = 0; kernel_copyout(addr, &ptr, sizeof(ptr)); addr = ptr; return *this; } uintptr_t getAddress() const noexcept { return addr; } }; struct SharedLibIterator { uintptr_t addr; int pid; SharedLibIterator(uintptr_t addr, int pid) : addr(addr), pid(pid) {} SharedLibIterable begin() const { return {addr, pid}; } decltype(nullptr) end() const { return nullptr; } }; class SharedObject : KernelObject { static constexpr size_t EBOOT_OFFSET = 0; mutable UniquePtr eboot; public: int pid; SharedObject(uintptr_t addr, int pid) : KernelObject(addr), eboot(nullptr), pid(pid) {} SharedLibIterator getLibs() const { return {get(), pid}; } SharedLib *getEboot() const { if (eboot == nullptr) [[unlikely]] { const auto ptr = get(); if (ptr == 0) [[unlikely]] { return nullptr; } eboot = new SharedLib{ptr, pid}; } return eboot.get(); } UniquePtr getLib(int handle) const { for (auto lib : getLibs()) { if (lib->handle() == handle) { return lib.release(); } } return nullptr; } UniquePtr getLib(const StringView &name) const { String fullname = name; if (!name.endswith(".sprx"_sv)) { fullname += ".sprx"_sv; } for (auto lib : getLibs()) { printf("lib: %s\n", lib->getPath().c_str()); if (lib->getPath().endswith(fullname)) { return lib.release(); } } return nullptr; } }; class RtldMeta : public KernelObject { static constexpr size_t TOTAL_SIZE_OFFSET = 0x20; static constexpr size_t SYMTAB_OFFSET = 0x28; static constexpr size_t SYMTAB_SIZE_OFFSET = 0x30; static constexpr size_t STRTAB_OFFSET = 0x38; static constexpr size_t STRTAB_SIZE_OFFSET = 0x40; static constexpr size_t PLTTAB_OFFSET = 0x48; static constexpr size_t PLTTAB_SIZE_OFFSET = 0x50; // mutable members for caching mutable UniquePtr symTable; mutable UniquePtr strTable; mutable Array pltTbl; public: uintptr_t imageBase; RtldMeta(uintptr_t imageBase, uintptr_t addr) : KernelObject(addr), symTable(nullptr), strTable(nullptr), pltTbl(nullptr), imageBase(imageBase) {} size_t totalSize() const { return get(); } uintptr_t symtab() const { return get(); } size_t symtabSize() const { return get(); } size_t nSymbols() const { return symtabSize() / sizeof(Elf64_Sym); } uintptr_t strtab() const { return get(); } size_t strtabSize() const { return get(); } uintptr_t plttab() const { return get(); } size_t pltTblSize() const { return get(); } size_t nPlt() const { return pltTblSize() / sizeof(Elf64_Rela); } const rtld::ElfSymbolTable &getSymbolTable() const { if (symTable == nullptr) { symTable = newSymbolTable(this); } return *symTable; } const rtld::ElfStringTable &getStringTable() const { if (strTable == nullptr) { strTable = newStringTable(this); } return *strTable; } const Array &getPltTable() const { if (pltTbl == nullptr) { pltTbl = Array(nPlt()); kernel_copyout(plttab(), pltTbl.begin(), pltTblSize()); } return pltTbl; } }; class rtld::ElfStringTable { size_t size; UniquePtr tbl; public: ElfStringTable(const RtldMeta *meta) : size(meta->strtabSize()), tbl(new char[size]) { kernel_copyout(meta->strtab(), tbl.get(), size); } ElfStringTable(const ElfStringTable &) = delete; ElfStringTable &operator=(const ElfStringTable &) = delete; ElfStringTable(ElfStringTable&&) noexcept = default; ElfStringTable &operator=(ElfStringTable&&) noexcept = default; ~ElfStringTable() noexcept = default; StringView operator[](size_t offset) const { #ifdef DEBUG if (offset >= size) [[unlikely]] { fatalf("offset %llu is out of bounds for size %llu\n", offset, size); } #endif return tbl.get() + offset; } Nid getNid(size_t offset) const { return Nid{tbl.get() + offset}; } const char *c_str() const noexcept { return tbl.get(); } }; class rtld::ElfSymbol : public Elf64_Sym { static constexpr auto EXPORT_MASK = 0x30; const RtldMeta *meta; public: ElfSymbol(decltype(nullptr)) : Elf64_Sym(), meta(nullptr) {} ElfSymbol(const RtldMeta *meta, const Elf64_Sym &sym) : Elf64_Sym(sym), meta(meta) {} StringView name() const { return meta->getStringTable()[st_name]; } bool exported() const { // exported if the binding is global or weak and shndx is not 0 return (st_info & EXPORT_MASK) && st_shndx != 0; } uintptr_t vaddr() const { return meta->imageBase + st_value; } Nid nid() const { return meta->getStringTable().getNid(st_name); } explicit operator bool() const { return meta; } }; class rtld::ElfSymbolTable { const RtldMeta *meta; UniquePtr symbols; size_t size; Elf64_Sym *begin() const { return symbols.get(); } Elf64_Sym *end() const { return symbols.get() + size; } public: ElfSymbolTable(const RtldMeta *meta) : meta(meta), symbols(new Elf64_Sym[meta->nSymbols()]), size(meta->nSymbols()) { kernel_copyout(meta->symtab(), symbols.get(), size * sizeof(Elf64_Sym)); } const ElfSymbol operator[](size_t i) const { #ifdef DEBUG if (i >= size) [[unlikely]] { fatalf("index %llu is out of bounds for length %llu\n", i, size); } #endif return {meta, symbols.get()[i]}; } const Elf64_Sym *getSymbol(size_t i) const { return symbols.get() + i; } ssize_t getSymbolIndex(const Nid &nid) const { const ssize_t len = static_cast(size); for (ssize_t i = 0; i < len; i++) { if (meta->getStringTable().getNid(symbols[i].st_name) == nid) { return i; } } return -1; } const ElfSymbol operator[](const Nid &nid) const { for (Elf64_Sym &esym : *this) { ElfSymbol sym{meta, esym}; if (!sym.exported()) { continue; } if (sym.nid() == nid) { return sym; } } return nullptr; } const ElfSymbol operator[](Nid &&nid) const { return this->operator[](nid); } size_t length() const { return size; } const Elf64_Sym *c_symbols() const noexcept { return symbols.get(); } }; namespace { static inline UniquePtr newRtldMeta(uintptr_t imageBase, uintptr_t addr) { return new RtldMeta(imageBase, addr); } static inline UniquePtr newSymbolTable(const RtldMeta *meta) { return new rtld::ElfSymbolTable(meta); } static inline UniquePtr newStringTable(const RtldMeta *meta) { return new rtld::ElfStringTable(meta); } }