linker-scripts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Linker Scripts

链接脚本

Purpose

用途

Guide agents through writing and modifying GNU ld linker scripts for embedded targets: MEMORY and SECTIONS commands, VMA vs LMA for code relocation, startup
.bss
/
.data
initialization, placing sections in specific regions, and using PROVIDE/KEEP/ALIGN directives.
指导开发者编写和修改面向嵌入式目标的GNU ld链接脚本:包括MEMORY与SECTIONS命令、用于代码重定位的VMA与LMA、启动阶段的
.bss
/
.data
初始化、将段放置到特定区域,以及使用PROVIDE/KEEP/ALIGN指令。

Triggers

触发场景

  • "How do I write a linker script for my MCU?"
  • "How do I place a function in a specific flash/RAM region?"
  • "What's the difference between VMA and LMA in a linker script?"
  • "How does .bss and .data initialization work at startup?"
  • "Linker error: region 'FLASH' overflowed"
  • "How do I use weak symbols in a linker script?"
  • "如何为我的MCU编写链接脚本?"
  • "如何将函数放置到特定的Flash/RAM区域?"
  • "链接脚本中的VMA和LMA有什么区别?"
  • "启动阶段的.bss和.data初始化是如何工作的?"
  • "链接错误:region 'FLASH' overflowed"
  • "如何在链接脚本中使用弱符号?"

Workflow

工作流程

1. Linker script anatomy

1. 链接脚本结构

ld
/* Minimal Cortex-M linker script */

ENTRY(Reset_Handler)            /* entry point symbol */

