Introduction
GNU Linker scripts are a powerful way to instruct the linker on how to lay out memory for your firmware project. When developing firmware for a specific microcontroller, which often has unique memory arrangements, customizing a linker script becomes crucial. This allows you to define how different sections of your code are placed in memory, which is essential for optimizing performance and meeting system requirements.
Understanding Linker Script Layout
A GNU Linker script generally consists of three parts:
<html><b>MEMORY</b></html>
: Defines the available memory regions in the microcontroller.
<html><b>SECTIONS</b></html>
: Specifies how input sections in your compiled object files are mapped to output sections in memory.
<html><b>ENTRY</b></html>
(optional): Defines the entry point for your application.
Customizing the MEMORY Section
In the MEMORY block, you define the memory regions of your specific microcontroller. This could include Flash, RAM, EEPROM, etc. For example, if you have a microcontroller with 64KB of Flash and 8KB of RAM, the MEMORY section could look like this:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 8K
}
<html><b>ORIGIN</b></html>
defines the start address of the memory region.
<html><b>LENGTH</b></html>
specifies the size of the memory region.
- The memory block attributes such as
(rx)
or (rwx)
dictate the permissions (read, write, execute).
Defining the SECTIONS in the Linker Script
In the SECTIONS block, you specify how different input sections, generated by the compiler (like .text
, .data
, .bss
, etc.), are combined into output sections and where they are placed in the defined memory regions. An example SECTIONS block might appear as follows:
SECTIONS
{
.text :
{
*(.text .text.*)
KEEP(*(.isr_vector))
} > FLASH
.data : AT(ADDR(.text) + SIZEOF(.text))
{
*(.data)
} > RAM
.bss (NOLOAD) :
{
__bss_start__ = .;
*(.bss)
__bss_end__ = .;
} > RAM
. = ALIGN(4);
_estack = ORIGIN(RAM) + LENGTH(RAM);
}
- The
<html><b>.text</b></html>
section is mapped to FLASH, while <html><b>.data</b></html>
and <html><b>.bss</b></html>
are placed in RAM.
- The
KEEP()
directive ensures that certain sections are retained in the output even if they appear to be unused, which can be crucial for sections like interrupt vectors.
<html><b>AT</b></html>
is used to load .data
into FLASH initially and later copied to RAM on startup.
Creating Linker Script Symbols
Linker scripts allow you to define symbols that can be used in your code. These are helpful for initializing sections or defining boundaries:
__bss_start__
and __bss_end__
symbols mark the beginning and end of your .bss
section and can be used in C code to zero initialize these sections.
_estack
represents the top of the stack, calculated as the end of the RAM.
Practical Considerations
When customizing linker scripts, consider the following:
- Memory Alignment: Align sections as per your architecture requirements (e.g., 4-byte alignment for 32-bit architectures).
- Stack and Heap Size: Remember to allocate enough space for the stack and heap, and keep track of stack overflows.
- Size Constraints: Ensure that the total length of all sections does not exceed the defined memory sizes.
It's crucial to refer to your microcontroller's documentation to accurately configure these memory regions.
Conclusion
Custom linker scripts are vital for effectively utilizing a microcontroller's memory. Adjusting the MEMORY and SECTIONS directives allows firmware developers to control where and how their program is loaded and executed. Advanced manipulation of these scripts can significantly impact the system's efficiency and reliability.