crt0/dynamic: refactoring + add support for reprotecting relro segment

This commit is contained in:
fincs 2023-10-19 21:25:23 +02:00
parent 3a5d0dae35
commit 4ff1c52869
2 changed files with 98 additions and 48 deletions

View File

@ -1,34 +1,42 @@
#include "result.h"
#include "kernel/svc.h"
#include "runtime/diag.h"
#include <elf.h>
#include <string.h>
void __nx_dynamic(uintptr_t base, const Elf64_Dyn* dyn)
typedef struct Mod0Header {
u32 magic_mod0;
s32 dyn_offset;
s32 bss_start_offset;
s32 bss_end_offset;
s32 eh_frame_hdr_start_offset;
s32 eh_frame_hdr_end_offset;
s32 unused;
u32 magic_lny0;
s32 got_start_offset;
s32 got_end_offset;
u32 magic_lny1;
s32 relro_start_offset;
s32 relro_end_offset;
} Mod0Header;
NX_INLINE void* _dynResolveOffset(const Mod0Header* mod0, s32 offset)
{
const Elf64_Rela* rela = NULL;
u64 relasz = 0;
return (void*)((uintptr_t)mod0 + offset);
}
for (; dyn->d_tag != DT_NULL; dyn++)
{
switch (dyn->d_tag)
{
case DT_RELA:
rela = (const Elf64_Rela*)(base + dyn->d_un.d_ptr);
break;
case DT_RELASZ:
relasz = dyn->d_un.d_val / sizeof(Elf64_Rela);
break;
}
}
if (rela == NULL)
static void _dynProcessRela(uintptr_t base, const Elf64_Rela* rela, size_t relasz)
{
for (; relasz--; rela++) {
switch (ELF64_R_TYPE(rela->r_info)) {
default: {
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_BadReloc));
break;
}
for (; relasz--; rela++)
{
switch (ELF64_R_TYPE(rela->r_info))
{
case R_AARCH64_RELATIVE:
{
case R_AARCH64_RELATIVE: {
u64* ptr = (u64*)(base + rela->r_offset);
*ptr = base + rela->r_addend;
break;
@ -36,3 +44,57 @@ void __nx_dynamic(uintptr_t base, const Elf64_Dyn* dyn)
}
}
}
void __nx_dynamic(uintptr_t base, const Mod0Header* mod0)
{
// Return early if MOD0 header has been invalidated
if (mod0->magic_mod0 != 0x30444f4d) { // MOD0
return;
}
// Clear the BSS area
u8* bss_start = _dynResolveOffset(mod0, mod0->bss_start_offset);
u8* bss_end = _dynResolveOffset(mod0, mod0->bss_end_offset);
if (bss_start != bss_end) {
memset(bss_start, 0, bss_end - bss_start);
}
// Retrieve pointer to the ELF dynamic section
const Elf64_Dyn* dyn = _dynResolveOffset(mod0, mod0->dyn_offset);
// Extract relevant information from the ELF dynamic section
const Elf64_Rela* rela = NULL;
size_t relasz = 0;
for (; dyn->d_tag != DT_NULL; dyn++) {
switch (dyn->d_tag) {
case DT_RELA:
rela = (const Elf64_Rela*)(base + dyn->d_un.d_ptr);
break;
case DT_RELASZ:
relasz = dyn->d_un.d_val / sizeof(Elf64_Rela);
break;
}
}
// Apply RELA relocations if present
if (rela && relasz) {
_dynProcessRela(base, rela, relasz);
}
// Return early if LNY0/LNY1 extensions are not present
if (mod0->magic_lny0 != 0x30594e4c || mod0->magic_lny1 != 0x31594e4c) { // LNY0, LNY1
return;
}
// Reprotect relro segment as read-only now that we're done processing relocations
u8* relro_start = _dynResolveOffset(mod0, mod0->relro_start_offset);
size_t relro_sz = (u8*)_dynResolveOffset(mod0, mod0->relro_end_offset) - relro_start;
Result rc = svcSetMemoryPermission(relro_start, relro_sz, Perm_R);
if (R_FAILED(rc)) {
diagAbortWithResult(rc);
}
// Lock the relro segment's permissions
svcSetMemoryAttribute(relro_start, relro_sz, MemAttr_IsPermissionLocked, MemAttr_IsPermissionLocked);
}

View File

@ -23,36 +23,20 @@ _start:
b __libnx_exception_entry
.Lcrt0_main_entry:
// Get pointer to MOD0 struct (contains offsets to important places)
adr x28, __nx_mod0
// Calculate BSS address/size
ldp w8, w9, [x28, #8] // load BSS start/end offset from MOD0
sub w9, w9, w8 // calculate BSS size
add w9, w9, #7 // round up to 8
bic w9, w9, #7 // ^
add x8, x28, x8 // fixup the start pointer
// Clear the BSS in 8-byte units
1: subs w9, w9, #8
str xzr, [x8], #8
bne 1b
// Preserve registers across function calls
mov x25, x0 // entrypoint argument 0
mov x26, x1 // entrypoint argument 1
mov x27, x30 // loader return address
mov x28, sp // initial stack pointer
// Perform runtime linking on ourselves (including relocations)
adr x0, _start // get aslr base
adr x1, __nx_mod0 // get pointer to MOD0 struct
bl __nx_dynamic
// Save initial stack pointer
mov x8, sp
adrp x9, __stack_top
str x8, [x9, #:lo12:__stack_top]
// Parse ELF .dynamic section (which applies relocations to our module)
adr x0, _start // get aslr base
ldr w1, [x28, #4] // pointer to .dynamic section
add x1, x28, x1
bl __nx_dynamic
str x28, [x9, #:lo12:__stack_top]
// Perform system initialization
mov x0, x25
@ -65,8 +49,8 @@ _start:
ldr w0, [x0, #:lo12:__system_argc]
adrp x1, __system_argv // argv
ldr x1, [x1, #:lo12:__system_argv]
adrp x30, exit
add x30, x30, #:lo12:exit
adrp x30, :got:exit
ldr x30, [x30, #:got_lo12:exit]
b main
.global __nx_exit
@ -95,6 +79,10 @@ __nx_mod0:
.word __got_start__ - __nx_mod0
.word __got_end__ - __nx_mod0
.ascii "LNY1"
.word __relro_start - __nx_mod0
.word __data_start - __nx_mod0
.section .bss.__stack_top, "aw", %nobits
.global __stack_top
.align 3