リンクとは? 最終的なバイナリを組み立てるステップ
リンクは、コンパイルされたオブジェクトファイルとライブラリを受け取り、それらの間のすべての参照を解決して、最終的な実行可能プログラムを生成するbuildフェーズです。
コンパイルとリンクは2つの異なるステップです。コンパイルは各ソースファイルを機械語に変換し、他の場所で参照するものにはプレースホルダを残します。リンクはそれらのプレースホルダを埋め、すべての参照をその定義に接続し、部品を組み合わせて完全な実行可能ファイルやライブラリにします。それは、別々のコンパイル単位が1つのプログラムになる瞬間です。
リンクが位置づけられる場所
典型的なbuildは各ソースファイルを個別にコンパイルし、その後で結果をリンクします。コンパイルはファイル単位のエラーを捉え、リンクはファイル間のエラーを捉えます。リンクが成功して初めて、実際に実行できるものが手に入ります。
リンクが解決するもの
- 他のファイルやライブラリ内の定義への関数呼び出し。
- グローバル変数や共有データへの参照。
- 静的または動的なライブラリ依存関係。
- プログラムの最終的なメモリレイアウトとエントリポイント。
静的リンク vs 動的リンク
静的リンクはbuild時にライブラリのコードを実行可能ファイルに焼き込み、より大きいが自己完結したバイナリを生成します。動的リンクは共有ライブラリへの参照をロード時に解決するよう残し、より小さくライブラリを共有するがそれらが存在することに依存するバイナリを生み出します。
リンクが遅くなる理由
大規模なプログラムでは、リンクは特に多数のオブジェクトファイルと大きなライブラリがある場合、コンパイルに匹敵するかそれを上回る時間を要することがあります。またメモリを大量に消費するため、制約のあるマシンではリンクのステップがbuildを支配したり停止させたりすることがあります。
簡単な例
gcc a.o b.o -o app を実行するのがリンクのステップです。これは、既にコンパイルされた2つのオブジェクトファイルを、それらの間の参照を解決しながら、app という名前の1つの実行可能ファイルに結合します。
CIにおけるリンク
リンクは重く、メモリを大量に使うbuildフェーズであり、大きなリンクステップは小さいrunnerでは遅くなったりOOMで強制終了されたりします。より大きなrunner(Latchkey)はリンカにより多くのメモリを与えてbuildを高速化し、リンク中の一時的なOOMやツールチェーンの障害は自動でretryできます。
重要なポイント
- リンクは、コンパイルされた単位をまたぐ参照を解決し、実行可能なプログラムを生成します。
- コンパイルとは別物で、コンパイルでは捉えられないファイル間のエラーを捉えます。
- リンクはメモリを大量に使い、小さいrunnerでは遅くなったりOOMで強制終了されたりします。