본문으로 건너뛰기
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.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

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의 스킬 모두 보기

이 스킬의 문제 신고