Skip to content

Instantly share code, notes, and snippets.

@ketsuban
Created December 13, 2021 11:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ketsuban/6bb9b243d3eceebc7e1c7e8ef2d40256 to your computer and use it in GitHub Desktop.
Save ketsuban/6bb9b243d3eceebc7e1c7e8ef2d40256 to your computer and use it in GitHub Desktop.
Linker script for a Rust executable crate targeting the GBA
ENTRY(__start)
/*
* The GBA has two sections of work RAM: internal and external. Internal work
* RAM is essentially instantaneous to access and sits on a 32-bit bus, so it's
* suitable for ARM code. External work RAM has a delay of two clock cycles and
* sits on a 16-bit bus.
*/
MEMORY {
rom (rx) : ORIGIN = 0x08000000, LENGTH = 32M
ewram (rwx) : ORIGIN = 0x02000000, LENGTH = 256K
iwram (rwx) : ORIGIN = 0x03000000, LENGTH = 32K
}
/*
* This is an implementation detail of my interrupt handler.
*/
PROVIDE(INTERRUPT_HANDLERS = 0);
SECTIONS {
/*
* This section contains the ROM header so it's imperative that it go right
* at the beginning of the ROM.
*/
.text.start : {
*(.text.__start)
. = ALIGN(4);
} >rom = 0xFF
.text : {
/*
* It's useful to be able to include text and data in either of the two
* blocks of work RAM. devkitARM, being a thoroughly C-based toolchain,
* does this with filename: `*.iwram.c` is compiled as ARM code and
* placed in internal work RAM, `*.ewram.c` is compiled as Thumb code
* and placed in external work RAM, `*.arm.c` is compiled as ARM code
* and placed in ROM, and everything else is compiled as Thumb code and
* placed in ROM.
*
* That won't fly in Rust, since filenames are tied up with modules, so
* I use the input section name - `.text.iwram*` and `.data.iwram*` are
* placed in internal work RAM, `.text.ewram*` and `.data.ewram*` are
* placed in external work RAM, everything else is placed in ROM.
* Unfortunately, that means I have to use this mess of glob syntax to
* ensure that the `.text` output section doesn't consume precisely the
* input sections I want it to not consume, rather than the much neater
* `*(.text*)`.
*
* I'm sorry.
*/
*(.text .text.[^ie]* .text.[ie][^w]* .text.[ie]w[^r]* .text.[ie]wr[^a]* .text.[ie]wra[^m]*)
. = ALIGN(4);
} >rom = 0xFF
.rodata : {
*(.rodata*)
. = ALIGN(4);
} >rom = 0xFF
.text.iwram : {
*(.text.iwram*)
. = ALIGN(4);
} >iwram AT>rom = 0xFF
.data.iwram : {
*(.data.iwram*)
. = ALIGN(4);
} >iwram AT>rom = 0xFF
.text.ewram : {
*(.text.ewram*)
. = ALIGN(4);
} >ewram AT>rom = 0xFF
.data.ewram : {
*(.data.ewram*)
. = ALIGN(4);
} >ewram AT>rom = 0xFF
.data : {
*(.data*)
. = ALIGN(4);
} >ewram AT>rom = 0xFF
.bss (NOLOAD) : {
*(.bss*)
. = ALIGN(4);
} >iwram
/DISCARD/ : {
/*
* Rust has a bit of a habit of including ARM exception sections for
* some reason even though I'm aborting on panic, and they cause symbol
* lookup failures at link time if included, so I cut them off at the
* pass and make sure they're thrown away just in case.
*/
*(.ARM.exidx.*)
}
}
/*
* Since the work RAM sections are contiguous in the final ROM, I only need one
* copy loop for each one.
*/
__ewram_lma = LOADADDR(.text.ewram);
__ewram_vma = ADDR(.text.ewram);
__ewram_len = SIZEOF(.text.ewram) + SIZEOF(.data.ewram) + SIZEOF(.data);
__iwram_lma = LOADADDR(.text.iwram);
__iwram_vma = ADDR(.text.iwram);
__iwram_len = SIZEOF(.text.iwram) + SIZEOF(.data.iwram);
/*
* Any space in external work RAM not taken up by other things gets used for
* the global heap.
*/
__heap_start = ORIGIN(ewram) + __ewram_len;
__heap_length = LENGTH(ewram) - __heap_start;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment