C言語のコンパイルにおけるアセンブラ→実行ファイルまでの流れをまとめてみた

備忘録として、C言語のコンパイルの流れをまとめてみた。

C言語のコンパイラの大まかな流れ

簡単なプログラムであればgcc test.cだけでコンパイルが可能だが、実際のコンパイルは以下の4つの手順を踏んでいる。

  1. プリプロセッサでソースコードをコンパイラが解釈できるように直す
  2. 1で作ったソースコードをアセンブラに直す
  3. 2のコードをオブジェクトファイル(機械語)に直す
  4. 実行ファイルに直す(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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

SNS Shere

Related Post

C言語における関数プロトタイプの必要性について解説する