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

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

@ -243,7 +243,7 @@ func (c *Config) Debug() bool {
// extension and the configured binary format in the target JSON file.
func (c *Config) BinaryFormat(ext string) string {
switch ext {
case ".bin", ".gba":
case ".bin", ".gba", ".nro":
// The simplest format possible: dump everything in a raw binary file.
if 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
{
. = 0;
.text : ALIGN(0x1000) {
/* Code and file header */
.text : {
HIDDEN(__text_start = .);
KEEP(*(.text.jmp))
. = 0x80;
*(.text .text.*)
. = ALIGN(0x1000);
HIDDEN(__text_end = .);
HIDDEN(__text_size = . - __text_start);
}
/* 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.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 */
. = ALIGN(0x1000);
.data : {
*(.data .data.*)
} :data
HIDDEN(__data_start = .);
.dynamic : {
HIDDEN(__dynamic_start = .);
*(.dynamic)
*(.data .data.*)
HIDDEN(__data_end = .);
HIDDEN(__data_size = . - __data_start);
}
/* BSS section */
. = ALIGN(0x1000);
.bss : {
HIDDEN(__bss_start = .);
*(.bss .bss.*)
*(COMMON)
. = ALIGN(8);
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"
.global _start
_start:
b 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
.word 0, 8