この記事は、アセンブラの進化 からの続編です。
この記事は、絵でわかる プログラムとは何か(3)~リンクで進化した言語処理~ の一記事です。
この記事のポイント
- モジュールを集めたファイルをライブラリと言う。
- ソースコードをコンパイルすると、オブジェクトファイルが出力される。
- オブジェクトファイルの中身は、内容に応じて、セクション分けされている。
- オブジェクトファイルの冒頭には、セクション情報などが入ったヘッダがある。
- コンパイラは、コンパイル時に、シンボルテーブルへシンボル名を仮登録する。
- リンカは、複数オブジェクトファイルの各セクションをリロケートし、1つの実行ファイルを作成する。
- シンボルテーブルへ仮登録されたシンボルの、実際にリンクには、静的リンクと動的リンクがある。
ライブラリ
モジュールのストック
アセンブリ言語のマクロもその一例でしたが、高級言語でも当然、プログラムの共通部分はたくさん出てきます。そうしてモジュールのストックは、どんどん増えていきました。
モジュールの数が増えてくると、それらのモジュールを別個に集めてくるよりも、関連性のある複数のモジュールをあらかじめ束ねておいた方が便利になります。
そこで、関連性のある複数のモジュールを集め、それを様々なプログラム間で、共通して使えるように1つのファイルにまとめたものがライブラリです。
様々なプログラムとは言っても、それらのソースコードは通常、ライブラリの中身と同じプログラミング言語である必要があります。
ただし、DLL(Dynamic Link Library)のように、ソースコードのプログラミング言語を問わない、ネイティブコードでできた特殊なライブラリもあります。
ライブラリの利用
ライブラリは、最初からOSと一緒にコンピューターに入っている場合もありますが、必要なライブラリがコンピューターに入っていない場合は、そのコンピューターの外部からライブラリファイルを持ってきて(ネットからダウンロードなどして)、コンピューターに入れておきます。
ライブラリを利用するプログラムでは、通常、以下のように2段階で記述します。
- ソースコードの冒頭に、どのライブラリを利用するかを記述。
- モジュールの処理が必要な行に、そのモジュール名を記述。
こうすることで、ライブラリの記述は冒頭に一回だけであっても、モジュールはプログラム中に何度も使うことができるようになります。
別ファイルへのリンク
実行ファイル形式
ライブラリの利用や、大規模プログラムでのモジュール分割など、複数ファイルからなるプログラムは、ファイルをまたいで結合し、最終的には実行ファイル(executable)という一つのマシンコードファイルになるように変換する必要があります。
実行ファイルが、正しく安全に効率良く動くようにするためには、OSとの連携が欠かせません。
そこで、OSがプログラムの構造に合わせて適切にプロセッサやメモリを制御できるよう、セクションとヘッダという2つの工夫が、実行ファイルに付け加えられました。
セクション
プログラムの中身を整理分類(セクション分け)して、OSがセクション毎に扱いを変えられるようにします。
セクションの例)
- テキストセクション = マシンコードになったプログラムの集まり
- データセクション = 初期値が入った変数の集まり
- BSSセクション = 初期値の無い変数が入る予定のセクション(空)
ヘッダ
実行ファイルの冒頭(ヘッダ)に、セクション情報やプログラムの情報など、OSとの連携に必要な情報を入れておきます。
これらの工夫は、プログラムを実行するプラットフォームに合わせてその形式(フォーマット)が決められています。これを実行ファイル形式(実行ファイルフォーマット)と言います。
実行ファイル形式の例:
- a.out (assembler output)・・・初期のUNIXプラットフォーム
- COFF (Common Object File Format)・・・1980年代UNIXプラットフォーム
- PE (Portable Executable)・・・Windowsプラットフォーム
- ELF (Executable and Linkable Format) ・・・2000年代以降のUNIXプラットフォーム など
オブジェクトファイル
コンパイラで1つのソースコードファイルをコンパイルすると、そのソースコードに対応した実行ファイルとして、オブジェクトファイルが1つ生成されます。
1つのプログラムが、複数のソースコードファイルに分割されている場合は、各ソースコードファイルごとにコンパイルします。(分割コンパイル)
そしてコンパイラは、それぞれのソースコードファイルに対応したオブジェクトファイルを生成します。(ソースコードファイルの数だけオブジェクトファイルが生成されます。)
それら複数のオブジェクトファイルは、あとで合体させ、各ファイルに分散した同種セクションを再配置(リロケート)して、ヘッダも書き換え、1つの実行ファイル(実行ファイル形式)にすることで、1つのプログラムとして動くようになります。
シンボルテーブルへの仮登録
ソースコードではリンクとして書かれていた部分も、プロセッサが実行ファイルを順次処理する時には、オペランドとして実際のリンク先アドレスへの参照が入っていなければなりません。
プロセッサが処理できるマシンコードは、単なるオペコードとオペランドの繰り返しだけだったからです。
しかし、リロケート前のコンパイラでは、実行ファイルで各リンク先の開始アドレスがどこになるかは、まだわかりません。
コンパイラが作成した、オブジェクトファイル内のリンクは、いったん、未結合のリンク(シンボルテーブルへのシンボル仮登録まで)になります。
シンボル
なお、シンボルとは参照の識別に使われる名前のことです。例えば、モジュール名や変数名などのことをシンボル名と言います。シンボルテーブルは、シンボルとそのメモリアドレスとの対応表です。コンパイラが行うシンボルの仮登録では、メモリアドレス無しでシンボル名だけをシンボルテーブルに登録しておきます。
変数については、プログラムとは何か(6)~シンボル編~で詳しく説明します。
リンカ
そして、必要な全てのオブジェクトファイルが揃ったところで、リンカ(linker)というまた別のプログラムによって、各セクションをリロケート(再配置)し、1つの実行ファイルを生成します。
全セクションのリロケーション(再配置)が完了してようやく、各ファイルに入っていたシンボル実体の開始アドレス(リンク先)が定まります。
シンボルのリンク
その後の、シンボル名(リンク元)からシンボル実体の開始アドレス(リンク先)への変換には、次の2つの方式があります。
- 静的リンク
- 全てのファイルのコンパイルが完了した後で、リンカが、各オブジェクトファイルをリロケートした上で結合し、1ファイルのマシンコード(実行ファイル)にまとめる方式。コンパイルからオブジェクトファイルの結合までを合わせてビルドとも言います。
- 動的リンク
- コンパイラで各ファイルをコンパイルしてオブジェクトファイルにだけしておき、シンボルテーブルにシンボルを仮登録します。本体オブジェクトファイルを実行ファイルとし、メモリにロードして実行します。そして必要になったモジュールだけを、OS(カーネル)が順次追加ロードし、シンボルテーブルにそのアドレスを登録することでリンク結合が完了する方式。WindowsのDLL(Dynamic Link Library)は、この方式でリンクできるライブラリになっています。
なお、静的(static)/動的(dynamic)という言葉は、メモリに記録されるものが、プログラム実行前から実行中もずっと変動しない場合に静的、プログラム実行中に変動し得る場合に動的、として使い分けられています。
この記事のまとめ
プログラムの実行ファイルは、複数のモジュールから構成されたり、複数のファイルを結合して構成されたりする場合があります。特に、モジュールだけを集めたファイルをライブラリと言います。
コンパイラは、ソースコードをコンパイルし、オブジェクトファイルを出力します。オブジェクトファイルの中身は、内容に応じて、セクション分けされており、オブジェクトファイルの冒頭には、セクション情報などが入ったヘッダが付けられます。コンパイラは、コンパイル時に、シンボルテーブルへシンボル名を仮登録します。
リンカは、複数オブジェクトファイルの各セクションをリロケートし、1つの実行ファイルを作成します。こうして初めて、シンボルテーブルに仮登録されたシンボルに対して、アドレスが確定します。(リンク)
シンボルテーブルへ仮登録されたシンボルの、実際のリンクには、プログラム実行前にリンクする静的リンクと、プログラム実行後にリンクする動的リンクがあります。
次は、言語処理系 に進みましょう。
この記事は、絵でわかる プログラムとは何か(3)~リンクで進化した言語処理~ の一記事です。
コンピューターのしくみ全体を理解したい場合は、以下の2コースがお勧めです。
日本全国 オンラインレッスン にも対応しています。
知りたいことだけ単発で聞きたい場合は、 オンラインサポート をご利用ください。