mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 12:32:40 +02:00
crt0/dynamic: refactoring + add support for reprotecting relro segment
This commit is contained in:
parent
3a5d0dae35
commit
4ff1c52869
@ -1,34 +1,42 @@
|
|||||||
#include "result.h"
|
#include "result.h"
|
||||||
|
#include "kernel/svc.h"
|
||||||
#include "runtime/diag.h"
|
#include "runtime/diag.h"
|
||||||
#include <elf.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;
|
return (void*)((uintptr_t)mod0 + offset);
|
||||||
u64 relasz = 0;
|
}
|
||||||
|
|
||||||
for (; dyn->d_tag != DT_NULL; dyn++)
|
static void _dynProcessRela(uintptr_t base, const Elf64_Rela* rela, size_t relasz)
|
||||||
{
|
{
|
||||||
switch (dyn->d_tag)
|
for (; relasz--; rela++) {
|
||||||
{
|
switch (ELF64_R_TYPE(rela->r_info)) {
|
||||||
case DT_RELA:
|
default: {
|
||||||
rela = (const Elf64_Rela*)(base + dyn->d_un.d_ptr);
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_BadReloc));
|
||||||
break;
|
break;
|
||||||
case DT_RELASZ:
|
}
|
||||||
relasz = dyn->d_un.d_val / sizeof(Elf64_Rela);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rela == NULL)
|
case R_AARCH64_RELATIVE: {
|
||||||
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_BadReloc));
|
|
||||||
|
|
||||||
for (; relasz--; rela++)
|
|
||||||
{
|
|
||||||
switch (ELF64_R_TYPE(rela->r_info))
|
|
||||||
{
|
|
||||||
case R_AARCH64_RELATIVE:
|
|
||||||
{
|
|
||||||
u64* ptr = (u64*)(base + rela->r_offset);
|
u64* ptr = (u64*)(base + rela->r_offset);
|
||||||
*ptr = base + rela->r_addend;
|
*ptr = base + rela->r_addend;
|
||||||
break;
|
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);
|
||||||
|
}
|
||||||
|
@ -23,36 +23,20 @@ _start:
|
|||||||
b __libnx_exception_entry
|
b __libnx_exception_entry
|
||||||
|
|
||||||
.Lcrt0_main_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
|
// Preserve registers across function calls
|
||||||
mov x25, x0 // entrypoint argument 0
|
mov x25, x0 // entrypoint argument 0
|
||||||
mov x26, x1 // entrypoint argument 1
|
mov x26, x1 // entrypoint argument 1
|
||||||
mov x27, x30 // loader return address
|
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
|
// Save initial stack pointer
|
||||||
mov x8, sp
|
|
||||||
adrp x9, __stack_top
|
adrp x9, __stack_top
|
||||||
str x8, [x9, #:lo12:__stack_top]
|
str x28, [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
|
|
||||||
|
|
||||||
// Perform system initialization
|
// Perform system initialization
|
||||||
mov x0, x25
|
mov x0, x25
|
||||||
@ -65,8 +49,8 @@ _start:
|
|||||||
ldr w0, [x0, #:lo12:__system_argc]
|
ldr w0, [x0, #:lo12:__system_argc]
|
||||||
adrp x1, __system_argv // argv
|
adrp x1, __system_argv // argv
|
||||||
ldr x1, [x1, #:lo12:__system_argv]
|
ldr x1, [x1, #:lo12:__system_argv]
|
||||||
adrp x30, exit
|
adrp x30, :got:exit
|
||||||
add x30, x30, #:lo12:exit
|
ldr x30, [x30, #:got_lo12:exit]
|
||||||
b main
|
b main
|
||||||
|
|
||||||
.global __nx_exit
|
.global __nx_exit
|
||||||
@ -95,6 +79,10 @@ __nx_mod0:
|
|||||||
.word __got_start__ - __nx_mod0
|
.word __got_start__ - __nx_mod0
|
||||||
.word __got_end__ - __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
|
.section .bss.__stack_top, "aw", %nobits
|
||||||
.global __stack_top
|
.global __stack_top
|
||||||
.align 3
|
.align 3
|
||||||
|
Loading…
Reference in New Issue
Block a user