リンカによるシンボル解決(1)ファイル内変数のシンボル解決(前編)
以下は、あるオブジェクトファイルの機械語コードのダンプです。
$ gcc -c sample.c -Wall $ objdump -d sample.o -M intel (略) 00000000 <main>: 0: 55 push ebp 1: 89 e5 mov ebp,esp 3: 8b 15 04 00 00 00 mov edx,DWORD PTR ds:0x4 9: a1 08 00 00 00 mov eax,ds:0x8 e: 01 d0 add eax,edx 10: 5d pop ebp 11: c3 ret
これをコンパイルしてリンクが完了すると、以下のコードに変換されます。
$ gcc sample.o -o sample -wall $ objdump -d sample -M intel (略) 080483b4 <main>: 80483b4: 55 push ebp 80483b5: 89 e5 mov ebp,esp 80483b7: 8b 15 28 a0 04 08 mov edx,DWORD PTR ds:0x804a028 80483bd: a1 18 a0 04 08 mov eax,ds:0x804a018 80483c2: 01 d0 add eax,edx 80483c4: 5d pop ebp 80483c5: c3 ret
リンクの前後で変化のあった行を抜粋すると…
/* リンク前 */ 3: 8b 15 04 00 00 00 mov edx,DWORD PTR ds:0x4 9: a1 08 00 00 00 mov eax,ds:0x8
/* リンク後 */ 80483b7: 8b 15 28 a0 04 08 mov edx,DWORD PTR ds:0x804a028 80483bd: a1 18 a0 04 08 mov eax,ds:0x804a018
リンク前とリンク後で、2つのmov命令のオペランドが変化しています。
- 「04 00 00 00」→「28 a0 04 08」
- 「08 00 00 00」→「18 a0 04 08」
エンディアンを考慮すると、リンク後の値は「0x0804a028」「0x804a018」となっているわけですが、これは変数のアドレスですね。つまり、機械語コードの中で、変数のアドレスが埋め込まれる予定の部分には、リンク前には「04」「08」という仮の値(?)が埋め込まれており、リンクによって、その「仮の値」が実際の論理アドレスに置換されるのだと推測されます。
イメージで言うと、リンク前のアドレス埋め込み部分は空欄になっていますが、リンカによるシンボル解決によって、空欄だった部分に、実際の論理アドレスが埋め込まれるわけです。
Cのソースコード | (コンパイル) | (アセンブル) ↓ mov edx,[ ] mov eax,[ ] | (リンク) ↓ mov edx,DWORD PTR ds:0x804a028 mov eax,ds:0x804a018
しかし、機械語コードの中にアドレスを埋め込む部分は、どうやって管理されているのでしょうか。リンカは、オブジェクトファイルに書かれた機械語の意味を理解せず、単純にベタバイナリ(16進数の羅列)のデータとしてしか見てないのに、どうやって埋め込み部分を特定しているのでしょうか。
つづく…