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 {
|
||||
switch rela.Info {
|
||||
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(base + uintptr(rela.Addend))
|
||||
default:
|
||||
if debugLoader {
|
||||
println("unknown section to load:", rela.Info)
|
||||
}
|
||||
}
|
||||
|
||||
rptr := uintptr(unsafe.Pointer(rela))
|
||||
|
|
|
@ -8,35 +8,67 @@ type timeUnit int64
|
|||
|
||||
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
|
||||
|
||||
//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 preinit() {
|
||||
// Unsafe to use heap here
|
||||
setupEnv()
|
||||
setupHeap()
|
||||
}
|
||||
|
||||
// Entry point for Go. Initialize all packages and call main.main().
|
||||
//export main
|
||||
func main() int {
|
||||
func main() {
|
||||
preinit()
|
||||
|
||||
// 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()
|
||||
run()
|
||||
|
||||
// Call exit to correctly finish the program
|
||||
// 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
|
||||
func sleepTicks(d timeUnit) {
|
||||
sleepThread(uint64(ticksToNanoseconds(d)))
|
||||
svcSleepThread(uint64(ticksToNanoseconds(d)))
|
||||
}
|
||||
|
||||
// armTicksToNs converts cpu ticks to nanoseconds
|
||||
|
@ -60,7 +92,7 @@ var position = 0
|
|||
|
||||
func putchar(c byte) {
|
||||
if c == '\n' || position >= len(stdoutBuffer) {
|
||||
nxOutputString(&stdoutBuffer[0], uint64(position))
|
||||
svcOutputDebugString(&stdoutBuffer[0], uint64(position))
|
||||
position = 0
|
||||
return
|
||||
}
|
||||
|
@ -85,11 +117,159 @@ func write(fd int32, buf *byte, count int) int {
|
|||
return count
|
||||
}
|
||||
|
||||
//export sleepThread
|
||||
func sleepThread(nanos uint64)
|
||||
// exit checks if a savedReturnAddress were provided by the launcher
|
||||
// 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
|
||||
func exit(code int) int
|
||||
nxExit(code, stackTop, savedReturnAddress)
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
.global armGetSystemTick
|
||||
.type armGetSystemTick, %function
|
||||
// Macro for writing less code
|
||||
.macro FUNC name
|
||||
.section .text.\name, "ax", %progbits
|
||||
.global \name
|
||||
.type \name, %function
|
||||
.align 2
|
||||
armGetSystemTick:
|
||||
\name:
|
||||
.endm
|
||||
|
||||
FUNC armGetSystemTick
|
||||
mrs x0, cntpct_el0
|
||||
ret
|
||||
|
||||
.section .text.nxOutputString, "ax", %progbits
|
||||
.global nxOutputString
|
||||
.type nxOutputString, %function
|
||||
.align 2
|
||||
.cfi_startproc
|
||||
nxOutputString:
|
||||
svc 0x27
|
||||
ret
|
||||
.cfi_endproc
|
||||
|
||||
.section .text.exit, "ax", %progbits
|
||||
.global exit
|
||||
.type exit, %function
|
||||
.align 2
|
||||
exit:
|
||||
svc 0x7
|
||||
ret
|
||||
|
||||
.section .text.setHeapSize, "ax", %progbits
|
||||
.global setHeapSize
|
||||
.type setHeapSize, %function
|
||||
.align 2
|
||||
setHeapSize:
|
||||
// Horizon System Calls
|
||||
// https://switchbrew.org/wiki/SVC
|
||||
FUNC svcSetHeapSize
|
||||
str x0, [sp, #-16]!
|
||||
svc 0x1
|
||||
ldr x2, [sp], #16
|
||||
str x1, [x2]
|
||||
ret
|
||||
|
||||
FUNC svcExitProcess
|
||||
svc 0x7
|
||||
ret
|
||||
|
||||
.section .text.sleepThread, "ax", %progbits
|
||||
.global sleepThread
|
||||
.type sleepThread, %function
|
||||
.align 2
|
||||
sleepThread:
|
||||
FUNC svcSleepThread
|
||||
svc 0xB
|
||||
ret
|
||||
|
||||
FUNC svcOutputDebugString
|
||||
svc 0x27
|
||||
ret
|
||||
|
||||
FUNC svcGetInfo
|
||||
str x0, [sp, #-16]!
|
||||
svc 0x29
|
||||
ldr x2, [sp], #16
|
||||
str x1, [x2]
|
||||
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",
|
||||
"gc": "conservative",
|
||||
"relocation-model": "pic",
|
||||
"cpu": "cortex-a57",
|
||||
"cflags": [
|
||||
"-target", "aarch64-none-linux-gnu",
|
||||
"-mtune=cortex-a57",
|
||||
"-target", "aarch64-unknown-none",
|
||||
"-mcpu=cortex-a57",
|
||||
"-fPIE",
|
||||
"-Werror",
|
||||
"-Qunused-arguments",
|
||||
|
|
|
@ -17,6 +17,7 @@ SECTIONS
|
|||
KEEP(*(.text.jmp))
|
||||
|
||||
. = 0x80;
|
||||
KEEP(*(.text.start))
|
||||
|
||||
*(.text .text.*)
|
||||
*(.plt .plt.*)
|
||||
|
|
|
@ -14,11 +14,11 @@ _start:
|
|||
.word 0 // flags (unused)
|
||||
|
||||
// segment headers
|
||||
.word 0 // __text_start
|
||||
.word __text_start - _start
|
||||
.word __text_size
|
||||
.word 0 //__rodata_start
|
||||
.word __rodata_start - _start
|
||||
.word __rodata_size
|
||||
.word 0 //__data_start
|
||||
.word __data_start - _start
|
||||
.word __data_size
|
||||
.word __bss_size
|
||||
.word 0
|
||||
|
@ -41,19 +41,37 @@ _mod_header:
|
|||
.word 0, 0 // eh_frame_hdr start/end
|
||||
.word 0 // runtime-generated module object offset
|
||||
|
||||
.section .text, "x"
|
||||
.section .text.start, "x"
|
||||
.global start
|
||||
start:
|
||||
// Get ASLR Base
|
||||
adrp x6, _start
|
||||
// save lr
|
||||
mov x7, x30
|
||||
|
||||
// get aslr base
|
||||
bl +4
|
||||
sub x6, x30, #0x88
|
||||
|
||||
// context ptr and main thread handle
|
||||
mov x5, x0
|
||||
mov x4, x1
|
||||
mov x25, x0
|
||||
mov x26, x1
|
||||
|
||||
// Save ASLR Base to use later
|
||||
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
|
||||
adrp x5, __bss_start
|
||||
add x5, x5, #:lo12:__bss_start
|
||||
|
@ -76,3 +94,33 @@ run:
|
|||
|
||||
// call entrypoint
|
||||
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
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче