はじめに

gccに入門したのでメモ

gccは何をしているのか

「プリプロセシング」「コンパイル」「リンク」を順番に行っている。

Cファイルごとにプリプロセシング、コンパイルを行い、 Cファイルごとにオブジェクトファイルを生成する。

最後に、リンクによりmain関数が含まれるCファイルをエントリーポイントとする実行可能ELFファイルを作成する。

シンプルなCファイルで実験してみる

まずはファイルを作成。

# hello_const.c
const char msg[] = "Hello World"

コンパイルのみ行ってオブジェクトファイルを作成。

gcc -c hello_const.c -o hello_const.o

中身を見てみる。

コマンド

xxd ./hello_const.o

結果

00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0100 3e00 0100 0000 0000 0000 0000 0000  ..>.............
00000020: 0000 0000 0000 0000 5801 0000 0000 0000  ........X.......
00000030: 0000 0000 4000 0000 0000 4000 0b00 0a00  ....@.....@.....
00000040: 4865 6c6c 6f20 576f 726c 6400 0047 4343  Hello World..GCC
00000050: 3a20 2855 6275 6e74 7520 3133 2e33 2e30  : (Ubuntu 13.3.0
00000060: 2d36 7562 756e 7475 327e 3234 2e30 3429  -6ubuntu2~24.04)
00000070: 2031 332e 332e 3000 0400 0000 1000 0000   13.3.0.........
00000080: 0500 0000 474e 5500 0200 00c0 0400 0000  ....GNU.........
00000090: 0300 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0100 0000 0400 f1ff 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0f00 0000 1100 0400  ................
000000d0: 0000 0000 0000 0000 0c00 0000 0000 0000  ................
000000e0: 0068 656c 6c6f 5f63 6f6e 7374 2e63 006d  .hello_const.c.m
000000f0: 7367 0000 2e73 796d 7461 6200 2e73 7472  sg...symtab..str
00000100: 7461 6200 2e73 6873 7472 7461 6200 2e74  tab..shstrtab..t
00000110: 6578 7400 2e64 6174 6100 2e62 7373 002e  ext..data..bss..
00000120: 726f 6461 7461 002e 636f 6d6d 656e 7400  rodata..comment.
00000130: 2e6e 6f74 652e 474e 552d 7374 6163 6b00  .note.GNU-stack.
00000140: 2e6e 6f74 652e 676e 752e 7072 6f70 6572  .note.gnu.proper
00000150: 7479 0000 0000 0000 0000 0000 0000 0000  ty..............
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000190: 0000 0000 0000 0000 1b00 0000 0100 0000  ................
000001a0: 0600 0000 0000 0000 0000 0000 0000 0000  ................
000001b0: 4000 0000 0000 0000 0000 0000 0000 0000  @...............
000001c0: 0000 0000 0000 0000 0100 0000 0000 0000  ................
000001d0: 0000 0000 0000 0000 2100 0000 0100 0000  ........!.......
000001e0: 0300 0000 0000 0000 0000 0000 0000 0000  ................
000001f0: 4000 0000 0000 0000 0000 0000 0000 0000  @...............
00000200: 0000 0000 0000 0000 0100 0000 0000 0000  ................
00000210: 0000 0000 0000 0000 2700 0000 0800 0000  ........'.......
00000220: 0300 0000 0000 0000 0000 0000 0000 0000  ................
00000230: 4000 0000 0000 0000 0000 0000 0000 0000  @...............
00000240: 0000 0000 0000 0000 0100 0000 0000 0000  ................
00000250: 0000 0000 0000 0000 2c00 0000 0100 0000  ........,.......
00000260: 0200 0000 0000 0000 0000 0000 0000 0000  ................
00000270: 4000 0000 0000 0000 0c00 0000 0000 0000  @...............
00000280: 0000 0000 0000 0000 0800 0000 0000 0000  ................
00000290: 0000 0000 0000 0000 3400 0000 0100 0000  ........4.......
000002a0: 3000 0000 0000 0000 0000 0000 0000 0000  0...............
000002b0: 4c00 0000 0000 0000 2c00 0000 0000 0000  L.......,.......
000002c0: 0000 0000 0000 0000 0100 0000 0000 0000  ................
000002d0: 0100 0000 0000 0000 3d00 0000 0100 0000  ........=.......
000002e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002f0: 7800 0000 0000 0000 0000 0000 0000 0000  x...............
00000300: 0000 0000 0000 0000 0100 0000 0000 0000  ................
00000310: 0000 0000 0000 0000 4d00 0000 0700 0000  ........M.......
00000320: 0200 0000 0000 0000 0000 0000 0000 0000  ................
00000330: 7800 0000 0000 0000 2000 0000 0000 0000  x....... .......
00000340: 0000 0000 0000 0000 0800 0000 0000 0000  ................
00000350: 0000 0000 0000 0000 0100 0000 0200 0000  ................
00000360: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000370: 9800 0000 0000 0000 4800 0000 0000 0000  ........H.......
00000380: 0900 0000 0200 0000 0800 0000 0000 0000  ................
00000390: 1800 0000 0000 0000 0900 0000 0300 0000  ................
000003a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003b0: e000 0000 0000 0000 1300 0000 0000 0000  ................
000003c0: 0000 0000 0000 0000 0100 0000 0000 0000  ................
000003d0: 0000 0000 0000 0000 1100 0000 0300 0000  ................
000003e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003f0: f300 0000 0000 0000 6000 0000 0000 0000  ........`.......
00000400: 0000 0000 0000 0000 0100 0000 0000 0000  ................
00000410: 0000 0000 0000 0000                      ........

ELFヘッダ

コマンド

readelf --file-header --wide ./hello_const.o

結果

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          344 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         11
  Section header string table index: 10

Type

実行可能ならDYN(PIEの場合)やEXEC、 共有オブジェクトならDYNなどになっているはずだが、オブジェクトファイルなのでRELになっている。

プログラムヘッダ

ELFヘッダから見てだ存在しないが、念の為確認。

コマンド

readelf --program-header --wide ./hello_const.o

結果

There are no program headers in this file.

セクションヘッダ

コマンド

readelf --wide --section-headers ./hello_const.o

結果

There are 11 section headers, starting at offset 0x158:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        0000000000000000 000040 000000 00  AX  0   0  1
  [ 2] .data             PROGBITS        0000000000000000 000040 000000 00  WA  0   0  1
  [ 3] .bss              NOBITS          0000000000000000 000040 000000 00  WA  0   0  1
  [ 4] .rodata           PROGBITS        0000000000000000 000040 00000c 00   A  0   0  8
  [ 5] .comment          PROGBITS        0000000000000000 00004c 00002c 01  MS  0   0  1
  [ 6] .note.GNU-stack   PROGBITS        0000000000000000 000078 000000 00      0   0  1
  [ 7] .note.gnu.property NOTE            0000000000000000 000078 000020 00   A  0   0  8
  [ 8] .symtab           SYMTAB          0000000000000000 000098 000048 18      9   2  8
  [ 9] .strtab           STRTAB          0000000000000000 0000e0 000013 00      0   0  1
  [10] .shstrtab         STRTAB          0000000000000000 0000f3 000060 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), l (large), p (processor specific)

.text セクション

命令列がないのでなし

.data セクション

なし

.bss セクション

なし

.rodata セクション

readelf の結果に従ってxxdで取り出してみる。

xxd -seek 0x000040 -len 0x00000c ./hello_const.o
00000040: 4865 6c6c 6f20 576f 726c 6400            Hello World.

ASCIIでHello Worldが入っていた。ちゃんとNULL終端でいい感じ。

※ xxdはNULLをドットで表現するので注意。

.comment セクション

同様にxxdで取り出す。

xxd -seek 0x00004c -len 0x00002c ./hello_const.o
0000004c: 0047 4343 3a20 2855 6275 6e74 7520 3133  .GCC: (Ubuntu 13
0000005c: 2e33 2e30 2d36 7562 756e 7475 327e 3234  .3.0-6ubuntu2~24
0000006c: 2e30 3429 2031 332e 332e 3000            .04) 13.3.0.

GCCのバージョン13.3.0が入っていた。

OSのバージョンはrangeのようだけど、不明。

.note.GNU-stack セクション

空。

.note.gnu.property セクション

xxdコマンドは以後省略。

xxd -seek 0x000078 -len 0x000020 ./hello_const.o
00000078: 0400 0000 1000 0000 0500 0000 474e 5500  ............GNU.
00000088: 0200 00c0 0400 0000 0300 0000 0000 0000  ................

これについてはよくわからなかったので解析したところ、一応理解できた。

0400 0000: name size
1000 0000: description size
0500 0000: Type
474e 5500: name(GNU\0)
0200 00c0 0400 0000 0300 0000 0000 0000: description

確かに、nameは4byte

descriptionは0x10byteになっている。

このdescriptionには、GNU関連のメモが書かれている。

.symtab セクション

00000098: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a8: 0000 0000 0000 0000 0100 0000 0400 f1ff  ................
000000b8: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c8: 0f00 0000 1100 0400 0000 0000 0000 0000  ................
000000d8: 0c00 0000 0000 0000                      ........

シンボルテーブルセクション。 上記には3つのシンボルが記載されているが、読みづらいのでコマンドで得た。

readelf --wide --symbols ./hello_const.o

3行目を見るとmsgという変数(OBJECTなので、関数でなく値)があることがわかる。

また、Bind=GLOBALということなので、ELFレベルでGLOBAL変数ということがわかる。

また、NdxがUNDではないので、ちゃんと定義がされていることがわかる。

2行目にはファイル名が書かれている。

Symbol table '.symtab' contains 3 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello_const.c
     2: 0000000000000000    12 OBJECT  GLOBAL DEFAULT    4 msg

.strtab セクション

000000e0: 0068 656c 6c6f 5f63 6f6e 7374 2e63 006d  .hello_const.c.m
000000f0: 7367 00                                  sg.

文字列が、先頭のNULLを入れて3つある。

.shstrtab セクション

000000f3: 002e 7379 6d74 6162 002e 7374 7274 6162  ..symtab..strtab
00000103: 002e 7368 7374 7274 6162 002e 7465 7874  ..shstrtab..text
00000113: 002e 6461 7461 002e 6273 7300 2e72 6f64  ..data..bss..rod
00000123: 6174 6100 2e63 6f6d 6d65 6e74 002e 6e6f  ata..comment..no
00000133: 7465 2e47 4e55 2d73 7461 636b 002e 6e6f  te.GNU-stack..no
00000143: 7465 2e67 6e75 2e70 726f 7065 7274 7900  te.gnu.property.

最初のNULLを入れて、11個の文字列がある。

そもそも、セクション名もこのStringテーブルにおけるoffsetとsizeで指定されており、この場所がセクション名の定義なのであった。

セクション名が、ドット(2e)で始まっているのが面白い。

本体

それぞれのセクションヘッダのoffsetが、直前のoffset+sizeになっているので、セクションヘッダの並び通りにセクションのデータが本体に含まれていることがわかる。