nintendoswitch: Add dynamic loader for runtime loading PIE sections

Этот коммит содержится в:
Lucas Teske 2020-09-13 21:10:27 -03:00 коммит произвёл Ron Evans
родитель 490e377bba
коммит fb1fc267ab
6 изменённых файлов: 148 добавлений и 35 удалений

Просмотреть файл

@ -9,6 +9,10 @@ import (
"github.com/marcinbor85/gohex"
)
// maxPadBytes is the maximum allowed bytes to be padded in a rom extraction
// this value is currently defined by Nintendo Switch Page Alignment (4096 bytes)
const maxPadBytes = 4095
// objcopyError is an error returned by functions that act like objcopy.
type objcopyError struct {
Op string
@ -70,7 +74,12 @@ func extractROM(path string) (uint64, []byte, error) {
var rom []byte
for _, prog := range progs {
if prog.Paddr != progs[0].Paddr+uint64(len(rom)) {
return 0, nil, objcopyError{"ROM segments are non-contiguous: " + path, nil}
diff := prog.Paddr - (progs[0].Paddr + uint64(len(rom)))
if diff > maxPadBytes {
return 0, nil, objcopyError{"ROM segments are non-contiguous: " + path, nil}
}
// Pad the difference
rom = append(rom, make([]byte, diff)...)
}
data, err := ioutil.ReadAll(prog.Open())
if err != nil {

55
src/runtime/dynamic_arm64.go Обычный файл
Просмотреть файл

@ -0,0 +1,55 @@
package runtime
import (
"debug/elf"
"unsafe"
)
const debugLoader = false
//export __dynamic_loader
func dynamicLoader(base uintptr, dyn *elf.Dyn64) {
var rela *elf.Rela64
relasz := uint64(0)
if debugLoader {
println("ASLR Base: ", base)
}
for dyn.Tag != int64(elf.DT_NULL) {
switch elf.DynTag(dyn.Tag) {
case elf.DT_RELA:
rela = (*elf.Rela64)(unsafe.Pointer(base + uintptr(dyn.Val)))
case elf.DT_RELASZ:
relasz = uint64(dyn.Val) / uint64(unsafe.Sizeof(elf.Rela64{}))
}
ptr := uintptr(unsafe.Pointer(dyn))
ptr += unsafe.Sizeof(elf.Dyn64{})
dyn = (*elf.Dyn64)(unsafe.Pointer(ptr))
}
if rela == nil {
runtimePanic("bad reloc")
}
if rela == nil {
runtimePanic("bad reloc")
}
if debugLoader {
println("Sections to load: ", relasz)
}
for relasz > 0 && rela != nil {
switch elf.R_AARCH64(rela.Info) {
case elf.R_AARCH64_RELATIVE:
ptr := (*uint64)(unsafe.Pointer(base + uintptr(rela.Off)))
*ptr = uint64(base + uintptr(rela.Addend))
}
rptr := uintptr(unsafe.Pointer(rela))
rptr += unsafe.Sizeof(elf.Rela64{})
rela = (*elf.Rela64)(unsafe.Pointer(rptr))
relasz--
}
}

Просмотреть файл

@ -2,6 +2,8 @@
package runtime
import "unsafe"
type timeUnit int64
const asyncScheduler = false
@ -60,6 +62,16 @@ func abort() {
}
}
//export write
func write(fd int32, buf *byte, count int) int {
// TODO: Proper handling write
for i := 0; i < count; i++ {
putchar(*buf)
buf = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + 1))
}
return count
}
//export sleepThread
func sleepThread(nanos uint64)

Просмотреть файл

@ -20,6 +20,10 @@
"-fno-exceptions", "-fno-unwind-tables",
"-ffunction-sections", "-fdata-sections"
],
"ldflags": [
"-pie",
"-z", "notext"
],
"linkerscript": "targets/nintendoswitch.ld",
"extra-files": [
"targets/nintendoswitch.s",

Просмотреть файл

@ -1,69 +1,84 @@
PHDRS
{
text PT_LOAD FLAGS(5) /* Read | Execute */;
rodata PT_LOAD FLAGS(4) /* Read */;
data PT_LOAD FLAGS(6) /* Read | Write */;
bss PT_LOAD FLAGS(6) /* Read | Write */;
dynamic PT_DYNAMIC;
}
SECTIONS
{
/* Code and file header */
. = 0;
/* Code and file header */
.text : {
.text : ALIGN(0x1000) {
HIDDEN(__text_start = .);
KEEP(*(.text.jmp))
. = 0x80;
*(.text .text.*)
*(.plt .plt.*)
. = ALIGN(0x1000);
HIDDEN(__text_end = .);
HIDDEN(__text_size = . - __text_start);
}
/* Read-only sections */
. = ALIGN(0x1000);
.rodata : {
HIDDEN(__rodata_start = .);
*(.rodata .rodata.*)
*(.got)
HIDDEN(__rodata_start = .);
.rodata : { *(.rodata .rodata.*) } :rodata
.mod0 : {
KEEP(crt0.nso.o(.data.mod0))
KEEP(crt0.nro.o(.data.mod0))
KEEP(crt0.lib.nro.o(.data.mod0))
KEEP(*(.data.mod0))
HIDDEN(__dynamic_start = .);
*(.dynamic)
. = ALIGN(0x1000);
HIDDEN(__rodata_end = .);
HIDDEN(__rodata_size = . - __rodata_start);
}
.dynsym : { *(.dynsym) } :rodata
.dynstr : { *(.dynstr) } :rodata
.rela.dyn : { *(.rela.*) } :rodata
HIDDEN(__rodata_end = .);
HIDDEN(__rodata_size = . - __rodata_start);
/* Read-write sections */
. = ALIGN(0x1000);
.data : {
HIDDEN(__data_start = .);
*(.data .data.*)
*(.got .got.*)
*(.got.plt .got.plt.*)
HIDDEN(__data_end = .);
HIDDEN(__data_size = . - __data_start);
} :data
.dynamic : {
HIDDEN(__dynamic_start = .);
*(.dynamic)
}
/* BSS section */
. = ALIGN(0x1000);
.bss : {
HIDDEN(__bss_start = .);
*(.bss .bss.*)
*(COMMON)
. = ALIGN(8);
HIDDEN(__bss_end = .);
HIDDEN(__bss_size = . - __bss_start);
}
} : bss
/DISCARD/ :
{
*(.eh_frame) /* This is probably unnecessary and bloats the binary. */
*(.eh_frame_hdr)
}
}

Просмотреть файл

@ -6,8 +6,7 @@
_start:
b start
.word _mod_header - _start
.word 0
.word 0
.ascii "HOMEBREW"
.ascii "NRO0" // magic
.word 0 // version (always 0)
@ -15,11 +14,11 @@ _start:
.word 0 // flags (unused)
// segment headers
.word __text_start
.word 0 // __text_start
.word __text_size
.word __rodata_start
.word 0 //__rodata_start
.word __rodata_size
.word __data_start
.word 0 //__data_start
.word __data_size
.word __bss_size
.word 0
@ -45,18 +44,17 @@ _mod_header:
.section .text, "x"
.global start
start:
// save lr
mov x7, x30
// get aslr base
bl +4
sub x6, x30, #0x88
// Get ASLR Base
adrp x6, _start
// context ptr and main thread handle
mov x5, x0
mov x4, x1
// Save lr, context pointer, main thread handler
adrp x0, _aslr_base
str x6, [x0, #:lo12:_aslr_base]
// clear .bss
adrp x5, __bss_start
add x5, x5, #:lo12:__bss_start
@ -71,7 +69,27 @@ bssloop:
b bssloop
run:
// process .dynamic section
adrp x0, _aslr_base
ldr x0, [x0, #:lo12:_aslr_base]
adrp x1, _DYNAMIC
add x1, x1, #:lo12:_DYNAMIC
bl __dynamic_loader
// set LR to svcExitProcess if it's null
adrp x3, exit
add x3, x3, #:lo12:exit
cmp x30, xzr
csel x30, x3, x30, eq
// call entrypoint
adrp x30, exit
add x30, x30, #:lo12:exit
mov x3, sp
sub sp, sp, 0x10
stp x29, x30, [sp]
b main
.section .data.horizon
.align 8
.global _aslr_base // Placeholder for ASLR Base Address
_aslr_base:
.dword 0