nintendoswitch: support outputting .nro files directly

By modifying the linker script a bit and adding the NRO0 header directly
in the assembly, it's possible to craft an ELF file that can be
converted straight to a binary (using objcopy or similar) that is a NRO
file. This avoids custom code for NRO files or an extra build step.

With another change, .nro files are recognized by TinyGo so that this
will create a ready-to-run NRO file:

    tinygo build -o test.nro -target=nintendoswitch examples/serial
Этот коммит содержится в:
Ayke van Laethem 2020-09-08 22:44:05 +02:00 коммит произвёл Ron Evans
родитель 81e325205f
коммит 9e599bac49
4 изменённых файлов: 64 добавлений и 30 удалений

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

@ -373,8 +373,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 $(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
@$(MD5SUM) test.elf @$(MD5SUM) test.nro
wasmtest: wasmtest:
$(GO) test ./tests/wasm $(GO) test ./tests/wasm

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

@ -243,7 +243,7 @@ func (c *Config) Debug() bool {
// extension and the configured binary format in the target JSON file. // extension and the configured binary format in the target JSON file.
func (c *Config) BinaryFormat(ext string) string { func (c *Config) BinaryFormat(ext string) string {
switch ext { switch ext {
case ".bin", ".gba": case ".bin", ".gba", ".nro":
// The simplest format possible: dump everything in a raw binary file. // The simplest format possible: dump everything in a raw binary file.
if c.Target.BinaryFormat != "" { if c.Target.BinaryFormat != "" {
return c.Target.BinaryFormat return c.Target.BinaryFormat

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

@ -1,61 +1,69 @@
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 SECTIONS
{ {
. = 0; . = 0;
.text : ALIGN(0x1000) { /* Code and file header */
.text : {
HIDDEN(__text_start = .); HIDDEN(__text_start = .);
KEEP(*(.text.jmp)) KEEP(*(.text.jmp))
. = 0x80; . = 0x80;
*(.text .text.*) *(.text .text.*)
. = ALIGN(0x1000);
HIDDEN(__text_end = .);
HIDDEN(__text_size = . - __text_start);
} }
/* Read-only sections */ /* Read-only sections */
. = ALIGN(0x1000); .rodata : {
HIDDEN(__rodata_start = .);
*(.rodata .rodata.*)
*(.got)
.rodata : { *(.rodata .rodata.*) } :rodata
.mod0 : {
KEEP(crt0.nso.o(.data.mod0)) KEEP(crt0.nso.o(.data.mod0))
KEEP(crt0.nro.o(.data.mod0)) KEEP(crt0.nro.o(.data.mod0))
KEEP(crt0.lib.nro.o(.data.mod0)) KEEP(crt0.lib.nro.o(.data.mod0))
KEEP(*(.data.mod0))
HIDDEN(__dynamic_start = .);
*(.dynamic)
. = ALIGN(0x1000);
HIDDEN(__rodata_end = .);
HIDDEN(__rodata_size = . - __rodata_start);
} }
/* Read-write sections */ /* Read-write sections */
. = ALIGN(0x1000);
.data : { .data : {
*(.data .data.*) HIDDEN(__data_start = .);
} :data
.dynamic : { *(.data .data.*)
HIDDEN(__dynamic_start = .);
*(.dynamic) HIDDEN(__data_end = .);
HIDDEN(__data_size = . - __data_start);
} }
/* BSS section */ /* BSS section */
. = ALIGN(0x1000);
.bss : { .bss : {
HIDDEN(__bss_start = .); HIDDEN(__bss_start = .);
*(.bss .bss.*) *(.bss .bss.*)
*(COMMON) *(COMMON)
. = ALIGN(8);
HIDDEN(__bss_end = .); HIDDEN(__bss_end = .);
} :bss HIDDEN(__bss_size = . - __bss_start);
}
/DISCARD/ :
{
*(.eh_frame) /* This is probably unnecessary and bloats the binary. */
}
} }

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

@ -1,8 +1,34 @@
// For more information on the .nro file format, see:
// https://switchbrew.org/wiki/NRO
.section .text.jmp, "x" .section .text.jmp, "x"
.global _start .global _start
_start: _start:
b start b start
.word _mod_header - _start .word _mod_header - _start
.word 0
.word 0
.ascii "NRO0" // magic
.word 0 // version (always 0)
.word __bss_start - _start // total NRO file size
.word 0 // flags (unused)
// segment headers
.word __text_start
.word __text_size
.word __rodata_start
.word __rodata_size
.word __data_start
.word __data_size
.word __bss_size
.word 0
// ModuleId (not supported)
. = 0x50; // skip 32 bytes
.word 0 // DSO Module Offset (unused)
.word 0 // reserved (unused)
.section .data.mod0 .section .data.mod0
.word 0, 8 .word 0, 8