Skip to content

Commit 9e599ba

Browse files
aykevldeadprogram
authored andcommitted
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
1 parent 81e3252 commit 9e599ba

File tree

4 files changed

+63
-29
lines changed

4 files changed

+63
-29
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,8 @@ endif
373373
@$(MD5SUM) test.hex
374374
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
375375
@$(MD5SUM) test.hex
376-
$(TINYGO) build -o test.elf -target=nintendoswitch examples/serial
377-
@$(MD5SUM) test.elf
376+
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
377+
@$(MD5SUM) test.nro
378378

379379
wasmtest:
380380
$(GO) test ./tests/wasm

compileopts/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ func (c *Config) Debug() bool {
243243
// extension and the configured binary format in the target JSON file.
244244
func (c *Config) BinaryFormat(ext string) string {
245245
switch ext {
246-
case ".bin", ".gba":
246+
case ".bin", ".gba", ".nro":
247247
// The simplest format possible: dump everything in a raw binary file.
248248
if c.Target.BinaryFormat != "" {
249249
return c.Target.BinaryFormat

targets/nintendoswitch.ld

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,69 @@
1-
OUTPUT_FORMAT(elf64-littleaarch64)
2-
OUTPUT_ARCH(aarch64)
3-
ENTRY(_start)
4-
5-
PHDRS
6-
{
7-
text PT_LOAD FLAGS(5);
8-
rodata PT_LOAD FLAGS(4);
9-
data PT_LOAD FLAGS(6);
10-
bss PT_LOAD FLAGS(6);
11-
dynamic PT_DYNAMIC;
12-
}
13-
141
SECTIONS
152
{
163
. = 0;
174

18-
.text : ALIGN(0x1000) {
5+
/* Code and file header */
6+
7+
.text : {
198
HIDDEN(__text_start = .);
209
KEEP(*(.text.jmp))
2110

2211
. = 0x80;
2312

2413
*(.text .text.*)
14+
15+
. = ALIGN(0x1000);
16+
HIDDEN(__text_end = .);
17+
HIDDEN(__text_size = . - __text_start);
2518
}
2619

2720
/* Read-only sections */
2821

29-
. = ALIGN(0x1000);
22+
.rodata : {
23+
HIDDEN(__rodata_start = .);
24+
25+
*(.rodata .rodata.*)
26+
27+
*(.got)
3028

31-
.rodata : { *(.rodata .rodata.*) } :rodata
32-
.mod0 : {
3329
KEEP(crt0.nso.o(.data.mod0))
3430
KEEP(crt0.nro.o(.data.mod0))
3531
KEEP(crt0.lib.nro.o(.data.mod0))
32+
KEEP(*(.data.mod0))
33+
34+
HIDDEN(__dynamic_start = .);
35+
*(.dynamic)
36+
37+
. = ALIGN(0x1000);
38+
HIDDEN(__rodata_end = .);
39+
HIDDEN(__rodata_size = . - __rodata_start);
3640
}
3741

3842
/* Read-write sections */
39-
. = ALIGN(0x1000);
4043

4144
.data : {
45+
HIDDEN(__data_start = .);
46+
4247
*(.data .data.*)
43-
} :data
4448

45-
.dynamic : {
46-
HIDDEN(__dynamic_start = .);
47-
*(.dynamic)
49+
HIDDEN(__data_end = .);
50+
HIDDEN(__data_size = . - __data_start);
4851
}
4952

5053
/* BSS section */
5154

52-
. = ALIGN(0x1000);
53-
5455
.bss : {
5556
HIDDEN(__bss_start = .);
57+
5658
*(.bss .bss.*)
5759
*(COMMON)
58-
. = ALIGN(8);
60+
5961
HIDDEN(__bss_end = .);
60-
} :bss
62+
HIDDEN(__bss_size = . - __bss_start);
63+
}
64+
65+
/DISCARD/ :
66+
{
67+
*(.eh_frame) /* This is probably unnecessary and bloats the binary. */
68+
}
6169
}

targets/nintendoswitch.s

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
1+
// For more information on the .nro file format, see:
2+
// https://switchbrew.org/wiki/NRO
3+
14
.section .text.jmp, "x"
25
.global _start
36
_start:
47
b start
58
.word _mod_header - _start
9+
.word 0
10+
.word 0
11+
12+
.ascii "NRO0" // magic
13+
.word 0 // version (always 0)
14+
.word __bss_start - _start // total NRO file size
15+
.word 0 // flags (unused)
16+
17+
// segment headers
18+
.word __text_start
19+
.word __text_size
20+
.word __rodata_start
21+
.word __rodata_size
22+
.word __data_start
23+
.word __data_size
24+
.word __bss_size
25+
.word 0
26+
27+
// ModuleId (not supported)
28+
. = 0x50; // skip 32 bytes
29+
30+
.word 0 // DSO Module Offset (unused)
31+
.word 0 // reserved (unused)
632

733
.section .data.mod0
834
.word 0, 8

0 commit comments

Comments
 (0)