nintendoswitch: Add env parser and removed unused stuff
* Heap allocation based on available ram * Added homebrew launcher parser (for overriden heap) * Removed unused stuff (moved to gonx) * Kept require code at minimum to work in a real device * Moved everything to a single file
Этот коммит содержится в:
родитель
d424b3d7ea
коммит
387bca8e32
9 изменённых файлов: 300 добавлений и 122 удалений
|
@ -59,8 +59,15 @@ func dynamicLoader(base uintptr, dyn *dyn64) {
|
||||||
for relasz > 0 && rela != nil {
|
for relasz > 0 && rela != nil {
|
||||||
switch rela.Info {
|
switch rela.Info {
|
||||||
case rAARCH64_RELATIVE:
|
case rAARCH64_RELATIVE:
|
||||||
|
if debugLoader {
|
||||||
|
println("relocating ", uintptr(rela.Addend), " to ", base+uintptr(rela.Addend))
|
||||||
|
}
|
||||||
ptr := (*uint64)(unsafe.Pointer(base + uintptr(rela.Off)))
|
ptr := (*uint64)(unsafe.Pointer(base + uintptr(rela.Off)))
|
||||||
*ptr = uint64(base + uintptr(rela.Addend))
|
*ptr = uint64(base + uintptr(rela.Addend))
|
||||||
|
default:
|
||||||
|
if debugLoader {
|
||||||
|
println("unknown section to load:", rela.Info)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rptr := uintptr(unsafe.Pointer(rela))
|
rptr := uintptr(unsafe.Pointer(rela))
|
||||||
|
|
|
@ -8,35 +8,67 @@ type timeUnit int64
|
||||||
|
|
||||||
const asyncScheduler = false
|
const asyncScheduler = false
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Handles
|
||||||
|
infoTypeTotalMemorySize = 6 // Total amount of memory available for process.
|
||||||
|
infoTypeUsedMemorySize = 7 // Amount of memory currently used by process.
|
||||||
|
currentProcessHandle = 0xFFFF8001 // Pseudo handle for the current process.
|
||||||
|
|
||||||
|
// Types of config Entry
|
||||||
|
envEntryTypeEndOfList = 0 // Entry list terminator.
|
||||||
|
envEntryTypeMainThreadHandle = 1 // Provides the handle to the main thread.
|
||||||
|
envEntryTypeOverrideHeap = 3 // Provides heap override information.
|
||||||
|
|
||||||
|
// Default heap size allocated by libnx
|
||||||
|
defaultHeapSize = 0x2000000 * 16
|
||||||
|
|
||||||
|
debugInit = false
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:extern _saved_return_address
|
||||||
|
var savedReturnAddress uintptr
|
||||||
|
|
||||||
|
//export __stack_top
|
||||||
var stackTop uintptr
|
var stackTop uintptr
|
||||||
|
|
||||||
|
//go:extern _context
|
||||||
|
var context uintptr
|
||||||
|
|
||||||
|
//go:extern _main_thread
|
||||||
|
var mainThread uintptr
|
||||||
|
|
||||||
|
var (
|
||||||
|
heapStart = uintptr(0)
|
||||||
|
heapEnd = uintptr(0)
|
||||||
|
usedRam = uint64(0)
|
||||||
|
totalRam = uint64(0)
|
||||||
|
totalHeap = uint64(0)
|
||||||
|
)
|
||||||
|
|
||||||
func postinit() {}
|
func postinit() {}
|
||||||
|
|
||||||
|
func preinit() {
|
||||||
|
// Unsafe to use heap here
|
||||||
|
setupEnv()
|
||||||
|
setupHeap()
|
||||||
|
}
|
||||||
|
|
||||||
// Entry point for Go. Initialize all packages and call main.main().
|
// Entry point for Go. Initialize all packages and call main.main().
|
||||||
//export main
|
//export main
|
||||||
func main() int {
|
func main() {
|
||||||
preinit()
|
preinit()
|
||||||
|
run()
|
||||||
// Obtain the initial stack pointer right before calling the run() function.
|
|
||||||
// The run function has been moved to a separate (non-inlined) function so
|
|
||||||
// that the correct stack pointer is read.
|
|
||||||
stackTop = getCurrentStackPointer()
|
|
||||||
runMain()
|
|
||||||
|
|
||||||
// Call exit to correctly finish the program
|
// Call exit to correctly finish the program
|
||||||
// Without this, the application crashes at start, not sure why
|
// Without this, the application crashes at start, not sure why
|
||||||
return exit(0)
|
for {
|
||||||
}
|
exit(0)
|
||||||
|
}
|
||||||
// Must be a separate function to get the correct stack pointer.
|
|
||||||
//go:noinline
|
|
||||||
func runMain() {
|
|
||||||
run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleepTicks sleeps for the specified system ticks
|
// sleepTicks sleeps for the specified system ticks
|
||||||
func sleepTicks(d timeUnit) {
|
func sleepTicks(d timeUnit) {
|
||||||
sleepThread(uint64(ticksToNanoseconds(d)))
|
svcSleepThread(uint64(ticksToNanoseconds(d)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// armTicksToNs converts cpu ticks to nanoseconds
|
// armTicksToNs converts cpu ticks to nanoseconds
|
||||||
|
@ -60,7 +92,7 @@ var position = 0
|
||||||
|
|
||||||
func putchar(c byte) {
|
func putchar(c byte) {
|
||||||
if c == '\n' || position >= len(stdoutBuffer) {
|
if c == '\n' || position >= len(stdoutBuffer) {
|
||||||
nxOutputString(&stdoutBuffer[0], uint64(position))
|
svcOutputDebugString(&stdoutBuffer[0], uint64(position))
|
||||||
position = 0
|
position = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -85,11 +117,159 @@ func write(fd int32, buf *byte, count int) int {
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
//export sleepThread
|
// exit checks if a savedReturnAddress were provided by the launcher
|
||||||
func sleepThread(nanos uint64)
|
// if so, calls the nxExit which restores the stack and returns to launcher
|
||||||
|
// otherwise just calls systemcall exit
|
||||||
|
func exit(code int) {
|
||||||
|
if savedReturnAddress == 0 {
|
||||||
|
svcExitProcess(code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//export exit
|
nxExit(code, stackTop, savedReturnAddress)
|
||||||
func exit(code int) int
|
}
|
||||||
|
|
||||||
|
type configEntry struct {
|
||||||
|
Key uint32
|
||||||
|
Flags uint32
|
||||||
|
Value [2]uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupEnv() {
|
||||||
|
if debugInit {
|
||||||
|
println("Saved Return Address:", savedReturnAddress)
|
||||||
|
println("Context:", context)
|
||||||
|
println("Main Thread Handle:", mainThread)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://switchbrew.org/w/index.php?title=Homebrew_ABI
|
||||||
|
// Here we parse only the required configs for initializing
|
||||||
|
if context != 0 {
|
||||||
|
ptr := context
|
||||||
|
entry := (*configEntry)(unsafe.Pointer(ptr))
|
||||||
|
for entry.Key != envEntryTypeEndOfList {
|
||||||
|
switch entry.Key {
|
||||||
|
case envEntryTypeOverrideHeap:
|
||||||
|
if debugInit {
|
||||||
|
println("Got heap override")
|
||||||
|
}
|
||||||
|
heapStart = uintptr(entry.Value[0])
|
||||||
|
heapEnd = heapStart + uintptr(entry.Value[1])
|
||||||
|
case envEntryTypeMainThreadHandle:
|
||||||
|
mainThread = uintptr(entry.Value[0])
|
||||||
|
default:
|
||||||
|
if entry.Flags&1 > 0 {
|
||||||
|
// Mandatory but not parsed
|
||||||
|
runtimePanic("mandatory config entry not parsed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr += unsafe.Sizeof(configEntry{})
|
||||||
|
entry = (*configEntry)(unsafe.Pointer(ptr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fetch used / total RAM for allocating HEAP
|
||||||
|
svcGetInfo(&totalRam, infoTypeTotalMemorySize, currentProcessHandle, 0)
|
||||||
|
svcGetInfo(&usedRam, infoTypeUsedMemorySize, currentProcessHandle, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupHeap() {
|
||||||
|
if heapStart != 0 {
|
||||||
|
if debugInit {
|
||||||
|
print("Heap already overrided by hblauncher")
|
||||||
|
}
|
||||||
|
// Already overrided
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if debugInit {
|
||||||
|
print("No heap override. Using normal initialization")
|
||||||
|
}
|
||||||
|
|
||||||
|
size := uint32(defaultHeapSize)
|
||||||
|
|
||||||
|
if totalRam > usedRam+0x200000 {
|
||||||
|
// Get maximum possible heap
|
||||||
|
size = uint32(totalRam-usedRam-0x200000) & ^uint32(0x1FFFFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
if size < defaultHeapSize {
|
||||||
|
size = defaultHeapSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if debugInit {
|
||||||
|
println("Trying to allocate", size, "bytes of heap")
|
||||||
|
}
|
||||||
|
|
||||||
|
svcSetHeapSize(&heapStart, uint64(size))
|
||||||
|
|
||||||
|
if heapStart == 0 {
|
||||||
|
runtimePanic("failed to allocate heap")
|
||||||
|
}
|
||||||
|
|
||||||
|
totalHeap = uint64(size)
|
||||||
|
|
||||||
|
heapEnd = heapStart + uintptr(size)
|
||||||
|
|
||||||
|
if debugInit {
|
||||||
|
println("Heap Start", heapStart)
|
||||||
|
println("Heap End ", heapEnd)
|
||||||
|
println("Total Heap", totalHeap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHeapBase returns the start address of the heap
|
||||||
|
// this is externally linked by gonx
|
||||||
|
func getHeapBase() uintptr {
|
||||||
|
return heapStart
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHeapEnd returns the end address of the heap
|
||||||
|
// this is externally linked by gonx
|
||||||
|
func getHeapEnd() uintptr {
|
||||||
|
return heapEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// getContextPtr returns the hblauncher context
|
||||||
|
// this is externally linked by gonx
|
||||||
|
func getContextPtr() uintptr {
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMainThreadHandle returns the main thread handler if any
|
||||||
|
// this is externally linked by gonx
|
||||||
|
func getMainThreadHandle() uintptr {
|
||||||
|
return mainThread
|
||||||
|
}
|
||||||
|
|
||||||
//export armGetSystemTick
|
//export armGetSystemTick
|
||||||
func getArmSystemTick() int64
|
func getArmSystemTick() int64
|
||||||
|
|
||||||
|
// nxExit exits the program to homebrew launcher
|
||||||
|
//export __nx_exit
|
||||||
|
func nxExit(code int, stackTop uintptr, exitFunction uintptr)
|
||||||
|
|
||||||
|
// Horizon System Calls
|
||||||
|
// svcSetHeapSize Set the process heap to a given size. It can both extend and shrink the heap.
|
||||||
|
// svc 0x01
|
||||||
|
//export svcSetHeapSize
|
||||||
|
func svcSetHeapSize(addr *uintptr, length uint64) uint64
|
||||||
|
|
||||||
|
// svcExitProcess Exits the current process.
|
||||||
|
// svc 0x07
|
||||||
|
//export svcExitProcess
|
||||||
|
func svcExitProcess(code int)
|
||||||
|
|
||||||
|
// svcSleepThread Sleeps the current thread for the specified amount of time.
|
||||||
|
// svc 0x0B
|
||||||
|
//export svcSleepThread
|
||||||
|
func svcSleepThread(nanos uint64)
|
||||||
|
|
||||||
|
// svcOutputDebugString Outputs debug text, if used during debugging.
|
||||||
|
// svc 0x27
|
||||||
|
//export svcOutputDebugString
|
||||||
|
func svcOutputDebugString(str *uint8, size uint64) uint64
|
||||||
|
|
||||||
|
// svcGetInfo Retrieves information about the system, or a certain kernel object.
|
||||||
|
// svc 0x29
|
||||||
|
//export svcGetInfo
|
||||||
|
func svcGetInfo(output *uint64, id0 uint32, handle uint32, id1 uint64) uint64
|
||||||
|
|
|
@ -1,45 +1,40 @@
|
||||||
.section .text.armGetSystemTick, "ax", %progbits
|
// Macro for writing less code
|
||||||
.global armGetSystemTick
|
.macro FUNC name
|
||||||
.type armGetSystemTick, %function
|
.section .text.\name, "ax", %progbits
|
||||||
.align 2
|
.global \name
|
||||||
armGetSystemTick:
|
.type \name, %function
|
||||||
|
.align 2
|
||||||
|
\name:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
FUNC armGetSystemTick
|
||||||
mrs x0, cntpct_el0
|
mrs x0, cntpct_el0
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.section .text.nxOutputString, "ax", %progbits
|
// Horizon System Calls
|
||||||
.global nxOutputString
|
// https://switchbrew.org/wiki/SVC
|
||||||
.type nxOutputString, %function
|
FUNC svcSetHeapSize
|
||||||
.align 2
|
str x0, [sp, #-16]!
|
||||||
.cfi_startproc
|
svc 0x1
|
||||||
nxOutputString:
|
ldr x2, [sp], #16
|
||||||
svc 0x27
|
str x1, [x2]
|
||||||
ret
|
ret
|
||||||
.cfi_endproc
|
|
||||||
|
|
||||||
.section .text.exit, "ax", %progbits
|
FUNC svcExitProcess
|
||||||
.global exit
|
svc 0x7
|
||||||
.type exit, %function
|
ret
|
||||||
.align 2
|
|
||||||
exit:
|
|
||||||
svc 0x7
|
|
||||||
ret
|
|
||||||
|
|
||||||
.section .text.setHeapSize, "ax", %progbits
|
FUNC svcSleepThread
|
||||||
.global setHeapSize
|
svc 0xB
|
||||||
.type setHeapSize, %function
|
ret
|
||||||
.align 2
|
|
||||||
setHeapSize:
|
|
||||||
str x0, [sp, #-16]!
|
|
||||||
svc 0x1
|
|
||||||
ldr x2, [sp], #16
|
|
||||||
str x1, [x2]
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
FUNC svcOutputDebugString
|
||||||
|
svc 0x27
|
||||||
|
ret
|
||||||
|
|
||||||
.section .text.sleepThread, "ax", %progbits
|
FUNC svcGetInfo
|
||||||
.global sleepThread
|
str x0, [sp, #-16]!
|
||||||
.type sleepThread, %function
|
svc 0x29
|
||||||
.align 2
|
ldr x2, [sp], #16
|
||||||
sleepThread:
|
str x1, [x2]
|
||||||
svc 0xB
|
ret
|
||||||
ret
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
// +build nintendoswitch
|
|
||||||
|
|
||||||
// +build gc.conservative gc.leaking
|
|
||||||
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
const heapSize = 0x2000000 * 16 // Default by libnx
|
|
||||||
|
|
||||||
var (
|
|
||||||
heapStart = uintptr(0)
|
|
||||||
heapEnd = uintptr(0)
|
|
||||||
)
|
|
||||||
|
|
||||||
//export setHeapSize
|
|
||||||
func setHeapSize(addr *uintptr, length uint64) uint64
|
|
||||||
|
|
||||||
func preinit() {
|
|
||||||
setHeapSize(&heapStart, heapSize)
|
|
||||||
|
|
||||||
if heapStart == 0 {
|
|
||||||
runtimePanic("failed to allocate heap")
|
|
||||||
}
|
|
||||||
|
|
||||||
heapEnd = heapStart + heapSize
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// +build nintendoswitch
|
|
||||||
|
|
||||||
// +build gc.none gc.extalloc
|
|
||||||
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
func preinit() {}
|
|
|
@ -1,22 +0,0 @@
|
||||||
// +build nintendoswitch
|
|
||||||
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Result nxOutputString(const char *str, u64 size)
|
|
||||||
//export nxOutputString
|
|
||||||
func nxOutputString(str *uint8, size uint64) uint64
|
|
||||||
|
|
||||||
func NxOutputString(str string) uint64 {
|
|
||||||
strData := (*_string)(unsafe.Pointer(&str))
|
|
||||||
return nxOutputString((*uint8)(unsafe.Pointer(strData.ptr)), uint64(strData.length))
|
|
||||||
}
|
|
||||||
|
|
||||||
//export malloc
|
|
||||||
func extalloc(size uintptr) unsafe.Pointer
|
|
||||||
|
|
||||||
//export free
|
|
||||||
func extfree(ptr unsafe.Pointer)
|
|
|
@ -9,9 +9,10 @@
|
||||||
"libc": "picolibc",
|
"libc": "picolibc",
|
||||||
"gc": "conservative",
|
"gc": "conservative",
|
||||||
"relocation-model": "pic",
|
"relocation-model": "pic",
|
||||||
|
"cpu": "cortex-a57",
|
||||||
"cflags": [
|
"cflags": [
|
||||||
"-target", "aarch64-none-linux-gnu",
|
"-target", "aarch64-unknown-none",
|
||||||
"-mtune=cortex-a57",
|
"-mcpu=cortex-a57",
|
||||||
"-fPIE",
|
"-fPIE",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-Qunused-arguments",
|
"-Qunused-arguments",
|
||||||
|
|
|
@ -17,6 +17,7 @@ SECTIONS
|
||||||
KEEP(*(.text.jmp))
|
KEEP(*(.text.jmp))
|
||||||
|
|
||||||
. = 0x80;
|
. = 0x80;
|
||||||
|
KEEP(*(.text.start))
|
||||||
|
|
||||||
*(.text .text.*)
|
*(.text .text.*)
|
||||||
*(.plt .plt.*)
|
*(.plt .plt.*)
|
||||||
|
|
|
@ -14,11 +14,11 @@ _start:
|
||||||
.word 0 // flags (unused)
|
.word 0 // flags (unused)
|
||||||
|
|
||||||
// segment headers
|
// segment headers
|
||||||
.word 0 // __text_start
|
.word __text_start - _start
|
||||||
.word __text_size
|
.word __text_size
|
||||||
.word 0 //__rodata_start
|
.word __rodata_start - _start
|
||||||
.word __rodata_size
|
.word __rodata_size
|
||||||
.word 0 //__data_start
|
.word __data_start - _start
|
||||||
.word __data_size
|
.word __data_size
|
||||||
.word __bss_size
|
.word __bss_size
|
||||||
.word 0
|
.word 0
|
||||||
|
@ -41,19 +41,37 @@ _mod_header:
|
||||||
.word 0, 0 // eh_frame_hdr start/end
|
.word 0, 0 // eh_frame_hdr start/end
|
||||||
.word 0 // runtime-generated module object offset
|
.word 0 // runtime-generated module object offset
|
||||||
|
|
||||||
.section .text, "x"
|
.section .text.start, "x"
|
||||||
.global start
|
.global start
|
||||||
start:
|
start:
|
||||||
// Get ASLR Base
|
// save lr
|
||||||
adrp x6, _start
|
mov x7, x30
|
||||||
|
|
||||||
|
// get aslr base
|
||||||
|
bl +4
|
||||||
|
sub x6, x30, #0x88
|
||||||
|
|
||||||
// context ptr and main thread handle
|
// context ptr and main thread handle
|
||||||
mov x5, x0
|
mov x25, x0
|
||||||
mov x4, x1
|
mov x26, x1
|
||||||
|
|
||||||
// Save ASLR Base to use later
|
// Save ASLR Base to use later
|
||||||
mov x0, x6
|
mov x0, x6
|
||||||
|
|
||||||
|
adrp x4, _saved_return_address
|
||||||
|
str x7, [x4, #:lo12:_saved_return_address]
|
||||||
|
|
||||||
|
adrp x4, _context
|
||||||
|
str x25, [x4, #:lo12:_context]
|
||||||
|
|
||||||
|
adrp x4, _main_thread
|
||||||
|
str x26, [x4, #:lo12:_main_thread]
|
||||||
|
|
||||||
|
// store stack pointer
|
||||||
|
mov x26, sp
|
||||||
|
adrp x4, _stack_top
|
||||||
|
str x26, [x4, #:lo12:_stack_top]
|
||||||
|
|
||||||
// clear .bss
|
// clear .bss
|
||||||
adrp x5, __bss_start
|
adrp x5, __bss_start
|
||||||
add x5, x5, #:lo12:__bss_start
|
add x5, x5, #:lo12:__bss_start
|
||||||
|
@ -76,3 +94,33 @@ run:
|
||||||
|
|
||||||
// call entrypoint
|
// call entrypoint
|
||||||
b main
|
b main
|
||||||
|
|
||||||
|
.global __nx_exit
|
||||||
|
.type __nx_exit, %function
|
||||||
|
__nx_exit:
|
||||||
|
// Exit code in x0
|
||||||
|
|
||||||
|
// restore stack pointer
|
||||||
|
mov sp, x1
|
||||||
|
|
||||||
|
// jump back to loader
|
||||||
|
br x2
|
||||||
|
|
||||||
|
.section .data.horizon
|
||||||
|
.align 8
|
||||||
|
.global _saved_return_address // Saved return address.
|
||||||
|
// This might be different than null when coming from launcher
|
||||||
|
_saved_return_address:
|
||||||
|
.dword 0
|
||||||
|
.global _context // Homebrew Launcher Context
|
||||||
|
// This might be different than null when not coming from launcher
|
||||||
|
_context:
|
||||||
|
.dword 0
|
||||||
|
|
||||||
|
.global _main_thread
|
||||||
|
_main_thread:
|
||||||
|
.dword 0
|
||||||
|
|
||||||
|
.global _stack_top
|
||||||
|
_stack_top:
|
||||||
|
.dword 0
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче