From 7728efce67f12efccff0e3fe30ff23f7fd74505c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 11 Oct 2023 10:12:20 -0700 Subject: [PATCH] kern: implement support for applying relr relocations --- .../mesosphere/init/kern_init_elf64.hpp | 21 +++++ .../source/init/kern_init_elf.cpp | 87 ++++++++++++++++--- 2 files changed, 94 insertions(+), 14 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/init/kern_init_elf64.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_elf64.hpp index 6b507f74c..76d858426 100644 --- a/libraries/libmesosphere/include/mesosphere/init/kern_init_elf64.hpp +++ b/libraries/libmesosphere/include/mesosphere/init/kern_init_elf64.hpp @@ -114,6 +114,23 @@ namespace ams::kern::init::Elf::Elf64 { } }; + class Relr { + private: + Xword m_info; + public: + constexpr ALWAYS_INLINE bool IsLocation() const { + return (m_info & 1) == 0; + } + + constexpr ALWAYS_INLINE Xword GetLocation() const { + return m_info; + } + + constexpr ALWAYS_INLINE Xword GetBitmap() const { + return m_info >> 1; + } + }; + enum DynamicTag { DT_NULL = 0, DT_RELA = 7, @@ -121,6 +138,10 @@ namespace ams::kern::init::Elf::Elf64 { DT_REL = 17, DT_RELENT = 19, + DT_RELRSZ = 35, + DT_RELR = 36, + DT_RELRENT = 37, + DT_RELACOUNT = 0x6ffffff9, DT_RELCOUNT = 0x6ffffffa }; diff --git a/libraries/libmesosphere/source/init/kern_init_elf.cpp b/libraries/libmesosphere/source/init/kern_init_elf.cpp index 96ac6b5ee..a9ef3666d 100644 --- a/libraries/libmesosphere/source/init/kern_init_elf.cpp +++ b/libraries/libmesosphere/source/init/kern_init_elf.cpp @@ -21,10 +21,13 @@ namespace ams::kern::init::Elf { void ApplyRelocations(uintptr_t base_address, const Dyn *dynamic) { uintptr_t dyn_rel = 0; uintptr_t dyn_rela = 0; + uintptr_t dyn_relr = 0; uintptr_t rel_count = 0; uintptr_t rela_count = 0; + uintptr_t relr_sz = 0; uintptr_t rel_ent = 0; uintptr_t rela_ent = 0; + uintptr_t relr_ent = 0; /* Iterate over all tags, identifying important extents. */ for (const Dyn *cur_entry = dynamic; cur_entry->GetTag() != DT_NULL; cur_entry++) { @@ -35,43 +38,99 @@ namespace ams::kern::init::Elf { case DT_RELA: dyn_rela = base_address + cur_entry->GetPtr(); break; + case DT_RELR: + dyn_relr = base_address + cur_entry->GetPtr(); + break; case DT_RELENT: rel_ent = cur_entry->GetValue(); break; case DT_RELAENT: rela_ent = cur_entry->GetValue(); break; + case DT_RELRENT: + relr_ent = cur_entry->GetValue(); + break; case DT_RELCOUNT: rel_count = cur_entry->GetValue(); break; case DT_RELACOUNT: rela_count = cur_entry->GetValue(); break; + case DT_RELRSZ: + relr_sz = cur_entry->GetValue(); + break; } } /* Apply all Rel relocations */ - for (size_t i = 0; i < rel_count; i++) { - const auto &rel = *reinterpret_cast(dyn_rel + rel_ent * i); + if (rel_count > 0) { + /* Check that the rel relocations are applyable. */ + MESOSPHERE_INIT_ABORT_UNLESS(dyn_rel != 0); + MESOSPHERE_INIT_ABORT_UNLESS(rel_ent == sizeof(Elf::Rel)); - /* Only allow architecture-specific relocations. */ - while (rel.GetType() != R_ARCHITECTURE_RELATIVE) { /* ... */ } + for (size_t i = 0; i < rel_count; ++i) { + const auto &rel = reinterpret_cast(dyn_rel)[i]; - /* Apply the relocation. */ - Elf::Addr *target_address = reinterpret_cast(base_address + rel.GetOffset()); - *target_address += base_address; + /* Only allow architecture-specific relocations. */ + while (rel.GetType() != R_ARCHITECTURE_RELATIVE) { /* ... */ } + + /* Apply the relocation. */ + Elf::Addr *target_address = reinterpret_cast(base_address + rel.GetOffset()); + *target_address += base_address; + } } /* Apply all Rela relocations. */ - for (size_t i = 0; i < rela_count; i++) { - const auto &rela = *reinterpret_cast(dyn_rela + rela_ent * i); + if (rela_count > 0) { + /* Check that the rela relocations are applyable. */ + MESOSPHERE_INIT_ABORT_UNLESS(dyn_rela != 0); + MESOSPHERE_INIT_ABORT_UNLESS(rela_ent == sizeof(Elf::Rela)); - /* Only allow architecture-specific relocations. */ - while (rela.GetType() != R_ARCHITECTURE_RELATIVE) { /* ... */ } + for (size_t i = 0; i < rela_count; ++i) { + const auto &rela = reinterpret_cast(dyn_rela)[i]; - /* Apply the relocation. */ - Elf::Addr *target_address = reinterpret_cast(base_address + rela.GetOffset()); - *target_address = base_address + rela.GetAddend(); + /* Only allow architecture-specific relocations. */ + while (rela.GetType() != R_ARCHITECTURE_RELATIVE) { /* ... */ } + + /* Apply the relocation. */ + Elf::Addr *target_address = reinterpret_cast(base_address + rela.GetOffset()); + *target_address = base_address + rela.GetAddend(); + } + } + + /* Apply all Relr relocations. */ + if (relr_sz >= sizeof(Elf::Relr)) { + /* Check that the relr relocations are applyable. */ + MESOSPHERE_INIT_ABORT_UNLESS(dyn_relr != 0); + MESOSPHERE_INIT_ABORT_UNLESS(relr_ent == sizeof(Elf::Relr)); + + const size_t relr_count = relr_sz / sizeof(Elf::Relr); + + Elf::Addr *where = nullptr; + for (size_t i = 0; i < relr_count; ++i) { + const auto &relr = reinterpret_cast(dyn_relr)[i]; + + if (relr.IsLocation()) { + /* Update location. */ + where = reinterpret_cast(base_address + relr.GetLocation()); + + /* Apply the relocation. */ + *(where++) += base_address; + } else { + /* Get the bitmap. */ + u64 bitmap = relr.GetBitmap(); + + /* Apply all relocations. */ + while (bitmap != 0) { + const u64 next = util::CountTrailingZeros(bitmap); + bitmap &= ~(static_cast(1) << next); + where[next] += base_address; + } + + /* Advance. */ + where += BITSIZEOF(bitmap) - 1; + } + } } }