Mímisbrunnr知恵の泉

← コンピュータ基礎 一覧

🎓 レベル:発展 | 重要度:B(推奨)

📎 前提:プロセスとスレッド物理メモリと論理アドレス | 関連:仮想記憶とページングCPUと命令実行

要点(BLUF)

概念 ── 「a+b」から動くプロセスまでの全工程

CPUと命令実行 で1行が複数命令になるのを見ました。では、複数のソースファイルやライブラリ呼び出しを含むプログラム全体は、どうやって1つの動くプロセスになるのか。工程は次の通りです。

flowchart LR
    src["ソース(main.c, util.c)"] -->|"コンパイル+アセンブル"| obj["オブジェクト(main.o, util.o)"]
    obj -->|"リンク(シンボル解決+再配置)"| exe["実行ファイル(ELF)"]
    lib["ライブラリ(libc など)"] --> exe
    exe -->|"exec → ローダ"| proc["プロセス(アドレス空間に展開)"]

仕組み① ── リンクがやること(シンボル解決と再配置)

オブジェクトファイル main.outil.o の関数 helper() を呼ぶとき、main.o の中では「helper のアドレスは未定(穴)」になっています。リンカは全オブジェクトを見渡して、

  1. シンボル解決helper がどこで定義されているか(util.o 内)を突き止め、対応づける。
  2. 再配置:各オブジェクトを最終的なアドレス配置に並べ、穴に正しい参照を書き込む。

同名シンボルが2つあれば「重複定義」、どこにもなければ「未定義参照」エラー。C/C++のリンクエラーの大半はこの段階です。

仕組み② ── 静的リンク vs 動的リンク

ライブラリ(printf などを含む標準Cライブラリ等)の取り込み方に2通りあります。

flowchart TB
    subgraph S["静的リンク"]
      e1["実行ファイル(ライブラリのコードを丸ごと内包)"]
    end
    subgraph D["動的リンク"]
      e2["実行ファイル(参照だけ持つ)"] -.->|"実行時に解決"| so["共有ライブラリ(libc.so / 1コピーを全プロセスで共有)"]
    end
静的リンク動的リンク(共有ライブラリ)
ライブラリの場所実行ファイルに埋め込み別ファイル(.so/.dll)を実行時に読み込み
実行ファイルサイズ大きい小さい
メモリプロセスごとに重複1コピーを全プロセスで共有(仮想記憶とページング のページ共有)
ライブラリ更新再リンクが必要差し替えるだけで全アプリに反映
起動速い(解決済み)起動時にシンボル解決の手間

動的リンクが現代の主流なのは、同じライブラリのコードページを全プロセスで物理的に1つだけ持てば済むセグメンテーションとメモリ保護 の読み取り専用ページ共有)から。何百のプロセスが libc を使っても、物理メモリ上のコピーは1つです。

仕組み③ ── ELFとローダ

Linuxの実行ファイル形式が ELF(Executable and Linkable Format)。中身は「セクション/セグメント」に分かれ、.text(機械語=CPUと命令実行 のテキスト領域)、.data(初期値ありの大域変数)、.bss(初期値0の大域変数・ファイルには領域だけ確保)などが並びます。

ローダは exec の延長で、ELFのプログラムヘッダを見て「このセグメントを仮想アドレスのここへ、この権限(R/W/X)で」マップします。実際にはデマンドページング仮想記憶とページング)で、最初は写像だけ設定し、実行が触れたページを順次ディスクから読み込みます。だから巨大な実行ファイルでも起動は速い。

flowchart LR
    elf[".text / .data / .bss(ELFのセクション)"] -->|"ローダがマップ"| vm["プロセスのアドレス空間(テキスト/データ/BSS領域)"]
    vm -->|"動的リンカが .so を解決"| run["main から実行開始"]

動的リンクの場合、ローダは続いて動的リンカld.so)を起動し、必要な共有ライブラリをアドレス空間にマップしてシンボルを解決してから、main へ制御を渡します。

具体例 ── 実機で覗く

Linuxでは(対応ラボ):

これらで「実行ファイルは単なる機械語の塊ではなく、配置とリンクの設計図」だと体感できます。

仕組みの直観 ── なぜこの設計か

⚠️ よくある誤解・落とし穴

対応ラボ

cs-foundations-study/labs/03-05_linking.md(Linux: gcc で静的/動的リンクを作り分け、lddnmreadelf -Ssize で依存とセクションを観察する手順)。

関連

第3章 メモリ管理 目次