本文へ移動
cccskills
無料GitHub で公開

linker-scripts

Use when writing a GNU ld script for a bare-metal target, placing code in a flash or RAM region, wiring .data and .bss startup, or fixing a region overflowed error. Not for LTO: use linkers-lto.

インストール方法を見る

含まれるファイル(3)

  • SKILL.md7.7 KB
  • agents/openai.yaml232 B
  • references/linker-script-anatomy.md5.1 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Linker scripts

Contract

FieldBound contract
TriggerA bare-metal image needs a GNU ld script: MEMORY and SECTIONS, VMA versus LMA for .data, the symbols startup code copies between, a function placed in a named region, weak default handlers, or a region overflow error.
AuthorityReversible local: writes only the linker script and the startup source in the project directory the user names; rollback is reverting those files in version control. No remote mutation.
Side effectNew or edited .ld and startup files. The image layout changes on the next link.
DoneThe ELF links, readelf -S shows .data with an LMA inside flash and a VMA inside RAM, .bss and the stack fit the RAM region, and the startup code copies .data and zeroes .bss between the script's symbols before main.

Inputs

  • Flash and RAM origins and lengths from the datasheet, plus any extra region (CCM, TCM, backup SRAM).
  • The vector table section name the startup file emits (.isr_vector in the CMSIS templates).
  • Whether the C runtime is newlib (needs __libc_init_array and the .init_array sections) or none.
  • Functions or buffers that must live in a specific region.

Procedure

  1. Write MEMORY and the skeleton SECTIONS. ENTRY names the reset symbol; KEEP on the vector table stops --gc-sections from removing it; _estack at the top of RAM is where the startup file points the stack. Done when: the script has one MEMORY block with every region from the datasheet and the link succeeds with -T script.ld.

    ENTRY(Reset_Handler)
    
    MEMORY
    {
        FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
        RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
    }
    
    SECTIONS
    {
        .text :
        {
            KEEP(*(.isr_vector))
            *(.text .text.*)
            *(.rodata .rodata.*)
            . = ALIGN(4);
            _etext = .;
        } > FLASH
    
        .data :
        {
            . = ALIGN(4);
            _sdata = .;
            *(.data .data.*)
            . = ALIGN(4);
            _edata = .;
        } > RAM AT > FLASH
    
        _sidata = LOADADDR(.data);
    
        .bss :
        {
            _sbss = .;
            *(.bss .bss.*)
            *(COMMON)
            . = ALIGN(4);
            _ebss = .;
        } > RAM
    
        _estack = ORIGIN(RAM) + LENGTH(RAM);
    }
    
  2. Separate VMA from LMA for .data. The VMA is the address the code runs at (RAM); the LMA is where the bytes sit in the image (flash). > RAM AT > FLASH sets both; AT(_etext) after the section name is the equivalent explicit form. LOADADDR(.data) gives startup code the flash copy's address. Done when: arm-none-eabi-readelf -S firmware.elf shows .data with Addr in RAM and arm-none-eabi-objdump -h shows its LMA in flash.

  3. Copy .data and zero .bss in the reset handler, between the script's symbols. With newlib, call __libc_init_array() before main so C++ constructors and .init_array entries run. Done when: a global initialized to a nonzero value reads that value in main, and a zero-initialized global reads zero.

    extern uint32_t _sdata, _edata, _sidata, _sbss, _ebss;
    
    void Reset_Handler(void) {
        uint32_t *src = &_sidata, *dst = &_sdata;
        while (dst < &_edata) *dst++ = *src++;
        for (dst = &_sbss; dst < &_ebss; ) *dst++ = 0;
        __libc_init_array();
        main();
        for (;;);
    }
    

    The startup file itself is the subject of baremetal-startup; this skill owns the symbols it consumes.

  4. Place code or data in a named region. Give the input section a name with __attribute__((section(".name"))), collect it into an output section with > REGION AT > FLASH, and copy it at startup like .data when the region is RAM. Done when: objdump -h shows the section in the intended region and the copy loop covers it.

    MEMORY
    {
        FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
        RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
        CCM   (rwx) : ORIGIN = 0x10000000, LENGTH = 64K
    }
    
    .fast_code :
    {
        _sfast = .;
        *(.fast_code .fast_code.*)
        _efast = .;
    } > CCM AT > FLASH
    _sifast = LOADADDR(.fast_code);
    
    __attribute__((section(".fast_code"))) void critical_isr_handler(void) { }
    

    CCM on STM32F4 is not on the DMA bus; place DMA buffers in SRAM.

  5. Use KEEP, ALIGN, PROVIDE, and the fill expression where each applies. KEEP protects sections --gc-sections would drop (vector table, .init_array). ALIGN(8) before the stack satisfies the AAPCS 8-byte stack alignment. PROVIDE defines a symbol only if no object defines it, which makes it the default a program can override. The =0xFF fill after an output section writes the erased-flash value into gaps. Done when: every KEEP guards a section the code never references directly, and the stack region starts 8-byte aligned.

    PROVIDE(_stack_size = 0x400);
    
    .stack (NOLOAD) :
    {
        . = ALIGN(8);
        . += _stack_size;
        _stack_top = .;
    } > RAM
    
    .text : { *(.text .text.*) . = ALIGN(4); } > FLASH = 0xFF
    
  6. Give every exception a weak default handler. PROVIDE(NMI_Handler = Default_Handler) in the script, or __attribute__((weak)) in C, binds the vector to a spin loop until the application defines the real symbol. Done when: a firmware with no SysTick_Handler links, and one that defines it calls the definition.

    PROVIDE(NMI_Handler       = Default_Handler);
    PROVIDE(HardFault_Handler = Default_Handler);
    PROVIDE(SysTick_Handler   = Default_Handler);
    
    __attribute__((weak)) void Default_Handler(void) { for (;;); }
    void SysTick_Handler(void) { tick_count++; }   /* overrides the default */
    
  7. Check sizes and addresses after every layout change. --print-memory-usage (pass as -Wl,--print-memory-usage) prints region use at link time; -Wl,-Map=firmware.map writes the symbol map for the big-symbol hunt. Done when: every region is under 100 percent and .data's LMA is inside flash.

    arm-none-eabi-size -A firmware.elf
    arm-none-eabi-objdump -h firmware.elf
    arm-none-eabi-readelf -S firmware.elf
    

    references/linker-script-anatomy.md has a complete STM32F407 script with the ARM unwind and .init_array sections and the table of built-in functions.

