mcpField
L
linker-scripts

linker-scripts

github.com/mohitmishra786开发269537 次安装18 周前更新
代码仓库

关于 linker-scripts

为处理嵌入式目标的 Agent 提供 GNU ld 链接脚本编写与修改指导,涵盖内存布局、段放置、启动初始化和常见错误排查。

主要功能

  1. 01配置 MEMORY 与 SECTIONS
  2. 02说明 VMA 与 LMA 的区别
  3. 03处理 .data 与 .bss 启动初始化
  4. 04将代码段放入指定内存区域
  5. 05使用 KEEP、ALIGN、PROVIDE
  6. 06排查链接器错误

适用场景

  1. 01为 MCU 编写链接脚本
  2. 02将关键函数放入 RAM
  3. 03诊断 FLASH 或 RAM 溢出
  4. 04设置弱符号与启动段

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill linker-scripts

安装后在 Claude Code 中输入/linker-scripts确认已加载。

附带文件references/linker-script-anatomy.md

详细介绍

链接器脚本

用途

指导 Agent 为嵌入式目标编写和修改 GNU ld 链接器脚本:MEMORY 和 SECTIONS 命令、代码重定位中的 VMA 与 LMA、启动时初始化 .bss/.data、将节放置到特定区域,以及使用 PROVIDE/KEEP/ALIGN 指令。

触发条件

  • “如何为我的 MCU 编写链接器脚本?”
  • “如何将函数放置在特定的闪存/RAM 区域?”
  • “链接器脚本中的 VMA 和 LMA 有什么区别?”
  • “启动时如何初始化 .bss 和 .data?”
  • “链接错误:区域 'FLASH' 溢出”
  • “如何在链接器脚本中使用弱符号?”

工作流

1. 链接器脚本结构
/* 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 与 LMA
  • VMA(虚拟内存地址):节在运行时执行的位置
  • LMA(加载内存地址):节在映像中存储的位置(闪存)

对于 .data:存储在闪存中(LMA),启动时复制到 RAM(VMA)。

/* AT() sets LMA explicitly */
.data : AT(ADDR(.text) + SIZEOF(.text))
{
    _sdata = .;
    *(.data)
    _edata = .;
} > RAM          /* VMA goes in RAM */

如果使用 AT(_etext),.data 的 LMA 会自动放置在 .text 之后。

3. 启动时初始化 .bss / .data

C 运行时必须在 main() 之前将 .data 从闪存复制到 RAM,并将 .bss 清零:

// 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):

.data : AT(_etext)
{
    _sdata = .;
    *(.data)
    _edata = .;
} > RAM

_sidata = LOADADDR(.data);    /* LMA of .data for startup code */
4. 将代码放置在特定区域
/* 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 */
// 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
/* 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. 弱符号
/* In linker script — provide weak default ISR */
PROVIDE(NMI_Handler        = Default_Handler);
PROVIDE(HardFault_Handler  = Default_Handler);
PROVIDE(SysTick_Handler    = Default_Handler);
// 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. 常见链接器错误
错误原因解决方法
region 'FLASH' overflowed二进制文件过大,无法放入闪存启用 LTO、使用 -Os、移除未使用的代码;使用 --gc-sections
region 'RAM' overflowedRAM 使用量过大减小栈大小,使用静态缓冲区,检查 .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 地址错误未设置 LMA在 .data 节定义之后添加 AT(_etext)
# Analyze section sizes
arm-none-eabi-size firmware.elf
arm-none-eabi-size -A firmware.elf    # verbose per-section

# Show all sections and their addresses
arm-none-eabi-objdump -h firmware.elf

# Check if .data LMA is in flash range
arm-none-eabi-readelf -S firmware.elf | grep -A2 "\.data"

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

相关技能

  • 使用 skills/embedded/openocd-jtag,将固件烧录到链接器脚本定义的地址
  • 使用 skills/embedded/freertos,将 FreeRTOS 堆放置在特定 RAM 区域
  • 使用 skills/binaries/linkers-lto,了解链接器 LTO 和符号标志
  • 使用 skills/binaries/elf-inspection,检查节的大小和地址

评论

支持 Markdown