Last modified: Tue Jun 17 22:50:15 JST 2008

伊達 >> 情報工学序説 >> 課題一覧 >> 演習5

コンパイル,アセンブル,リンク,...

演習の目的

コンパイル, アセンブル・コード, オブジェクトファイル,リンクなど, ソフトウェア開発の話にはカタカナ言葉が多い. ここでは最も単純なC言語のプログラムを 「コンパイル」し実行することを通し, それぞれの言葉が具体的に何を意味しているのか理解する.

はじまり

  1. emacs などのエディタを使い 以下のプログラムを入力する. もしくはダウンロードする.

    プログラム: hello.c. これを「ソースコード」と呼ぶ.

    #include<stdio.h>
    
    int main(void)
    {
    	printf("hello, world\n");
    }
    
    さすがに,このプログラム(ソースコード)の説明は必要ないはず... こういうファイルをアスキーファイルと呼ぶが, このファイルももちろん数字で書かれている. 確認のため od というコマンドを使い,ファイルの中身を見ておく.
    % od -t x1z -A x hello.c
    000000 23 69 6e 63 6c 75 64 65 20 3c 73 74 64 69 6f 2e  >#include h>..int main(voi<
    000020 64 29 0a 7b 0a 09 70 72 69 6e 74 66 28 22 68 65  >d).{..printf("he<
    000030 6c 6c 6f 2c 20 77 6f 72 6c 64 5c 6e 22 29 3b 0a  >llo, world\n");.<
    000040 7d 0a                                            >}.<
    

  2. プログラミング入門と演習で習ったように, コンパイルして走らせてみる.
    % gcc hello.c
    % ./a.out
    
    hello, world
    
    %ls -l
    
    -rwxr-xr-x    1 date     date         4628 Jun 15 20:32 a.out*
    
    この a.out というファイルができているか確認する. 4626 という数字は,ファイルの大きさが 4626バイトという意味. 文字を10文字程度表示させるプログラム(実行ファイル)でも 4kバイトもメモリを費やしていることが分かる.

  3. コマンド od を使い,ファイルの中身を16進数で表示する.

    %od -t x1z -A x a.out
    
    000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00  >.ELF............<
    000010 02 00 03 00 01 00 00 00 98 82 04 08 34 00 00 00  >............4...<
    000020 54 07 00 00 00 00 00 00 34 00 20 00 07 00 28 00  >T.......4. ...(.<
    000030 1b 00 18 00 06 00 00 00 34 00 00 00 34 80 04 08  >........4...4...<
    ....
    
    この 「gcc」 コマンドで作成した a.out は実行可能なファイルである. これから卒業までに gcc を使って,このような使い方以外のことをする機会は ないかもしれないが, 以下のこともできるというとを記憶の片隅に残しておこう.

  4. gcc を実行すると, 実は ,このC 言語のソースコードがまず 「アセンブリ・コード」に変換される. ここまでの仕事を「コンパイル」と言っても差し支えない. 具体的に見たい場合,gcc -S で hello.s が作成される. その中身を確認してみる.
    % gcc -S hello.c
    
    % less hello.s
    
            .file   "hello.c"
            .section        .rodata
    .LC0:
            .string "hello, world\n"
            .text
    .globl main
            .type   main, @function
    main:
            pushl   %ebp
            movl    %esp, %ebp
            subl    $8, %esp
            andl    $-16, %esp
            movl    $0, %eax
            subl    %eax, %esp
            subl    $12, %esp
            pushl   $.LC0
            call    printf
            addl    $16, %esp
            leave
            ret
            .size   main, .-main
            .section        .note.GNU-stack,"",@progbits
            .ident  "GCC: (GNU) 3.3.2 20031218 (Vine Linux 3.3.2-0vl8)"
    
    
    細かいことはどうでもいい. ここでは,命令がいろいろあることがわかればいい.

  5. gcc はその後,教科書で言う,オブジェクトモジュール (オブジェクトファイル,いわゆる機械語)を作成する. そこで止めてみよう.
    % gcc -c hello.c
    
    % ls
    -rw-r--r--    1 date     date          876 Jun 15 20:55 hello.o
    
    という 876バイトのファイルが作成されていることがわかる. これはアセンブリコード(アスキーファイル)と違って バイナリーファイルなので, 例によって od コマンドで中身を覗いてみる.
    %od -t x1z -A x hello.o
    
    000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00  >.ELF............<
    000010 01 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00  >................<
    000020 ec 00 00 00 00 00 00 00 34 00 00 00 00 00 28 00  >........4.....(.<
    000030 0b 00 08 00 55 89 e5 83 ec 08 83 e4 f0 b8 00 00  >....U...........<
    000040 00 00 29 c4 83 ec 0c 68 00 00 00 00 e8 fc ff ff  >..)....h........<
    000050 ff 83 c4 10 c9 c3 00 00 68 65 6c 6c 6f 2c 20 77  >........hello, w<
    000060 6f 72 6c 64 0a 00 00 47 43 43 3a 20 28 47 4e 55  >orld...GCC: (GNU<
    ....
    
    さきに作成した a.out よりはコンパクトである.

    この後,gcc は printf という関数を含んだ「ライブラリ」を hello.o に「リンク」して「ロードモジュール」を作成する. では実際に何がリンクされるのか. 少なくとも関数 printf のオブジェクトファイル printf.o が 含まれたライブラリがリンクされる必要がある. ディレクトリ /usr/lib 以下などに ライブラリは置かれている. stdio.h に関するものは libc.a ということで, そこから printf に関するものを コマンド ar を使って取り出してみる.

    % ar x /usr/lib/libc.a  printf.o
    
    %ls -al printf.o
    -rw-r--r--    1 date     date          696 Jun 17 21:42 printf.o
    
    これだけで約700バイト. libc.a にどんな関数が含まれているか知りたければ,
    % ar tv /usr/lib/libc.a
    
    とすれば分かる.
    % ls -l  /usr/lib/libc.a
    
    -rw-r--r--    1 root     root      2445076 Oct 29  2005 /usr/lib/libc.a
    
    というわけで libc.a はサイズが 2.4Mb ある. a.out は約4kb だったので, libc.a が全部リンクされているわけではないことが分かる.

  6. 実行時に必要となる「共有ライブラリー」というものもある. a.out がどんな共有ライブラリーを使っているかは ldd コマンドで分かる. いろいろな言葉が出てきて,わけが分からなくなっている かもしれないけれど,我慢して前に進む. このライブラリは a.out に含まれているものではない.
    % ldd a.out
            libc.so.6 => /lib/i686/libc.so.6 (0x40023000)
            /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
    
    例によって,わからないコマンドは man ldd と すれば説明が表示される.

  7. nm というコマンドをオブジェクトファイルに適用すれば, そのオブジェクトファイルが使用している関数名などがとりだせる.
    % nm hello.o
    00000000 T main
             U printf
    
    % nm a.out
    
    08049460 D _DYNAMIC
    0804953c D _GLOBAL_OFFSET_TABLE_
    0804843c R _IO_stdin_used
    ...
    
    % nm /usr/lib/libc.a
    ...
    
    オブジェクトファイルならなんでも適用できるので, なんでも試してみればいい.

  8. 実は C 言語のソースコードが アセンブリ・コードに変換されることを コンパイルと言ったが, そこにたどり着く前に, たくさんの処理をしている. たとえば
    % gcc -E hello.c | sed '/^[ ]*$/d' | less
    
    としてみれば分かる. これは前処理.

  9. より詳しい情報は readelf コマンドでもわかる. ELF は Executable and Linking Format の略.
    % readelf -a a.out
    
    ELF ヘッダ:
      マジック:  7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
      クラス:                            ELF32
      データ:                            2 の補数、リトルエンディアン
      バージョン:                        1 (current)
    ...
    
  10. オブジェクトファイル hello.c の逆アセンブル. ソースコードの何行目に対応するか表示.
    % gcc -c -g hello.c
    
    % objdump -d -l hello.o
    
    hello.o:     ファイル形式 elf32-i386
    
    セクション .text の逆アセンブル:
    
    00000000 
    : main(): /home/date/public_html/lectures/2008ics/kadai/hello.c:4 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 08 sub $0x8,%esp 6: 83 e4 f0 and $0xfffffff0,%esp 9: b8 00 00 00 00 mov $0x0,%eax e: 29 c4 sub %eax,%esp /home/date/public_html/lectures/2008ics/kadai/hello.c:5 10: 83 ec 0c sub $0xc,%esp 13: 68 00 00 00 00 push $0x0 18: e8 fc ff ff ff call 19 1d: 83 c4 10 add $0x10,%esp /home/date/public_html/lectures/2008ics/kadai/hello.c:6 20: c9 leave 21: c3 ret
    何回も言いますが,細かいところはわからなくて問題ないです. まずは言葉になれてもらうこと, さらに, オブジェクトファイルの中身がどうなっているか調べるため, いろいろなコマンドが用意されていることを 伝えることが,このページの目的でした. 興味が湧いたら,それぞれの言葉,コマンドを より深くしらべてみてください.

以下のページ,書籍を参考にしました.