nintendoswitch: Add experimental Nintendo Switch support without CRT
Bare minimal nintendo switch support using LLD
Этот коммит содержится в:
родитель
d4e04e4e49
коммит
3650c2c739
19 изменённых файлов: 401 добавлений и 4 удалений
2
Makefile
2
Makefile
|
@ -342,6 +342,8 @@ endif
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -o test.elf -target=nintendoswitch examples/serial
|
||||||
|
@$(MD5SUM) test.elf
|
||||||
|
|
||||||
wasmtest:
|
wasmtest:
|
||||||
$(GO) test ./tests/wasm
|
$(GO) test ./tests/wasm
|
||||||
|
|
|
@ -284,6 +284,16 @@ func (c *Config) CodeModel() string {
|
||||||
return "default"
|
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 {
|
type TestConfig struct {
|
||||||
CompileTestBinary bool
|
CompileTestBinary bool
|
||||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||||
|
|
|
@ -50,6 +50,7 @@ type TargetSpec struct {
|
||||||
OpenOCDTransport string `json:"openocd-transport"`
|
OpenOCDTransport string `json:"openocd-transport"`
|
||||||
JLinkDevice string `json:"jlink-device"`
|
JLinkDevice string `json:"jlink-device"`
|
||||||
CodeModel string `json:"code-model"`
|
CodeModel string `json:"code-model"`
|
||||||
|
RelocationModel string `json:"relocation-model"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyProperties copies all properties that are set in spec2 into itself.
|
// copyProperties copies all properties that are set in spec2 into itself.
|
||||||
|
@ -134,6 +135,10 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
||||||
if spec2.CodeModel != "" {
|
if spec2.CodeModel != "" {
|
||||||
spec.CodeModel = 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
|
// 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(), ",")
|
features := strings.Join(config.Features(), ",")
|
||||||
|
|
||||||
var codeModel llvm.CodeModel
|
var codeModel llvm.CodeModel
|
||||||
|
var relocationModel llvm.RelocMode
|
||||||
|
|
||||||
switch config.CodeModel() {
|
switch config.CodeModel() {
|
||||||
case "default":
|
case "default":
|
||||||
|
@ -109,7 +110,16 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||||
codeModel = llvm.CodeModelLarge
|
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
|
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.
|
// with the TinyGo version. This is the case on some targets.
|
||||||
func needsSyscallPackage(buildTags []string) bool {
|
func needsSyscallPackage(buildTags []string) bool {
|
||||||
for _, tag := range buildTags {
|
for _, tag := range buildTags {
|
||||||
if tag == "baremetal" || tag == "darwin" {
|
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
73
src/runtime/runtime_nintendoswitch.go
Обычный файл
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
Обычный файл
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
Обычный файл
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
|
||||||
|
}
|
7
src/runtime/runtime_nintendoswitch_noheap.go
Обычный файл
7
src/runtime/runtime_nintendoswitch_noheap.go
Обычный файл
|
@ -0,0 +1,7 @@
|
||||||
|
// +build nintendoswitch
|
||||||
|
|
||||||
|
// +build gc.none gc.extalloc
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
func preinit() {}
|
22
src/runtime/runtime_nintendoswitch_svc.go
Обычный файл
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 darwin linux,!baremetal freebsd,!baremetal
|
||||||
|
// +build !nintendoswitch
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// +build darwin linux,!baremetal freebsd,!baremetal
|
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||||
|
// +build !nintendoswitch
|
||||||
|
|
||||||
// +build gc.conservative gc.leaking
|
// +build gc.conservative gc.leaking
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// +build darwin linux,!baremetal freebsd,!baremetal
|
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||||
|
|
||||||
|
// +build !nintendoswitch
|
||||||
|
|
||||||
// +build gc.none gc.extalloc
|
// +build gc.none gc.extalloc
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build darwin
|
// +build darwin nintendoswitch
|
||||||
|
|
||||||
package syscall
|
package syscall
|
||||||
|
|
||||||
|
|
48
src/syscall/syscall_nintendoswitch.go
Обычный файл
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build baremetal
|
// +build baremetal nintendoswitch
|
||||||
|
|
||||||
package syscall
|
package syscall
|
||||||
|
|
||||||
|
|
28
targets/nintendoswitch.json
Обычный файл
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
Обычный файл
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
Обычный файл
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
|
Загрузка…
Создание таблицы
Сослаться в новой задаче