linker-scripts について
組み込みターゲット向けに GNU ld リンカースクリプトを作成・変更するエージェント用スキルです。メモリ配置、セクション配置、起動時の初期化、一般的なリンクエラーを扱います。
主な機能
- 01MEMORY と SECTIONS の設定
- 02VMA と LMA の違いの説明
- 03.data と .bss の起動時初期化
- 04特定のメモリ領域へのコード配置
- 05KEEP、ALIGN、PROVIDE の使用
- 06リンカーエラーの診断
活用シーン
- 01MCU 用リンカースクリプトの作成
- 02重要な関数の RAM 配置
- 03FLASH または RAM の容量超過の調査
- 04弱シンボルと起動セクションの設定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill linker-scriptsインストール後、Claude Code で/linker-scriptsを入力して読み込まれたか確認します。
同梱ファイルreferences/linker-script-anatomy.md
詳細
リンカースクリプト
目的
組み込みターゲット向けの GNU ld リンカースクリプトの作成と変更をエージェントに案内します。MEMORY コマンドと SECTIONS コマンド、コード再配置における VMA と LMA の違い、起動時の .bss/.data 初期化、特定の領域へのセクション配置、PROVIDE/KEEP/ALIGN ディレクティブの使用方法を扱います。
トリガー
- 「MCU 用のリンカースクリプトはどう書けばよいですか?」
- 「特定のフラッシュ/RAM 領域に関数を配置するにはどうすればよいですか?」
- 「リンカースクリプトにおける VMA と LMA の違いは何ですか?」
- 「起動時の .bss と .data の初期化はどのように行われますか?」
- 「リンカーエラー:region 'FLASH' overflowed」
- 「リンカースクリプトで弱いシンボルを使うにはどうすればよいですか?」
ワークフロー
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' 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 のアドレスが間違っています | 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を使用します - 特定の RAM 領域への FreeRTOS ヒープ配置には
skills/embedded/freertosを使用します - リンカーの LTO とシンボルフラグには
skills/binaries/linkers-ltoを使用します - セクションのサイズとアドレスの確認には
skills/binaries/elf-inspectionを使用します
コメント
Markdown 対応