Failure and recovery

ErrorCauseFix
region `FLASH' overflowed by N bytesImage larger than flash-Os, -ffunction-sections -fdata-sections with -Wl,--gc-sections, LTO; find the big symbols in the map file.
region `RAM' overflowed.data, .bss, heap, and stack exceed RAMShrink buffers, cut _stack_size or the heap, move constants to const so they land in .rodata.
undefined reference to '_estack'Startup expects a symbol the script does not defineDefine _estack = ORIGIN(RAM) + LENGTH(RAM);.
.data reads garbage at bootLMA equals VMA, or startup copies the wrong rangeAdd AT > FLASH; copy from LOADADDR(.data) to _sdata up to _edata.
Vector table missing from the image--gc-sections removed itWrap it in KEEP.
cannot open linker script fileWrong pathPass -T path/to/script.ld or add -L dir.

Output

A linker script and startup symbols in the named directory that link the image into the datasheet's regions, with readelf -S and objdump -h output confirming .data LMA in flash and VMA in RAM and every region within its length.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use when explaining System V AMD64, ARM AAPCS, RISC-V psABI, stack frames, variadic calls, or FFI register rules. Not for the Rust FFI binding layer: use rust-ffi.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

Use when configuring ADC sampling time, DMA-driven ADC, calibration, or DAC channel setup on bare-metal MCUs. Not for the DMA stream itself: use dma-baremetal.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

af-xdp

無料

Use when creating AF_XDP sockets, configuring UMEM and XSK rings, writing an XDP redirect program, or choosing copy versus zero-copy mode. Not for full kernel bypass: use dpdk.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

Use when a completed session needs an agent-environment retrospective. Not for an engineering retrospective from telemetry: use engineering-retrospective.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

Use when a redacted, trimmed agent transcript must be appended to a GitHub PR or issue body, with human approval and preview. Not for automated or model-initiated insertion.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

agents-md

無料

Use when a repo needs agent setup, AGENTS.md added or made lean, CLAUDE.md audited, or agent instructions scored or pruned. Not for remote, credential, publish, deploy, or irreversible changes.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

OutlineDriven のスキルをすべて見る

このスキルの問題を報告する