C言語のコンパイルにおけるアセンブラ→実行ファイルまでの流れをまとめてみた
備忘録として、C言語の コンパイルの流れをまとめてみた。
C言語の コンパイラの大まかな流れ
簡単なプログラムであれば gcc test.cだけで コンパイルが可能だが、実際の コンパイルは以下の4つの手順を踏んでいる。
- プリプロセッサで ソースコードを コンパイラが解釈できるように直す
- 1で作った ソースコードを アセンブラに直す
- 2のコードをオブジェクトファイル( 機械語)に直す
- 実行ファイルに直す(exeとかoutとか)
以下は、詳しく見ていく。
1、 プリプロセッサで ソースコードを コンパイラが解釈できるように直す。
プリプロセッサとは、C言語の ソースコードを コンパイラが解釈できるように書き直すことだ。
例えば、 C言語のコードには#includeや#defineなどのディレクティブ(Directive。日本語で「意味」と言う言葉)が用意されているが、 プリプロセッサを行うことで「define MAXのMAXはこういう意味だよ」とか「includeで指定したファイルも読み込んでね」と言う風に コンパイラに教えることができ、これらの情報を基に コンパイラが コンパイルできるような ソースコードに作り直している。
今回の例では、以下のtest.cファイルを用意した。hello worldを表示するための簡単なコードだ。
#include <stdio.h>
int main(void){
printf("hello world");
return 0;
}
上記のファイルを使って、コマンドラインで以下の様に書いて、プリプロセッサを行う。
gcc -E test.c
すると、以下の様な巨大な ソースコードが作られる。(以下の例は結構省略したヤツ。)
# 1 "test.c"
# 1 "<built -in>"
# 1 "<command -line/>"
# 1 "test.c"
# 1 "c:\\mingw\\include\\stdio.h" 1 3
# 38 "c:\\mingw\\include\\stdio.h" 3
# 39 "c:\\mingw\\include\\stdio.h" 3
# 56 "c:\\mingw\\include\\stdio.h" 3
# 1 "c:\\mingw\\include\\_mingw.h" 1 3
# 55 "c:\\mingw\\include\\_mingw.h" 3
# 56 "c:\\mingw\\include\\_mingw.h" 3
# 66 "c:\\mingw\\include\\_mingw.h" 3
# 1 "c:\\mingw\\include\\msvcrtver.h" 1 3
# 35 "c:\\mingw\\include\\msvcrtver.h" 3
# 36 "c:\\mingw\\include\\msvcrtver.h" 3
# 67 "c:\\mingw\\include\\_mingw.h" 2 3
</built>
上記のコードを生成することで、 コンパイラが C言語を解釈できるようになる。
参考記事:プリプロセッサでプログラムの質を向上させよう (1/4):目指せ! Cプログラマ(16) - @IT
参考記事:C言語 プリプロセッサ
また、 gcc -E の -Eの部分の意味は定かではないが、おそらくexpand(拡大する)と言う意味ではないか、と言う意見がある。(ソースが少なすぎて困った)
参考記事:c - What does gcc -E mean? - Stack Overflow
2、1で作った ソースコードを アセンブラに直す
1の手順で プリプロセッサを行った後は、 アセンブラファイルに書き直す。(別に1の手順を踏むことは必須ではない。1の手順を省略していきなり アセンブルしても、 コンパイラが自動で プリプロセッサを行ってくれるからだ)
gcc -S test.c
上記の手順でtest.sと言う アセンブラファイルが生成される。
.file "test.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "hello world\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB10:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
call ___main
movl $LC0, (%esp)
call _printf
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE10:
.ident "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
.def _printf; .scl 2; .type 32; .endef
gcc -Sの-Sの意味は、「Strip the outputでは?(outputを引きちぎる)」と言う意見もある。(ソースが少ない)
参考記事:what does the option -s of gcc mean ?
3, 2のコードをオブジェクトファイル( 機械語)に直す
アセンブラファイルが生成された後は、以下のようにコマンドを書く。
as -o test.o test.s
これでtest.oと言うオブジェクトファイル( 機械語)が生成される。 また、ここでは アセンブリをしたいので gcc ではなく as で実行する。
4、実行ファイルに直す(exeとかoutとか)
最後にオブジェクトファイルを実行ファイルに直す。
gcc -o test test.o
これで Windowsであればtest.exe、 Linuxであればtest.outが生成されてプログラムを実行できる。
gccには他にも色々なオプションが用意されているので、暇な時にチェックしよう。
参考記事:gcc
参考記事:Using the GNU Compiler Collection (GCC): Overall Options