diff options
author | Lucas Teske <[email protected]> | 2020-05-09 15:39:19 -0300 |
---|---|---|
committer | Ayke <[email protected]> | 2020-07-31 00:58:09 +0200 |
commit | 3650c2c7397b5f21f61c1a4362b28596968c912f (patch) | |
tree | 7c331676822cb4109fb11967cea73e826f2bd138 | |
parent | d4e04e4e493aa4a99659361e28b22ecd51cceee3 (diff) | |
download | tinygo-3650c2c7397b5f21f61c1a4362b28596968c912f.tar.gz tinygo-3650c2c7397b5f21f61c1a4362b28596968c912f.zip |
nintendoswitch: Add experimental Nintendo Switch support without CRT
Bare minimal nintendo switch support using LLD
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | compileopts/config.go | 10 | ||||
-rw-r--r-- | compileopts/target.go | 5 | ||||
-rw-r--r-- | compiler/compiler.go | 12 | ||||
-rw-r--r-- | loader/goroot.go | 2 | ||||
-rw-r--r-- | src/runtime/runtime_nintendoswitch.go | 73 | ||||
-rw-r--r-- | src/runtime/runtime_nintendoswitch.s | 45 | ||||
-rw-r--r-- | src/runtime/runtime_nintendoswitch_heap.go | 31 | ||||
-rw-r--r-- | src/runtime/runtime_nintendoswitch_noheap.go | 7 | ||||
-rw-r--r-- | src/runtime/runtime_nintendoswitch_svc.go | 22 | ||||
-rw-r--r-- | src/runtime/runtime_unix.go | 1 | ||||
-rw-r--r-- | src/runtime/runtime_unix_heap.go | 1 | ||||
-rw-r--r-- | src/runtime/runtime_unix_noheap.go | 2 | ||||
-rw-r--r-- | src/syscall/syscall_libc.go | 2 | ||||
-rw-r--r-- | src/syscall/syscall_nintendoswitch.go | 48 | ||||
-rw-r--r-- | src/syscall/tables_baremetal.go | 2 | ||||
-rw-r--r-- | targets/nintendoswitch.json | 28 | ||||
-rw-r--r-- | targets/nintendoswitch.ld | 61 | ||||
-rw-r--r-- | targets/nintendoswitch.s | 51 |
19 files changed, 401 insertions, 4 deletions
@@ -342,6 +342,8 @@ endif @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -o test.elf -target=nintendoswitch examples/serial + @$(MD5SUM) test.elf wasmtest: $(GO) test ./tests/wasm diff --git a/compileopts/config.go b/compileopts/config.go index 4c30d6b52..fdb26f01b 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -284,6 +284,16 @@ func (c *Config) CodeModel() string { return "default" } +// RelocationModel returns the relocation model in use on this platform. Valid +// values are "static", "pic", "dynamicnopic". +func (c *Config) RelocationModel() string { + if c.Target.RelocationModel != "" { + return c.Target.RelocationModel + } + + return "static" +} + type TestConfig struct { CompileTestBinary bool // TODO: Filter the test functions to run, include verbose flag, etc diff --git a/compileopts/target.go b/compileopts/target.go index 5d5e2f1b3..256e70751 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -50,6 +50,7 @@ type TargetSpec struct { OpenOCDTransport string `json:"openocd-transport"` JLinkDevice string `json:"jlink-device"` CodeModel string `json:"code-model"` + RelocationModel string `json:"relocation-model"` } // copyProperties copies all properties that are set in spec2 into itself. @@ -134,6 +135,10 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) { if spec2.CodeModel != "" { spec.CodeModel = spec2.CodeModel } + + if spec2.RelocationModel != "" { + spec.RelocationModel = spec2.RelocationModel + } } // load reads a target specification from the JSON in the given io.Reader. It diff --git a/compiler/compiler.go b/compiler/compiler.go index 7b52cb66b..cd5b15eed 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -93,6 +93,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) { features := strings.Join(config.Features(), ",") var codeModel llvm.CodeModel + var relocationModel llvm.RelocMode switch config.CodeModel() { case "default": @@ -109,7 +110,16 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) { codeModel = llvm.CodeModelLarge } - machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, codeModel) + switch config.RelocationModel() { + case "static": + relocationModel = llvm.RelocStatic + case "pic": + relocationModel = llvm.RelocPIC + case "dynamicnopic": + relocationModel = llvm.RelocDynamicNoPic + } + + machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, relocationModel, codeModel) return machine, nil } diff --git a/loader/goroot.go b/loader/goroot.go index 7947d15e2..d9cf6ad7e 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -165,7 +165,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides // with the TinyGo version. This is the case on some targets. func needsSyscallPackage(buildTags []string) bool { for _, tag := range buildTags { - if tag == "baremetal" || tag == "darwin" { + if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" { return true } } diff --git a/src/runtime/runtime_nintendoswitch.go b/src/runtime/runtime_nintendoswitch.go new file mode 100644 index 000000000..690f559e1 --- /dev/null +++ b/src/runtime/runtime_nintendoswitch.go @@ -0,0 +1,73 @@ +// +build nintendoswitch + +package runtime + +type timeUnit int64 + +const asyncScheduler = false + +func postinit() {} + +// Entry point for Go. Initialize all packages and call main.main(). +//export main +func main() int { + preinit() + run() + + // Call exit to correctly finish the program + // Without this, the application crashes at start, not sure why + return exit(0) +} + +// sleepTicks sleeps for the specified system ticks +func sleepTicks(d timeUnit) { + sleepThread(uint64(ticksToNanoseconds(d))) +} + +// armTicksToNs converts cpu ticks to nanoseconds +// Nintendo Switch CPU ticks has a fixed rate at 19200000 +// It is basically 52 ns per tick +// The formula 625 / 12 is equivalent to 1e9 / 19200000 +func ticksToNanoseconds(tick timeUnit) int64 { + return int64(tick * 625 / 12) +} + +func nanosecondsToTicks(ns int64) timeUnit { + return timeUnit(12 * ns / 625) +} + +func ticks() timeUnit { + return timeUnit(ticksToNanoseconds(timeUnit(getArmSystemTick()))) +} + +var stdoutBuffer = make([]byte, 0, 120) + +func putchar(c byte) { + if c == '\n' || len(stdoutBuffer)+1 >= 120 { + NxOutputString(string(stdoutBuffer)) + stdoutBuffer = stdoutBuffer[:0] + return + } + + stdoutBuffer = append(stdoutBuffer, c) +} + +func usleep(usec uint) int { + sleepThread(uint64(usec) * 1000) + return 0 +} + +func abort() { + for { + exit(1) + } +} + +//export sleepThread +func sleepThread(nanos uint64) + +//export exit +func exit(code int) int + +//export armGetSystemTick +func getArmSystemTick() int64 diff --git a/src/runtime/runtime_nintendoswitch.s b/src/runtime/runtime_nintendoswitch.s new file mode 100644 index 000000000..58ffe273f --- /dev/null +++ b/src/runtime/runtime_nintendoswitch.s @@ -0,0 +1,45 @@ +.section .text.armGetSystemTick, "ax", %progbits +.global armGetSystemTick +.type armGetSystemTick, %function +.align 2 +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: + str x0, [sp, #-16]! + svc 0x1 + ldr x2, [sp], #16 + str x1, [x2] + ret + + +.section .text.sleepThread, "ax", %progbits +.global sleepThread +.type sleepThread, %function +.align 2 +sleepThread: + svc 0xB + ret diff --git a/src/runtime/runtime_nintendoswitch_heap.go b/src/runtime/runtime_nintendoswitch_heap.go new file mode 100644 index 000000000..5518825e3 --- /dev/null +++ b/src/runtime/runtime_nintendoswitch_heap.go @@ -0,0 +1,31 @@ +// +build nintendoswitch + +// +build gc.conservative gc.leaking + +package runtime + +import "unsafe" + +const heapSize = 0x2000000 * 16 // Default by libnx + +//go:extern _stack_top +var stackTopSymbol [0]byte + +var ( + heapStart = uintptr(0) + heapEnd = uintptr(0) + stackTop = uintptr(unsafe.Pointer(&stackTopSymbol)) +) + +//export setHeapSize +func setHeapSize(addr *uintptr, length uint64) uint64 + +func preinit() { + setHeapSize(&heapStart, heapSize) + + if heapStart == 0 { + panic("failed to allocate heap") + } + + heapEnd = heapStart + heapSize +} diff --git a/src/runtime/runtime_nintendoswitch_noheap.go b/src/runtime/runtime_nintendoswitch_noheap.go new file mode 100644 index 000000000..41e0b3683 --- /dev/null +++ b/src/runtime/runtime_nintendoswitch_noheap.go @@ -0,0 +1,7 @@ +// +build nintendoswitch + +// +build gc.none gc.extalloc + +package runtime + +func preinit() {} diff --git a/src/runtime/runtime_nintendoswitch_svc.go b/src/runtime/runtime_nintendoswitch_svc.go new file mode 100644 index 000000000..2bc2003f9 --- /dev/null +++ b/src/runtime/runtime_nintendoswitch_svc.go @@ -0,0 +1,22 @@ +// +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) diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index acb830946..a3c2b4471 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -1,4 +1,5 @@ // +build darwin linux,!baremetal freebsd,!baremetal +// +build !nintendoswitch package runtime diff --git a/src/runtime/runtime_unix_heap.go b/src/runtime/runtime_unix_heap.go index c9b04f1d8..14168f438 100644 --- a/src/runtime/runtime_unix_heap.go +++ b/src/runtime/runtime_unix_heap.go @@ -1,4 +1,5 @@ // +build darwin linux,!baremetal freebsd,!baremetal +// +build !nintendoswitch // +build gc.conservative gc.leaking diff --git a/src/runtime/runtime_unix_noheap.go b/src/runtime/runtime_unix_noheap.go index 4802fdabf..bf7f67598 100644 --- a/src/runtime/runtime_unix_noheap.go +++ b/src/runtime/runtime_unix_noheap.go @@ -1,5 +1,7 @@ // +build darwin linux,!baremetal freebsd,!baremetal +// +build !nintendoswitch + // +build gc.none gc.extalloc package runtime diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 3f24ddb70..337a2f011 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -1,4 +1,4 @@ -// +build darwin +// +build darwin nintendoswitch package syscall diff --git a/src/syscall/syscall_nintendoswitch.go b/src/syscall/syscall_nintendoswitch.go new file mode 100644 index 000000000..8a81de1e6 --- /dev/null +++ b/src/syscall/syscall_nintendoswitch.go @@ -0,0 +1,48 @@ +// +build nintendoswitch + +package syscall + +import "errors" + +// A Signal is a number describing a process signal. +// It implements the os.Signal interface. +type Signal int + +const ( + _ Signal = iota + SIGCHLD + SIGINT + SIGKILL + SIGTRAP + SIGQUIT + SIGTERM +) + +// File system + +const ( + Stdin = 0 + Stdout = 1 + Stderr = 2 +) + +const ( + O_RDONLY = 0 + O_WRONLY = 1 + O_RDWR = 2 + + O_CREAT = 0100 + O_CREATE = O_CREAT + O_TRUNC = 01000 + O_APPEND = 02000 + O_EXCL = 0200 + O_SYNC = 010000 + + O_CLOEXEC = 0 +) + +var dummyError = errors.New("unknown syscall error") + +func getErrno() error { + return dummyError +} diff --git a/src/syscall/tables_baremetal.go b/src/syscall/tables_baremetal.go index c081feace..47a536bff 100644 --- a/src/syscall/tables_baremetal.go +++ b/src/syscall/tables_baremetal.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build baremetal +// +build baremetal nintendoswitch package syscall diff --git a/targets/nintendoswitch.json b/targets/nintendoswitch.json new file mode 100644 index 000000000..583d68f06 --- /dev/null +++ b/targets/nintendoswitch.json @@ -0,0 +1,28 @@ +{ + "llvm-target": "aarch64", + "build-tags": ["nintendoswitch", "arm64"], + "goos": "linux", + "goarch": "arm64", + "compiler": "clang", + "linker": "ld.lld", + "rtlib": "compiler-rt", + "libc": "picolibc", + "gc": "conservative", + "relocation-model": "pic", + "cflags": [ + "-target", "aarch64-none-linux-gnu", + "-mtune=cortex-a57", + "-fPIE", + "-Werror", + "-Qunused-arguments", + "-fshort-enums", + "-fomit-frame-pointer", + "-fno-exceptions", "-fno-unwind-tables", + "-ffunction-sections", "-fdata-sections" + ], + "linkerscript": "targets/nintendoswitch.ld", + "extra-files": [ + "targets/nintendoswitch.s", + "src/runtime/runtime_nintendoswitch.s" + ] +} diff --git a/targets/nintendoswitch.ld b/targets/nintendoswitch.ld new file mode 100644 index 000000000..e61391c83 --- /dev/null +++ b/targets/nintendoswitch.ld @@ -0,0 +1,61 @@ +OUTPUT_FORMAT(elf64-littleaarch64) +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + bss PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC; +} + +SECTIONS +{ + . = 0; + + .text : ALIGN(0x1000) { + HIDDEN(__text_start = .); + KEEP(*(.text.jmp)) + + . = 0x80; + + *(.text .text.*) + } + + /* Read-only sections */ + + . = ALIGN(0x1000); + + .rodata : { *(.rodata .rodata.*) } :rodata + .mod0 : { + KEEP(crt0.nso.o(.data.mod0)) + KEEP(crt0.nro.o(.data.mod0)) + KEEP(crt0.lib.nro.o(.data.mod0)) + } + + /* Read-write sections */ + . = ALIGN(0x1000); + + .data : { + *(.data .data.*) + } :data + + .dynamic : { + HIDDEN(__dynamic_start = .); + *(.dynamic) + } + + /* BSS section */ + + . = ALIGN(0x1000); + + .bss : { + HIDDEN(__bss_start = .); + *(.bss .bss.*) + *(COMMON) + . = ALIGN(8); + HIDDEN(__bss_end = .); + } :bss +} diff --git a/targets/nintendoswitch.s b/targets/nintendoswitch.s new file mode 100644 index 000000000..8e36fe7a6 --- /dev/null +++ b/targets/nintendoswitch.s @@ -0,0 +1,51 @@ +.section .text.jmp, "x" +.global _start +_start: + b start + .word _mod_header - _start + +.section .data.mod0 + .word 0, 8 + +.global _mod_header +_mod_header: + .ascii "MOD0" + .word __dynamic_start - _mod_header + .word __bss_start - _mod_header + .word __bss_end - _mod_header + .word 0, 0 // eh_frame_hdr start/end + .word 0 // runtime-generated module object offset + +.section .text, "x" +.global start +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 + + // clear .bss + adrp x5, __bss_start + add x5, x5, #:lo12:__bss_start + adrp x6, __bss_end + add x6, x6, #:lo12:__bss_end + +bssloop: + cmp x5, x6 + b.eq run + str xzr, [x5] + add x5, x5, 8 + b bssloop + +run: + // call entrypoint + adrp x30, exit + add x30, x30, #:lo12:exit + b main |