普段のgcc -o outfile infileと入力するだけだが、裏では様々なイベントが発生している。
gccは、
プリプロセッサ: /lib/cpp
コンパイラ : /usr/lib/gcc/i686-linux-gnu/4.4/cc1
アセンブラ : /usr/bin/as
リンカ: /usr/bin/lib
の4つのコマンド適宜呼び出すための、ドライバとしての働きをします。
以下に、ここのコマンドを個別に呼び出して、実行形式ファイルを作成する方法を示します。
#1.ソースコードの作成
$ vim hello.c
#include<stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
#2.プリプロセス(#で始まる内容の展開;ヘッダーファイルの取り込み、マクロの展開)
#プリプロセスの結果は標準出力に出されるので、リダイレクトしてファイルに出力する。
/lib/cpp hello.c > hello.i
#3.プリプロセスにより生成した中間ファイルをアセンブリソース(hello.s)にコンパイル
$ /usr/lib/gcc/i686-linux-gnu/4.4/cc1 hello.i
#4.アセンブラによりアセンブルを行いオブジェクトファイル(hello.o)を生成
$ /usr/bin/as hello.s -o hello.o
#5.オブジェクトファイル、共有ライブラリファイル(libc.so)、スタートアップルーチン(/usr/lib/crt1.o)等をダイナミックリンクさせ、実行形式ファイル(hello)を生成
$ /usr/bin/ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/libc.so /usr/lib/crtn.o hello.o
#6.実行形式ファイルを実行
$ ./hello
Hello, world!
理解すると非常にすっきりできるお話です。コンパイルは、入門書には非常に観念的なレベルのみ説明されていることがほとんだと思います。
今回のように、一つ一つんファイルをフルパスで指定していくと、コンパイルの本質が見てきます。
ちなみに、同様の作業は、gccに特定のオプションをつければ可能です。
ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o