GCCはGNU Compiler Collectionの略名で、C,C++,Objective-C, Fortran, Java, Adaなどのコンパイラです。そのため単体では、アセンブルとリンクはできないようです。これらはGNU Binutilのasとldが担当しています。最近のLinuxなどではgccをインストールすると当然のようにBinutilやmakeがインストールされているので実感がありませんが、C言語の基本的な部分を理解するためにもプリプロセス、コンパイル、アセンブル、リンクを手作業で実行して見ようと思います。
#include#define HOGE 1 int main(int argc, char *argv[]) { int testdata = HOGE; printf("Hello world\n"); printf("%d", testdata); return 0; }
cppコマンドはヘッダファイル(#include)の読み込みとマクロの展開を行います。
$cpp hello.c
上記を実行すると#include <stdio.h>の展開とマクロHOGEの展開をして標準出力に表示します。man gccでソースコードのプレフィックスを呼んでみるとプリプロセスの結果ファイルは.iなので以下のようにプリプロセスを行います。
$cpp hello.c hello.i
コンパイルはccコマンドが担当します。man gccを読むとコンパイルの出力結果は.sになります。またccコマンドでコンパイルのみを実行するためオプションの-Sをつけます。これでアセンブラソースコードを生成します。
$cc -S hello.i -o hello.s
アセンブルはasコマンドが担当します。man gccを読むとアセンブルの出力結果は.oになります。
$as hello.s -o hello.o
また-aオプションをつけるとマシン語のリストを表示します。
$as -a hello.s
リンクはldコマンドが担当します。リンカはアセンブルで生成したオブジェクトファイルとライブラリの結合をします。
$ld -o hello hello.o -lc
ld: warning: cannot find entry symbol _start; defaulting to 08048184
上記オプションだけではエラーが出力されました。man ldで確認すると、以下のような説明がかかれています。
どうやら、ライブラリの結合だけではなく、データのリロケートとシンボルの参照をまとめる作業を行うようです。
ld は複数のオブジェクトファイルや書庫 (archive) ファイルを結合し、それ
らのデータをリロケートして、シンボルの参照をまとめる。新たな実行プロ グ
ラ ムをコンパイルして作成する作業の最終ステップは、多くの場合 ld の呼び
出しとなる。
$ld -o hello hello.o -lc /lib/crt0.o
$ld -o hello /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc
WEBで検索して上記を実行してみたらリンクを正常におこなえましたが、ハローワールドが表示できませんでした。./helloを実行すると
以下のエラーが表示されます。
-bash: ./hello: /usr/lib/libc.so.1: bad ELF interpreter: そのようなファイルやデ ィレクトリはありません
ELFなどについて調べる必要があるようです。
man ldを確認してみるとオプション-M(--print-map)をつけることでリンクマップを標準出力に表示します。
$ ld -M -o hello /usr/lib/crti.o /usr/lib/crt1.o hello.o -lc
ldの詳細ははっきり言って分かりません。別のアプローチとして以下を実行してリンカの動作を確認してみます。
$ gcc -v hello.c -o hello
/usr/lib/gcc-lib/i386-redhat-linux/3.2/specs から spec を読み込み中
コンフィグオプション: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info
--enable-shared --enable-threads=posix --disable-checking --host=i386-redhat-linux
--with-system-zlib --enable-__cxa_atexit
スレッドモデル: posix
gcc バージョン 3.2 20020903 (Red Hat Linux 8.0 3.2-7)
/usr/lib/gcc-lib/i386-redhat-linux/3.2/cc1 -lang-c -v -D__GNUC__=3 -D__GNUC_MINOR__=2
-D__GNUC_PATCHLEVEL__=0 -D__GXX_ABI_VERSION=102 -D__ELF__ -Dunix -D__gnu_linux__
-Dlinux -D__ELF__ -D__unix__ -D__gnu_linux__ -D__linux__ -D__unix -D__linux
-Asystem=posix -D__NO_INLINE__ -D__STDC_HOSTED__=1 -Acpu=i386 -Amachine=i386
-Di386 -D__i386 -D__i386__ -D__tune_i386__ hello.c -quiet -dumpbase hello.c
-version -o /tmp/cctXOKKt.s
GNU CPP version 3.2 20020903 (Red Hat Linux 8.0 3.2-7) (cpplib) (i386 Linux/ELF)
GNU C version 3.2 20020903 (Red Hat Linux 8.0 3.2-7) (i386-redhat-linux)
compiled by GNU C version 3.2 20020903 (Red Hat Linux 8.0 3.2-7).
存在しないディレクトリ "/usr/i386-redhat-linux/include" を無視します
#include "..." の探索はここから始まります:
#include <...> の探索はここから始まります:
/usr/local/include
/usr/lib/gcc-lib/i386-redhat-linux/3.2/include
/usr/include
探索リストの終わり
as -V -Qy -o /tmp/ccmLiiSO.o /tmp/cctXOKKt.s
GNU assembler version 2.13.90.0.2 (i386-redhat-linux) using BFD version 2.13.90.0.2
20020802
/usr/lib/gcc-lib/i386-redhat-linux/3.2/collect2 --eh-frame-hdr -m elf_i386
-dynamic-linker /lib/ld-linux.so.2 -o hello /usr/lib/gcc-lib/i386-redhat-linux/3.2/../../../crt1.o
/usr/lib/gcc-lib/i386-redhat-linux/3.2/../../../crti.o /usr/lib/gcc-lib/i386-redhat-linux/3.2/crtbegin.o
-L/usr/lib/gcc-lib/i386-redhat-linux/3.2 -L/usr/lib/gcc-lib/i386-redhat-linux/3.2/../../..
/tmp/ccmLiiSO.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i386-redhat-linux/3.2/crtend.o
/usr/lib/gcc-lib/i386-redhat-linux/3.2/../../../crtn.o
上記からリンカの部分を実行してみます。asで生成したhello.oをmyhelloとしてリンクしてみます。
/usr/lib/gcc-lib/i386-redhat-linux/3.2/collect2 --eh-frame-hdr -m elf_i386
-dynamic-linker /lib/ld-linux.so.2 -o myhello /usr/lib/gcc-lib/i386-redhat-linux/3.2/../../../crt1.o
/usr/lib/gcc-lib/i386-redhat-linux/3.2/../../../crti.o /usr/lib/gcc-lib/i386-redhat-linux/3.2/crtbegin.o
-L/usr/lib/gcc-lib/i386-redhat-linux/3.2 -L/usr/lib/gcc-lib/i386-redhat-linux/3.2/../../..
./hello.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i386-redhat-linux/3.2/crtend.o
/usr/lib/gcc-lib/i386-redhat-linux/3.2/../../../crtn.o
$ ./myhello
Hello world
1
リンクは実際にldコマンドではなく、collect2コマンドを利用しているのが分かりましたが、当初考えていたよりもリンクのオプションは複雑で-dynamic-linkerなどリンカのオプションを詳しく調べてみる必要がありそうです。