気の向くままに辿るIT/ICT/IoT
UNIX/Linux

UNIX/Linux C言語コンパイルの過程

ホーム前へ次へ
コンパイルってどうなってるの?

UNIX/Linux コンパイルの過程/C言語

UNIX/Linux Cコンパイル過程

 コンパイラとコンパイルではUNIX/Linux及びシェル環境におけるコンパイラとコンパイルについて触れ、その中でプリプロセス、アセンブリコード生成、アセンブル、リンクというフレーズが出てきました。

 ここでは特に断りがない限り、Bourne Shell系のsh/bashgccによるC言語のコンパイルについて取り上げますが、プリプロセス、アセンブリコード生成、アセンブル、リンクというのは、まさにコンパイルという作業の中の個々の工程のことです。

プリプロセス/アセンブリコード生成/アセンブル/リンク

 C言語のソースファイルであるボディファイル(拡張子.c)をコンパイルして実行ファイルを生成すると

 プリプロセス済みソースファイル(拡張子.i) 

 アセンブリファイル(拡張子.s) 

 オブジェクトファイル(拡張子.o) 

 実行ファイル 

が生成されますが意図的にオプションを指定していないと消えてなくなり実行ファイルだけが残ります。

 コンピュータの元の世界は二進数の0と1の並びで成り立ち、何かをする際の命令文をハードウェアとしてのコンピュータに送るにもコンピュータが理解する形式として最低限、機械(マシン)語というもので記述するか、マシン語に翻訳しなければなりません(尤も更に遡ると機械語さえなく、人手によって組み版が行われていたと云われていますが)。

 しかし、機械語は多くの人が覚えられるようなレベルのものではないので翻訳する方法を考える方が得策だとして、その結果アセンブリ言語ができ、最初のUNIXもアセンブリベースでしたが、UNIX開発者の1人デニス・リッチー氏がUNIXの他への移植性を高めるため、より人間に理解しやすい言語として開発したのがC言語で(その後UNIX自体がC言語で書き換えられま)した。

 但し、コンピュータと人間の共通語としてC言語を作ったわけではなく、機械と人間とのいわば通訳としてコンパイルと言う過程を通してC言語をアセンブリ言語に、アセンブリ言語を機械語に翻訳させることにしたのです(その昔ブッシュマンという原住民と話す為に何人もの通訳を介して・・・)。

 一気に書き換えてしまわないのには、それ相応の理由が様々ありますが、コンパイルする人からしても助かるという理由もその1つです。

プリプロセス

 プリプロセス(前処理)は、コンパイル作業の中で最初に行われる工程でgccコマンドから呼ばれたcppコマンド(C PreProcess)によって(#includeした)ヘッダファイル(拡張子.h)などのファイルや(#defineで定義した)マクロを取り込んだり、展開したりして

 プリプロセス済みソースファイル(拡張子.i) 

がボディファイル1つに対して1つ生成されます(gccでこのファイルを生成するには-Eオプション)。

アセンブリコード生成

 アセンブリコード生成では、より機械語に近づける為にC言語で書かれたソースを(それ以前に当たり前で主流であった)アセンブリ言語に変換します。

 そのため、前工程でプリプロセス処理されたプリプロセス済みソースファイル(拡張子.i)に対し、gccは、cc1コマンドによって

 アセンブリファイル(拡張子.s) 

を生成し、これもまたボディファイル1つに付き1つ生成されます(gccでこのファイルを生成するには-Sオプション)。

アセンブル

 英語のアセンブル(assemble)とは日本語で「組み立てる」「集める」ことを表し、IT用語ではアセンブリ言語(で書かれたプログラム)を機械語(で書かれたプログラム)に変換することを、アセンブラ(assembler)はアセンブルする(してくれる)モノを指し、人間が読めるアセンブリ(assembly)言語で書かれたアセンブリファイル1つに付き1つの(もはや人には解読不能な)

 オブジェクトファイル(拡張子.o) 

という中間ファイルを生成します(gccでこのファイルを生成するには-cオプション)。

 この時、前工程でアセンブリコードのコード生成処理されたアセンブリファイル(拡張子.s)からオブジェクトファイル(拡張子.o)を生成する為にgccは、アセンブリ言語に変換する為のコマンドas/gas(assembler/GNU assembler)コマンドを呼びます。

リンク

 英語のリンク(link)とは日本語で「つなげる」「結びつける」ことを表し、むしろ「鎖の輪」「編み物の目」のように密接な結びつきとしての言葉ですが、コンパイルにおけるリンクもほぼ同様でボディファイル1つに付き1つ生成されるオブジェクトファイル(群)、ヘッダファイル、関連するソースファイル、と静的ライブラリファイル(拡張子.a)や共有ライブラリファイル(動的ファイル/拡張子.so)などのライブラリを結びつけて最終的な実行ファイルを生成する過程を指し、これを行うプログラムがリンカ(linker)です。

 この時、gccは、リンカであるldコマンドを呼びます。

他のファイルとオブジェクトファイル

 ここまでの各ファイルはボディファイル1つに付き1つ生成されてきましたが、このオブジェクトファイルは、そうしたファイルの最後のファイルであり、かなり意識して利用されます。

 にも関わらず、gccをオプションなしで実行すると前述の通り、実行ファイル以外は何も生成されないように見えますが、ここまでに挙げたファイルがそれぞれ内部で生成または生成され使用後、削除されてしまうのはなぜでしょうか?

Cのコンパイルでオブジェクトファイルが特に意識される理由

 これは1つのボディファイル(.c)から成るプログラムであれば一度コンパイルに成功す(しバグもないことがわか)ると、以後使われることもなく全く不要なものだからというのも理由の1つとも言えると思いますが、一方、これが数本、数百本のソースファイルを要するシステムだった場合、コンパイル作業だけでも相当の時間を要するだろうことは明白です。

 そんな時にあとはリンクを残すだけという状態までコンパイルが完了しているオブジェクトファイル(.o)を利用すれば、コンパイル時間を大幅に短縮できる可能性が大いにある上、リンクされるファイル、つまりライブラリとしても利用でき、不要になればいっぺんに削除することは容易なので、大量の無駄なファイルであるどころか、逆に効率的かつ便利で有用なファイルである為に意識して利用されるということです。

 というわけで、こうした流れを実際にgcc/GNU Compiler Collectionで確認してみましょう。

LINK

ホーム前へ次へ