A linkerscript first begins with these lines:
OUTPUT_ARCH (mips)
SECTIONS
{
__romPos = 0;
OUTPUT_ARCH (mips)
simply tells ld
that the output is in the MIPS architecture.
SECTIONS
tells ld
that we will be defining the segments and their order.
__romPos
is an internal variable used by the default macros to define the current location in the ROM.
BEGIN_SEG(boot, 0x04000000)
{
BUILD_DIR/asm/rom_header.o(.text); /* ROM Header */
BUILD_DIR/boot.6102.o(.data); /* CIC 6102 bootcode */
}
END_SEG(boot)
This is the first segment which every script has. This defines the ROM header, and the CIC bootcode. Specifics of these contents are out of scope for this doc.
Now we get to our initial codesegment:
BEGIN_SEG(code, 0x80000400)
{
BUILD_DIR/asm/entry.o(.start);
BUILD_DIR/src/main*.o(.text);
*/libultra_rom.a:*.o(.text);
BEGIN_SEG
declares our segment and the address. Then we get to the lines that include the code itself.
The basic format is PATH/TO/FILE.o(SECTION);
. SECTION
is the section of the object file, for example .text
, .data
, .rodata
, etc.
For including folders of files, the format is PATH/TO/FOLDER*.o(SECTION);
.
In all the demos, BUILD_DIR
is a variable changed by cpp
to be your build directory where built artifacts are stored.
The final line in this example tells ld
to check the libultra_rom.a
library for needed code. When running ld
-lultra_rom
is one of the arguments, so ld
knows where to find the library. To include code from a different library, change libultra_rom
to whatever the other library is.
In the demos, the order of inclusion is .text
, .data
, and .rodata
in that order. Here's a condensed example:
BUILD_DIR/src/main*.o(.text);
*/libultra_rom.a:*.o(.text);
/* data */
BUILD_DIR/src/main*.o(.data*);
*/libultra_rom.a:*.o(.data*);
/* rodata */
BUILD_DIR/src/main*.o(.*rodata*);
*/libultra_rom.a:*.o(.*rodata*);
}
END_SEG(code)
After the segment, include your .bss
data in a NOLOAD
segment like so:
BEGIN_NOLOAD(code)
{
BUILD_DIR/src/main*.o(.*bss*);
*/libultra_rom.a:*.o(.*bss*);
}
END_NOLOAD(code)
ld
does not support including binary blobs. To include binary data, the binaries must be converted into object files. There are multiple methods of doing this, and I recommend you automate this in your makefile or w/e build system you're using.
I know of two methods, which are:
objcopy
:
mips-n64-objcopy -I binary -B mips -O elf32-bigmips INPUTFILE OUTPUTFILE.o
ld
:
mips-n64-ld -r -b binary INPUTFILE -o OUTPUTFILE.o
Then you can include your binary into the script like so:
BEGIN_SEG(rawdata, __romPos)
{
BUILD_DIR/assets/BINARY.o(.data*);
}
END_SEG(rawdata)
In this case, __romPos
is used as the address argument as the binary is supposed to be in ROM only and not given a memory address in RAM.