diff --git a/Makefile b/Makefile index 9598f982..e59799ca 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/compileopts/config.go b/compileopts/config.go index c2eb17d1..a08a9ba1 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -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 diff --git a/targets/nintendoswitch.ld b/targets/nintendoswitch.ld index e61391c8..cc26a420 100644 --- a/targets/nintendoswitch.ld +++ b/targets/nintendoswitch.ld @@ -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. */ + } } diff --git a/targets/nintendoswitch.s b/targets/nintendoswitch.s index 8e36fe7a..e4af2d6d 100644 --- a/targets/nintendoswitch.s +++ b/targets/nintendoswitch.s @@ -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