kern: implement support for applying relr relocations

This commit is contained in:
Michael Scire 2023-10-11 10:12:20 -07:00
parent bc6d207469
commit 7728efce67
2 changed files with 94 additions and 14 deletions

View file

@ -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
};

View file

@ -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,24 +38,38 @@ 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<const Elf::Rel *>(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));
for (size_t i = 0; i < rel_count; ++i) {
const auto &rel = reinterpret_cast<const Elf::Rel *>(dyn_rel)[i];
/* Only allow architecture-specific relocations. */
while (rel.GetType() != R_ARCHITECTURE_RELATIVE) { /* ... */ }
@ -61,10 +78,16 @@ namespace ams::kern::init::Elf {
Elf::Addr *target_address = reinterpret_cast<Elf::Addr *>(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<const Elf::Rela *>(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));
for (size_t i = 0; i < rela_count; ++i) {
const auto &rela = reinterpret_cast<const Elf::Rela *>(dyn_rela)[i];
/* Only allow architecture-specific relocations. */
while (rela.GetType() != R_ARCHITECTURE_RELATIVE) { /* ... */ }
@ -75,6 +98,42 @@ namespace ams::kern::init::Elf {
}
}
/* 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<const Elf::Relr *>(dyn_relr)[i];
if (relr.IsLocation()) {
/* Update location. */
where = reinterpret_cast<Elf::Addr *>(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<u64>(1) << next);
where[next] += base_address;
}
/* Advance. */
where += BITSIZEOF(bitmap) - 1;
}
}
}
}
void CallInitArrayFuncs(uintptr_t init_array_start, uintptr_t init_array_end) {
for (uintptr_t cur_entry = init_array_start; cur_entry < init_array_end; cur_entry += sizeof(void *)) {
(*(void (**)())(cur_entry))();