/* # Developer notes
- Symbols that start with a double underscore (__) are considered "private"
- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
static mut _heap_size }`).
- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
symbol is not dropped if it appears in or near the front of the linker arguments and "it's not
needed" by any of the preceding objects (linker arguments)
- `PROVIDE` is used to provide default values that can be overridden by a user linker script
- In this linker script, you may find symbols that look like `${...}` (e.g., `${ARCH_WIDTH}`).
These are wildcards used by the `build.rs` script to adapt to different target particularities.
Check `build.rs` for more details about these symbols.
- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
the LMA of .data are all `${ARCH_WIDTH}`-byte aligned. These alignments are assumed by the RAM
initialization routine. There's also a second benefit: `${ARCH_WIDTH}`-byte aligned boundaries
means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`.
*/
/* Default abort entry point. If no abort symbol is provided, then abort maps to _default_abort. */
EXTERN(_default_abort);
PROVIDE(abort = _default_abort);
/* Trap for exceptions triggered during initialization. If the execution reaches this point, it
means that there is a bug in the boot code. If no _pre_init_trap symbol is provided, then
_pre_init_trap defaults to _default_abort. Note that _pre_init_trap must be 4-byte aligned */
PROVIDE(_pre_init_trap = _default_abort);
/* Multi-processor hook function (for multi-core targets only). If no _mp_hook symbol
is provided, then _mp_hook maps to _default_mp_hook, which leaves HART 0 running while
the other HARTS stuck in a busy loop. Note that _default_mp_hook cannot be overwritten.
We use PROVIDE to avoid compilation errors in single hart targets, not to allow users
to overwrite the symbol. */
PROVIDE(_default_mp_hook = abort);
PROVIDE(_mp_hook = _default_mp_hook);
/* Default trap entry point. If not _start_trap symbol is provided, then _start_trap maps to
_default_start_trap, which saves caller saved registers, calls _start_trap_rust, restores
caller saved registers and then returns. Note that _start_trap must be 4-byte aligned */
EXTERN(_default_start_trap);
PROVIDE(_start_trap = _default_start_trap);
/* Default interrupt setup entry point. If not _setup_interrupts symbol is provided, then
_setup_interrupts maps to _default_setup_interrupts, which in direct mode sets the value
of the xtvec register to _start_trap and, in vectored mode, sets its value to
_vector_table and enables vectored mode. */
EXTERN(_default_setup_interrupts);
PROVIDE(_setup_interrupts = _default_setup_interrupts);
/* Default main routine. If no hal_main symbol is provided, then hal_main maps to main, which
is usually defined by final users via the #[riscv_rt::entry] attribute. Using hal_main
instead of main directly allow HALs to inject code before jumping to user main. */
PROVIDE(hal_main = main);
/* Default exception handler. By default, the exception handler is abort.
Users can override this alias by defining the symbol themselves */
PROVIDE(ExceptionHandler = abort);
/* Default interrupt handler. By default, the interrupt handler is abort.
Users can override this alias by defining the symbol themselves */
PROVIDE(DefaultHandler = abort);
/* Default interrupt trap entry point. When vectored trap mode is enabled,
the riscv-rt crate provides an implementation of this function, which saves caller saved
registers, calls the the DefaultHandler ISR, restores caller saved registers and returns.
Note, however, that this provided implementation cannot be overwritten. We use PROVIDE
to avoid compilation errors in direct mode, not to allow users to overwrite the symbol. */
PROVIDE(_start_DefaultHandler_trap = _start_trap);
${INCLUDE_LINKER_FILES}
PROVIDE(_stext = ORIGIN(REGION_TEXT));
PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
PROVIDE(_max_hart_id = 0);
PROVIDE(_hart_stack_size = SIZEOF(.stack) / (_max_hart_id + 1));
PROVIDE(_heap_size = 0);
SECTIONS
{
.text.dummy (NOLOAD) :
{
/* This section is intended to make _stext address work */
. = ABSOLUTE(_stext);
} > REGION_TEXT
.text _stext :
{
__stext = .;
/* Put reset handler first in .text section so it ends up as the entry */
/* point of the program. */
KEEP(*(.init));
. = ALIGN(4);
KEEP(*(.trap.vector)); /* for _trap_vector (vectored mode only) */
KEEP(*(.trap.start)); /* for _start_trap routine */
KEEP(*(.trap.start.*)); /* for _start_INTERRUPT_trap routines (vectored mode only) */
KEEP(*(.trap.continue)); /* for _continue_trap routine (vectored mode only) */
KEEP(*(.trap.rust)); /* for _start_trap_rust Rust function */
KEEP(*(.trap .trap.*)); /* Other .trap symbols at the end */
*(.text.abort);
*(.text .text.*);
. = ALIGN(4);
__etext = .;
} > REGION_TEXT
.rodata : ALIGN(4)
{
. = ALIGN(4);
__srodata = .;
*(.srodata .srodata.*);
*(.rodata .rodata.*);
/* ${ARCH_WIDTH}-byte align the end (VMA) of this section.
This is required by LLD to ensure the LMA of the following .data
section will have the correct alignment. */
. = ALIGN(${ARCH_WIDTH});
__erodata = .;
} > REGION_RODATA
.data : ALIGN(${ARCH_WIDTH})
{
. = ALIGN(${ARCH_WIDTH});
__sdata = .;
/* Must be called __global_pointer$ for linker relaxations to work. */
PROVIDE(__global_pointer$ = . + 0x800);
*(.sdata .sdata.* .sdata2 .sdata2.*);
*(.data .data.*);
} > REGION_DATA AT > REGION_RODATA
/* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to
* use the .data loading mechanism by pushing __edata. Note: do not change
* output region or load region in those user sections! */
. = ALIGN(${ARCH_WIDTH});
__edata = .;
/* LMA of .data */
__sidata = LOADADDR(.data);
.bss (NOLOAD) : ALIGN(${ARCH_WIDTH})
{
. = ALIGN(${ARCH_WIDTH});
__sbss = .;
*(.sbss .sbss.* .bss .bss.*);
} > REGION_BSS
/* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to
* use the .bss zeroing mechanism by pushing __ebss. Note: do not change
* output region or load region in those user sections! */
. = ALIGN(${ARCH_WIDTH});
__ebss = .;
/* Uninitialized data segment. In contrast with .bss, .uninit is not initialized to zero by
* the runtime, and might contain residual data from previous executions or random values
* if not explicitly initialized. While .bss and .uninit are different sections, they are
* both allocated at REGION_BSS, as their purpose is similar. */
.uninit (NOLOAD) : ALIGN(${ARCH_WIDTH})
{
. = ALIGN(${ARCH_WIDTH});
__suninit = .;
*(.uninit .uninit.*);
. = ALIGN(${ARCH_WIDTH});
__euninit = .;
} > REGION_BSS
/* fictitious region that represents the memory available for the heap */
.heap (NOLOAD) : ALIGN(4)
{
__sheap = .;
. += _heap_size;
. = ALIGN(4);
__eheap = .;
} > REGION_HEAP
/* fictitious region that represents the memory available for the stack */
.stack (NOLOAD) :
{
__estack = .;
. = ABSOLUTE(_stack_start);
__sstack = .;
} > REGION_STACK
/* fake output .got section */
/* Dynamic relocations are unsupported. This section is only used to detect
relocatable code in the input files and raise an error if relocatable code
is found */
.got (INFO) :
{
KEEP(*(.got .got.*));
}
}
/* Do not exceed this mark in the error messages above | */
ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned");
ASSERT(ORIGIN(REGION_DATA) % ${ARCH_WIDTH} == 0, "
ERROR(riscv-rt): the start of the REGION_DATA must be ${ARCH_WIDTH}-byte aligned");
ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned");
ASSERT(ORIGIN(REGION_STACK) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned");
ASSERT(_stext % 4 == 0, "
ERROR(riscv-rt): `_stext` must be 4-byte aligned");
ASSERT(__sdata % ${ARCH_WIDTH} == 0 && __edata % ${ARCH_WIDTH} == 0, "
BUG(riscv-rt): .data is not ${ARCH_WIDTH}-byte aligned");
ASSERT(__sidata % ${ARCH_WIDTH} == 0, "
BUG(riscv-rt): the LMA of .data is not ${ARCH_WIDTH}-byte aligned");
ASSERT(__sbss % ${ARCH_WIDTH} == 0 && __ebss % ${ARCH_WIDTH} == 0, "
BUG(riscv-rt): .bss is not ${ARCH_WIDTH}-byte aligned");
ASSERT(__sheap % 4 == 0, "
BUG(riscv-rt): start of .heap is not 4-byte aligned");
ASSERT(_pre_init_trap % 4 == 0, "
BUG(riscv-rt): _pre_init_trap is not 4-byte aligned");
ASSERT(_start_trap % 4 == 0, "
BUG(riscv-rt): _start_trap is not 4-byte aligned");
ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), "
ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region.
Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'");
ASSERT(SIZEOF(.stack) >= (_max_hart_id + 1) * _hart_stack_size, "
ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts.
Consider changing `_max_hart_id` or `_hart_stack_size`.");
/* # Other checks */
ASSERT(SIZEOF(.got) == 0, "
ERROR(riscv-rt): .got section detected in the input files. Dynamic relocations are not
supported. If you are linking to C code compiled using the `cc` crate then modify your
build script to compile the C code _without_ the -fPIC flag. See the documentation of
the `cc::Build.pic` method for details.");
/* Do not exceed this mark in the error messages above | */