---
title: リンカスクリプトと再配置
url: https://doc.liz6.com/ja/systems-programming/07-toolchain-and-compilation/02-linker-scripts-and-relocations
locale: ja
area: systems-programming
tags:
- systems-programming
- toolchain-and-compilation
date: 2026-06-30
modified: 2026-07-16
description: 'カバー範囲: ld スクリプト構文 → 再配置タイプ → GOT/PLT 再配置 → リンカリラクセーション → LLD vs GNU ld vs gold → R_AARCH64_ADR_GOT_PAGE 適用: binutils ld, LLD, 静的/動的リンク'
---

# リンカスクリプトと再配置

> カバー範囲: ld スクリプト構文 → 再配置タイプ → GOT/PLT 再配置 → リンカリラクセーション → LLD vs GNU ld vs gold → R_AARCH64_ADR_GOT_PAGE
> 適用: binutils ld, LLD, 静的/動的リンク

## 再配置: リンカの役割

```
コンパイル時 (gcc -c):
  .o ファイル内に RELA エントリが存在 → 「このアドレスに値を埋め込む必要がある。対象シンボルはこれだ」と記録
  例: R_X86_64_PLT32: この 4バイトのオフセットには、シンボルの PLT 番地を埋め込む必要がある

リンク時 (ld):
  全 .o の RELA を走査 → シンボルを解決 → 番地を埋め込む
  動的シンボルの場合 → .rela.plt を生成 (実行時に ld.so によって解決される)
```

## 一般的な再配置タイプ

```c
// x86-64 (AMD64):
R_X86_64_64:        直接の 64ビット絶対番地
R_X86_64_PC32:      32ビット PC 相対オフセット (call/jmp のターゲット)
R_X86_64_PLT32:     PC32 に類似するが、対象が PLT 内にある
R_X86_64_GOTPCREL:  GOT エントリの PC 相対オフセット
R_X86_64_COPY:      共有ライブラリ内の変数をメインプログラムの .bss にコピー

// ARM64 (AArch64):
R_AARCH64_ABS64:    絶対番地
R_AARCH64_ADR_PREL_PG_HI21:  PC 相対、ページ粒度 (ADR)
R_AARCH64_ADD_ABS_LO12_NC:   ページ内オフセット (ADD)
R_AARCH64_LDST64_ABS_LO12_NC: 読み込み/書き込み、ページ内オフセット
R_AARCH64_ADR_GOT_PAGE:       GOT エントリの PC 相対、ページ粒度 + 読み込み
```

## ld スクリプト: リンカスクリプト

```ld
/* デフォルトの ld スクリプト: /usr/lib/ldscripts/elf_x86_64.x */

SECTIONS
{
    /* 開始: コードセグメント (Read + Execute) */
    . = 0x400000 + SIZEOF_HEADERS;  /* PIE 以外 */
    .text : { *(.text.unlikely .text.*_unlikely .text.unlikely.*)
              *(.text.exit .text.exit.*)
              *(.text.startup .text.startup.*)
              *(.text.hot .text.hot.*)
              *(.text .stub .text.* .gnu.linkonce.t.*) }

    /* 読み取り専用データ */
    .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
    . = ALIGN(0x1000);
    __init_array_start = .;
    .init_array : { *(.init_array) }
    __init_array_end = .;

    /* 読み書き可能セグメント (次のページから開始) */
    . = ALIGN(0x1000);
    .data : { *(.data .data.* .gnu.linkonce.d.*) }
    .bss : { *(.bss .bss.* .gnu.linkonce.b.*) }
}
```

## LLD vs GNU ld vs gold

| | GNU ld (BFD) | gold | LLD (LLVM) |
|---|---|---|---|
| 速度 | ベンチマーク | 2〜3倍高速 | 3〜5倍高速 |
| メモリ | 高い | より低い | 低い |
| LTO | 対応 (GCC LTO) | 対応 (GCC LTO) | 対応 (ThinLTO) |
| リンカスクリプト | 完全対応 | 一部 | 大部分 |
| デフォルト | 伝統的 | 保守終了 | 将来標準 |

## リンカリラクセーション

```
特定の再配置は「リラクセーション」によって最適化できる:
  RISC-V / ARM64 でサポート:
    例: R_RISCV_CALL → リンク時にターゲットまでの距離が分かる → より短い命令列を使用
    → call (32ビットオフセット) → jal (20ビットオフセット) 距離が十分であれば
    → コードが短くなり + 高速化

  x86 はリラクセーションに対応していない (命令サイズが固定で、最適化の余地がない)
```

## デバッグ

```bash
# 再配置テーブルを表示
readelf -r file.o

# デフォルトの ld スクリプトを表示
ld --verbose | less

# リンクプロセスのトレース
ld -Map output.map -o prog *.o  # 詳細なマッピングファイルを出力
gcc -Wl,--trace -o prog *.o     # リンクされる各ファイルを表示

# 未定義シンボル
ld -o prog *.o 2>&1 | grep undefined
```

## 参考

- **ドキュメント**: `info ld`, ELF spec (System V ABI, Chapter 4)
- **ソースコード**: `binutils/ld/`, `lld/ELF/`
- **LWN**: "Linker relaxation"

*キーワード: relocation, R_X86_64_PLT32, R_AARCH64_ADR_GOT_PAGE, ld script, LLD, gold, linker relaxation*