MEMORY
{
    FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
    RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

SECTIONS
{
    .text :                     /* code section */
    {
        KEEP(*(.isr_vector))    /* interrupt vector must be first */
        *(.text)
        *(.text.*)
        *(.rodata)
        *(.rodata.*)
        . = ALIGN(4);
        _etext = .;             /* end of flash content */
    } > FLASH

    .data : AT(_etext)          /* VMA = RAM, LMA = FLASH */
    {
        _sdata = .;
        *(.data)
        *(.data.*)
        . = ALIGN(4);
        _edata = .;
    } > RAM

    .bss :
    {
        _sbss = .;
        *(.bss)
        *(.bss.*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;
    } > RAM

    /* Stack at top of RAM */
    _estack = ORIGIN(RAM) + LENGTH(RAM);
}
ld
/* Minimal Cortex-M linker script */

ENTRY(Reset_Handler)            /* entry point symbol */

MEMORY
{
    FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
    RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

SECTIONS
{
    .text :                     /* code section */
    {
        KEEP(*(.isr_vector))    /* interrupt vector must be first */
        *(.text)
        *(.text.*)
        *(.rodata)
        *(.rodata.*)
        . = ALIGN(4);
        _etext = .;             /* end of flash content */
    } > FLASH

    .data : AT(_etext)          /* VMA = RAM, LMA = FLASH */
    {
        _sdata = .;
        *(.data)
        *(.data.*)
        . = ALIGN(4);
        _edata = .;
    } > RAM

    .bss :
    {
        _sbss = .;
        *(.bss)
        *(.bss.*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;
    } > RAM

    /* Stack at top of RAM */
    _estack = ORIGIN(RAM) + LENGTH(RAM);
}

2. VMA vs LMA

2. VMA与LMA

  • VMA (Virtual Memory Address): where the section runs at runtime
  • LMA (Load Memory Address): where the section is stored in the image (flash)
For
.data
: stored in flash (LMA), copied to RAM at startup (VMA).
ld
/* AT() sets LMA explicitly */
.data : AT(ADDR(.text) + SIZEOF(.text))
{
    _sdata = .;
    *(.data)
    _edata = .;
} > RAM          /* VMA goes in RAM */
LMA of
.data
is automatically placed after
.text
if you use
AT(_etext)
.
  • VMA(Virtual Memory Address,虚拟内存地址):段在运行时所在的地址
  • LMA(Load Memory Address,加载内存地址):段在镜像(Flash)中存储的地址
对于
.data
段:存储在Flash(LMA)中,启动时复制到RAM(VMA)。
ld
/* AT() sets LMA explicitly */
.data : AT(ADDR(.text) + SIZEOF(.text))
{
    _sdata = .;
    *(.data)
    _edata = .;
} > RAM          /* VMA goes in RAM */
如果使用
AT(_etext)
.data
的LMA会自动放置在
.text
之后。

3. Startup .bss / .data initialization

3. 启动阶段的.bss / .data初始化

The C runtime must copy
.data
from flash to RAM and zero
.bss
before
main()
:
c
// startup.c or startup.s equivalent in C
extern uint32_t _sdata, _edata, _sidata; // _sidata = LMA of .data
extern uint32_t _sbss, _ebss;

void Reset_Handler(void) {
    // Copy .data from flash to RAM
    uint32_t *src = &_sidata;
    uint32_t *dst = &_sdata;
    while (dst < &_edata) *dst++ = *src++;

    // Zero-initialize .bss
    dst = &_sbss;
    while (dst < &_ebss) *dst++ = 0;

    // Call C++ constructors
    // (call __libc_init_array() if using newlib)

    main();
    for (;;);  // should never return
}
Linker script provides
_sidata
(LMA of
.data
):
ld
.data : AT(_etext)
{
    _sdata = .;
    *(.data)
    _edata = .;
} > RAM

_sidata = LOADADDR(.data);    /* LMA of .data for startup code */
C运行时必须在
main()
之前将
.data
从Flash复制到RAM,并将
.bss
清零:
c
// startup.c or startup.s equivalent in C
extern uint32_t _sdata, _edata, _sidata; // _sidata = LMA of .data
extern uint32_t _sbss, _ebss;

void Reset_Handler(void) {
    // Copy .data from flash to RAM
    uint32_t *src = &_sidata;
    uint32_t *dst = &_sdata;
    while (dst < &_edata) *dst++ = *src++;

    // Zero-initialize .bss
    dst = &_sbss;
    while (dst < &_ebss) *dst++ = 0;

    // Call C++ constructors
    // (call __libc_init_array() if using newlib)

    main();
    for (;;);  // should never return
}
链接脚本提供
_sidata
.data
的LMA):
ld
.data : AT(_etext)
{
    _sdata = .;
    *(.data)
    _edata = .;
} > RAM

_sidata = LOADADDR(.data);    /* LMA of .data for startup code */

4. Placing code in specific regions

4. 将代码放置到特定区域

ld
/* Place time-critical code in RAM for faster execution */
MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
    RAM   (rwx): ORIGIN = 0x20000000, LENGTH = 128K
    CCM   (rwx): ORIGIN = 0x10000000, LENGTH = 64K   /* Cortex-M4 CCM */
}

.fast_code : AT(_etext)
{
    _sfast = .;
    *(.fast_code)   /* sections marked __attribute__((section(".fast_code"))) */
    _efast = .;
} > CCM              /* runs from CCM RAM */
c
// Mark a function to go in fast_code section
__attribute__((section(".fast_code")))
void critical_isr_handler(void) {
    // runs from CCM RAM
}
ld
/* Place time-critical code in RAM for faster execution */
MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
    RAM   (rwx): ORIGIN = 0x20000000, LENGTH = 128K
    CCM   (rwx): ORIGIN = 0x10000000, LENGTH = 64K   /* Cortex-M4 CCM */
}

.fast_code : AT(_etext)
{
    _sfast = .;
    *(.fast_code)   /* sections marked __attribute__((section(".fast_code"))) */
    _efast = .;
} > CCM              /* runs from CCM RAM */
c
// Mark a function to go in fast_code section
__attribute__((section(".fast_code")))
void critical_isr_handler(void) {
    // runs from CCM RAM
}

5. KEEP, ALIGN, PROVIDE

5. KEEP、ALIGN、PROVIDE

ld
/* KEEP — prevent garbage collection of section */
KEEP(*(.isr_vector))        /* linker gc won't remove interrupt table */
KEEP(*(.init))
KEEP(*(.fini))

/* ALIGN — advance location counter to alignment boundary */
. = ALIGN(8);               /* align to 8 bytes */

/* PROVIDE — define symbol only if not already defined (weak default) */
PROVIDE(_stack_size = 0x400);  /* default 1KB stack; override in code */

/* Symbols for stack */
.stack :
{
    . = ALIGN(8);
    . += _stack_size;
    _stack_top = .;
} > RAM

/* FILL — fill unused bytes */
.text :
{
    *(.text)
    . = ALIGN(4);
    FILL(0xFF)             /* fill flash gaps with 0xFF (erased state) */
} > FLASH
ld
/* KEEP — prevent garbage collection of section */
KEEP(*(.isr_vector))        /* linker gc won't remove interrupt table */
KEEP(*(.init))
KEEP(*(.fini))

/* ALIGN — advance location counter to alignment boundary */
. = ALIGN(8);               /* align to 8 bytes */

/* PROVIDE — define symbol only if not already defined (weak default) */
PROVIDE(_stack_size = 0x400);  /* default 1KB stack; override in code */

/* Symbols for stack */
.stack :
{
    . = ALIGN(8);
    . += _stack_size;
    _stack_top = .;
} > RAM

/* FILL — fill unused bytes */
.text :
{
    *(.text)
    . = ALIGN(4);
    FILL(0xFF)             /* fill flash gaps with 0xFF (erased state) */
} > FLASH

6. Weak symbols

6. 弱符号

ld
/* In linker script — provide weak default ISR */
PROVIDE(NMI_Handler        = Default_Handler);
PROVIDE(HardFault_Handler  = Default_Handler);
PROVIDE(SysTick_Handler    = Default_Handler);
c
// In C — weak default handler
__attribute__((weak)) void Default_Handler(void) {
    for (;;);  // spin — override this in application
}

// Override by defining a non-weak symbol with the same name
void SysTick_Handler(void) {
    tick_count++;
}
ld
/* In linker script — provide weak default ISR */
PROVIDE(NMI_Handler        = Default_Handler);
PROVIDE(HardFault_Handler  = Default_Handler);
PROVIDE(SysTick_Handler    = Default_Handler);
c
// In C — weak default handler
__attribute__((weak)) void Default_Handler(void) {
    for (;;);  // spin — override this in application
}

// Override by defining a non-weak symbol with the same name
void SysTick_Handler(void) {
    tick_count++;
}

7. Common linker errors

7. 常见链接错误

ErrorCauseFix
region 'FLASH' overflowed
Binary too large for flashEnable LTO,
-Os
, remove unused code;
--gc-sections
region 'RAM' overflowed
Too much RAM usedReduce stack size, use static buffers, check
.bss
size
undefined reference to '_estack'
Missing linker script symbolDefine
_estack
in linker script
no rule to process file
.ld
extension not recognized
Pass with
-T script.ld
cannot find linker script
Wrong pathUse
-L dir -T name.ld
Data section
.data
at wrong address
LMA not setAdd
AT(_etext)
after
.data
section definition
bash
undefined
错误原因修复方法
region 'FLASH' overflowed
二进制文件过大,超出Flash容量启用LTO、
-Os
优化,移除未使用代码;使用
--gc-sections
region 'RAM' overflowed
RAM使用量过大减小栈大小,使用静态缓冲区,检查
.bss
大小
undefined reference to '_estack'
缺少链接脚本符号在链接脚本中定义
_estack
no rule to process file
.ld
扩展名未被识别
使用
-T script.ld
参数传入脚本
cannot find linker script
路径错误使用
-L dir -T name.ld
指定路径和脚本
Data section
.data
at wrong address
未设置LMA
.data
段定义后添加
AT(_etext)
bash
undefined

Analyze section sizes

Analyze section sizes

arm-none-eabi-size firmware.elf arm-none-eabi-size -A firmware.elf # verbose per-section
arm-none-eabi-size firmware.elf arm-none-eabi-size -A firmware.elf # verbose per-section

Show all sections and their addresses

Show all sections and their addresses

arm-none-eabi-objdump -h firmware.elf
arm-none-eabi-objdump -h firmware.elf

Check if .data LMA is in flash range

Check if .data LMA is in flash range

arm-none-eabi-readelf -S firmware.elf | grep -A2 ".data"

For linker script anatomy details, see [references/linker-script-anatomy.md](references/linker-script-anatomy.md).
arm-none-eabi-readelf -S firmware.elf | grep -A2 ".data"

有关链接脚本结构的详细信息,请参阅[references/linker-script-anatomy.md](references/linker-script-anatomy.md)。

Related skills

相关技能

  • Use
    skills/embedded/openocd-jtag
    for flashing to addresses defined in linker script
  • Use
    skills/embedded/freertos
    for FreeRTOS heap placement in specific RAM regions
  • Use
    skills/binaries/linkers-lto
    for linker LTO and symbol flags
  • Use
    skills/binaries/elf-inspection
    to inspect section sizes and addresses
  • 使用
    skills/embedded/openocd-jtag
    将程序烧写到链接脚本定义的地址
  • 使用
    skills/embedded/freertos
    在特定RAM区域放置FreeRTOS堆
  • 使用
    skills/binaries/linkers-lto
    配置链接器LTO和符号标志
  • 使用
    skills/binaries/elf-inspection
    检查段大小和地址