nintendoswitch: Add experimental Nintendo Switch support without CRT

Bare minimal nintendo switch support using LLD
Этот коммит содержится в:
Lucas Teske 2020-05-09 15:39:19 -03:00 коммит произвёл Ayke
родитель d4e04e4e49
коммит 3650c2c739
19 изменённых файлов: 401 добавлений и 4 удалений

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

@ -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

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

@ -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

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

@ -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

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

@ -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
}

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

@ -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
}
}

73
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

45
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

31
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
}

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

@ -0,0 +1,7 @@
// +build nintendoswitch
// +build gc.none gc.extalloc
package runtime
func preinit() {}

22
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)

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

@ -1,4 +1,5 @@
// +build darwin linux,!baremetal freebsd,!baremetal
// +build !nintendoswitch
package runtime

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

@ -1,4 +1,5 @@
// +build darwin linux,!baremetal freebsd,!baremetal
// +build !nintendoswitch
// +build gc.conservative gc.leaking

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

@ -1,5 +1,7 @@
// +build darwin linux,!baremetal freebsd,!baremetal
// +build !nintendoswitch
// +build gc.none gc.extalloc
package runtime

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

@ -1,4 +1,4 @@
// +build darwin
// +build darwin nintendoswitch
package syscall

48
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
}

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

@ -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

28
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"
]
}

61
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
}

51
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