Cプログラミング入門

第8回Chapter 3 コンパイルの段階(2)

コンパイルの途中の段階で止めてみる

ここでhello.cについて、コンパイル途中の各ファイルの中身の移り変わりを見てみましょう(図3.2⁠⁠。ただし、プリプロセッサを通った直後のファイルについては、stdio.hの展開結果が長く複雑になるため、図では省略しています。

コンパイルの途中の段階で止めるためのgccのオプションは次のとおりです。

gcc -Eプリプロセッサを通った段階で止める(出力は標準出力)
gcc -Sアセンブラのソースの段階で止める
gcc -cリロケータブルオブジェクトファイルの段階で止める
gcc最後までコンパイルする

図3.2からは、hello.c→hello.s→hello.o→helloというようにファイルが変換されてコンパイルが行われている様子がわかります。

hello.sのアセンブラのソースの段階まではテキストファイルであり、そのまま中身を表示することができます。それ以降の、リロケータブルオブジェクトファイルのhello.oと実行バイナリファイルのhelloはバイナリファイルのため、GNU binutilsに含まれるobjdumpという一種の逆アセンブラを使って表示しています。

なお、ここではgccに、最適化オプションの「-O2」のほかに、別の最適化オプションのひとつである-fomit-frame-pointerを付け、アセンブラのコードがさらに小さくなるようにしています。

図3.2 コンパイルの段階によるファイルの中身の移り変わり

hello.c

#include <stdio.h>

int
main()
{
  printf("Hello World\n");
  return 0;
}

↓

hello.s ⁠gcc -S -O2 -fomit-frame-pointer hello.c)
.section        .rodata
.LC0:
        .string "Hello World\n"
.text
        .align 4
.globl main
        .type    main,@function
main:
        pushl $.LC0
        call printf
        xorl %eax,%eax
        addl $4,%esp
        ret

↓

hello.o ⁠gcc -c -O2 -fomit-frame-pointer hello.c)
⁠objdump -d hello.o」で表示)

hello.o:     file format elf32-i386

Disassembly of section .text:

00000000 <main>:
   0:   68 00 00 00 00          pushl  $0x0
   5:   e8 fc ff ff ff          call   6 <main+0x6>
   a:   31 c0                   xorl   %eax,%eax
   c:   83 c4 04                addl   $0x4,%esp
   f:   c3                      ret

↓

hello(gcc -o hello -O2 -fomit-frame-pointer hello.c)
⁠objdump -d hello」で表示)

hello:     file format elf32-i386

Disassembly of section .init:

08048298 <_init>:
 8048298:       55                      pushl  %ebp
                :
                :
 80482c6:       c3                      ret
Disassembly of section .plt:

080482c8 <.plt>:
 80482c8:       ff 35 64 94 04 08       pushl  0x8049464
                :
                :
 8048313:       e9 b0 ff ff ff          jmp    80482c8 <_init+0x30>
Disassembly of section .text:

08048320 <_start>:
 8048320:       31 ed                   xorl   %ebp,%ebp
                :
                :
                :
080483c8 <main>:
 80483c8:       68 30 84 04 08          pushl  $0x8048430
 80483cd:       e8 36 ff ff ff          call   8048308 <_init+0x70>
 80483d2:       31 c0                   xorl   %eax,%eax
 80483d4:       83 c4 04                addl   $0x4,%esp
 80483d7:       c3                      ret
 80483d8:       90                      nop
                :
                :
                :

lddコマンドでライブラリを確認

コンパイル済みの実行バイナリファイルは、libc(標準ライブラリ関数)とリンクして動作するようになっています。libcは共有ライブラリであり、libcとのリンクは実行バイナリファイルの実行時に動的に行われます。

実行バイナリファイルが実際にどのライブラリとリンクしているかは、図3.3の実行例のように、lddコマンドを使って確認することができます。

図3.3 lddコマンドで動的リンクしている共有ライブラリを表示
$ ldd hello
        -lc.1 => /usr/lib/libc.so.1.9     ← libc
        -ldl.1 => /usr/lib/libdl.so.1.0

OS環境によってはlibcのほか、いくつかのシステムで必要なライブラリともリンクされている場合があります。なお、lddコマンドで表示されるのは動的リンクしている共有ライブラリの場合であり、ライブラリが静的リンクされている場合はlddコマンドでは表示されません。

おすすめ記事

記事・ニュース一覧