プログラムは、ソースコードを書いて保存すればすぐ動くわけではありません。
コンパイル、リンク、ロードという段階を順に通り抜けてはじめて実行に到達します。
本記事では、特に混同しやすいリンカとローダの違いと役割を、コンパイルから実行までの流れに沿ってやさしく解説します。
コンパイルから実行までの流れ
4ステップ
ステップ1: コンパイルでオブジェクトファイルを作る
ソースコード(例: main.c)は、コンパイラによって機械語に近い形のオブジェクトファイル(main.o)に変換されます。
ここでは他ファイルの関数やライブラリ関数はまだ「名前だけ参照」しており、中身は未解決のままです。
コンパイルは各ソースを個別に「機械語の部品」にする作業です。
ステップ2: リンクで実行ファイルを作る
複数のオブジェクトファイルやライブラリをまとめ、未解決の参照(関数や変数)を結びつけて、1つの実行ファイル(例: a.out、app.exe)を作ります。
未解決の名前と実体を結ぶのがリンカの役目です。
ステップ3: ロードでメモリに配置する
実行ファイルを起動すると、ローダがその中身をメモリに読み込み、必要な共有ライブラリ(例: libm.so、user32.dll)も探して読み込み、アドレスを調整します。
ローダは「ファイル」を「動くプログラム(プロセス)」に変える役目です。
ステップ4: 実行開始
準備が整うと、プログラムの入口(例: main関数)に処理が移り、本体のコードが走り出します。
開発者の書いたロジックがここで初めて動きます。
ファイルの違い
主なファイル拡張子と役割の早見表
ソースから実行までの各段階で扱うファイルの種類と役割をまとめます。
| 拡張子・種類 | できるタイミング | 誰が作る | 主な中身 | 次の段階 |
|---|---|---|---|---|
| .c/.cpp (ソース) | 最初 | 人間(開発者) | テキストのコード | コンパイル |
| .o/.obj (オブジェクト) | コンパイル後 | コンパイラ | 機械語の断片、未解決参照 | リンク |
| .a/.lib (静的ライブラリ) | 共有部品化 | アーカイバ等 | 複数のオブジェクトの束 | リンク |
| .so/.dll/.dylib (共有ライブラリ) | 再利用部品 | コンパイラ+リンカ | 実行時に共有されるコード | ロード |
| 実行ファイル(a.out/.exe等) | リンク後 | リンカ | 実行可能なまとまり | ロードと実行 |
コンパイルは「.c→.o」、リンクは「.o等→実行ファイル」、ロードは「実行ファイル→メモリ上のプロセス」と覚えると理解が定着します。
1ファイルがどう変わるかの例
たとえば main.c を gcc -c main.c で main.o にし、gcc main.o -o app で app を作ります。
./app と実行するとローダが働き、メモリに展開されて動き出します。
実行時にローダが共有ライブラリも自動で読み込む点がポイントです。
リンカの役割
すること
参照(シンボル)の解決
オブジェクト同士が呼び合う関数や変数の「名前」と「実体」を結びつけます。
未解決の参照をすべて解決できたとき、実行ファイルが完成します。
配置と結合
複数のオブジェクトやライブラリのコードやデータを1つにまとめ、必要な場所に並べ替えます。
アドレスの調整や不要部分の除去(デッドコード除去)も行われます。
実行ファイルやライブラリの生成
最終的に実行ファイルや共有ライブラリを作ります。
リンカは成果物を作る「ビルドの仕上げ担当」です。
入出力
入力にあたるもの
- 複数のオブジェクトファイル(.o/.obj)
- 静的ライブラリ(.a/.lib)
- 動的にリンクする共有ライブラリ(.so/.dllなど)の参照情報
リンカは「部品」を集めて完成品に組み上げる道具だと捉えると理解しやすいです。
出力にあたるもの
- 実行ファイル(a.out、.exeなど)
- 共有ライブラリ(.so/.dll/.dylib)
- 必要ならマップファイル(配置情報のレポート)
エラー例
参照未解決の例
代表例は undefined reference to 'foo' です。
これは、コードが foo() を呼んでいるのに、その実体をリンカが見つけられなかったことを示します。
足りないオブジェクトやライブラリ、または指定順の誤りが原因になりがちです。
重複定義の例
multiple definition of 'bar' は、同じシンボルを複数のオブジェクトやライブラリで定義してしまったときに出ます。
グローバル変数の二重定義や、同名関数の重複が原因です。
ライブラリ順序の例
特に一部のリンカはライブラリの指定順に敏感です。
gcc main.o -lfoo -lbar の順を誤ると undefined reference が出る場合があります。
必要なものを後に書くと解決しやすい、などの作法が存在します。
静的リンクと動的リンク
概要
静的リンクは必要なコードを実行ファイルに取り込む方式、動的リンクは共有ライブラリを実行時に読み込む方式です。
| 項目 | 静的リンク | 動的リンク |
|---|---|---|
| 容量 | 大きくなりやすい | 小さくなりやすい |
| 配布 | 単体で配りやすい | 依存ライブラリの配布が必要 |
| 起動 | 速い傾向 | 初回ロードでわずかに時間 |
| 更新 | 再ビルドが必要 | ライブラリ差し替えで更新可能 |
| 互換性 | 影響少ない | ライブラリの互換性に注意 |
初心者はまず動的リンクの環境に慣れ、必要に応じて静的リンクを選ぶ、という順序が扱いやすいです。
ローダの役割
すること
実行ファイルの読み込みと初期化
ローダは実行ファイルをメモリに読み込み、必要なメモリ領域を確保し、開始準備を行います。
ファイルを「プロセス」に変換するのがローダのコア機能です。
共有ライブラリの解決と読み込み
実行ファイルが依存する共有ライブラリを見つけ、メモリに読み込んで結び付けます。
遅延読み込み(使われた時に読み込む)を行う場合もあります。
アドレスの調整
実際に読み込んだ場所に合わせて、参照先のアドレスを調整します。
この調整により、ライブラリがどのアドレスに置かれても正しく呼び出せます。
入出力
入力にあたるもの
- 実行ファイル本体
- 実行ファイルが参照する共有ライブラリ(.so/.dllなど)
- システムの設定や検索パス
出力にあたるもの
- メモリ上で動作可能なプロセス
- 初期化済みのスタック、ヒープ、ライブラリの初期化状態
ローダは「メモリ上の完成形」を出力する、と考えるとイメージしやすいです。
共有ライブラリの読み込み
どこを探すか
ローダはOSごとの検索パスを順に探します。
たとえばLinuxではシステムディレクトリや LD_LIBRARY_PATH、Windowsでは PATH 上のディレクトリなどです。
ライブラリが見つからないと起動自体が止まります。
よくあるエラー
- Linux系:
error while loading shared libraries: libX.so: cannot open shared object file - Windows:
The program can't start because X.dll is missing from your computer
「見つからない」「互換性がない」などはローダ段階の典型的なトラブルです。
リンカとローダの違い
動くタイミング
ビルド時に動くのがリンカ
リンカはビルド中に動いて、実行ファイルやライブラリを作ります。
実行時に動くのがローダ
ローダはプログラムを起動したときに動き、ファイルをメモリに展開します。
扱う対象
リンカが扱うもの
オブジェクトファイルとライブラリという「ファイル」を材料にし、参照を解決して1つの成果物にまとめます。
素材の統合作業が中心です。
ローダが扱うもの
実行ファイルと共有ライブラリを入力に、メモリ上で動く「プロセス」を用意します。
実行のための準備作業が中心です。
目的の違い
リンカの目的は「完成品の生成」、ローダの目的は「完成品を動かすこと」です。
似ているようで、前者はビルド工程、後者は起動工程を担います。
エラーが出る場面
ビルド中に出るエラー(リンカ)
undefined reference、multiple definition など。
足りないライブラリ、順序、重複定義に注意します。
実行時に出るエラー(ローダ)
X.dll is missing や cannot open shared object file など。
検索パス、インストール漏れ、バージョン不一致を疑います。
ビルドは成功しても起動で失敗する、という分かれ目がここです。
まとめ
リンカはビルド時に部品(オブジェクトやライブラリ)を結び付けて実行ファイルを作り、ローダは実行時にそれらをメモリへ配置して動かします。
コンパイルから実行までを4ステップ(コンパイル→リンク→ロード→実行)で捉えると、どこで何が起きるかが明確になります。
初心者の方は、エラーが出たときに「いまはビルド中のリンカか、起動時のローダか」を切り分ける癖をつけると、原因にたどり着きやすくなります。
リンカは完成品づくり、ローダは起動のお世話という役割の違いを押さえ、安心してプログラムを動かしていきましょう。
