はじめに
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になっているので、セクションヘッダの並び通りにセクションのデータが本体に含まれていることがわかる。