From 4ff1c528697197144362b7cbaf65f569737580fb Mon Sep 17 00:00:00 2001 From: fincs Date: Thu, 19 Oct 2023 21:25:23 +0200 Subject: [PATCH] crt0/dynamic: refactoring + add support for reprotecting relro segment --- nx/source/runtime/dynamic.c | 108 +++++++++++++++++++++++++------- nx/source/runtime/switch_crt0.s | 38 ++++------- 2 files changed, 98 insertions(+), 48 deletions(-) diff --git a/nx/source/runtime/dynamic.c b/nx/source/runtime/dynamic.c index cc79582f..c9e2587e 100644 --- a/nx/source/runtime/dynamic.c +++ b/nx/source/runtime/dynamic.c @@ -1,34 +1,42 @@ #include "result.h" +#include "kernel/svc.h" #include "runtime/diag.h" #include +#include -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); +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; - case DT_RELASZ: - relasz = dyn->d_un.d_val / sizeof(Elf64_Rela); - break; - } - } + } - if (rela == NULL) - diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_BadReloc)); - - 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); +} diff --git a/nx/source/runtime/switch_crt0.s b/nx/source/runtime/switch_crt0.s index de63d136..85a2ea30 100644 --- a/nx/source/runtime/switch_crt0.s +++ b/nx/source/runtime/switch_crt0.s @@ -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