シンボルの置き換え
今回は、シンボルの置き換えについて解説します。次のコマンドを実行し、huga.txtを開きましょう:
objdump -r exec_file_sample.o >> huga.txt
このコマンドにより、exec_file_sample.oの.rela.textセクションがhuga.txtに書き込まれます:
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000000000015 R_X86_64_PC32 hoge-0x0000000000000004
0000000000000021 R_X86_64_PLT32 add_num-0x0000000000000004
000000000000002a R_X86_64_PC32 .rodata-0x0000000000000004
0000000000000034 R_X86_64_PLT32 printf-0x0000000000000004
VALUEの中に、hogeやadd_num、printf、等のexec_file_sample.cで用いられた関数名が表示されているのが分かります。このセクションではこれらのシンボルの実態データを.textセクションのどこに配置すべきかを示しています。では、また.textセクションを見てみましょう:
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 10 sub $0x10,%rsp
c: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
13: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 19 <main+0x19>
19: 8b 55 fc mov -0x4(%rbp),%edx
1c: 89 d6 mov %edx,%esi
1e: 89 c7 mov %eax,%edi
20: e8 00 00 00 00 callq 25 <main+0x25>
25: 89 c6 mov %eax,%esi
27: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 2e <main+0x2e>
2e: b8 00 00 00 00 mov $0x0,%eax
33: e8 00 00 00 00 callq 38 <main+0x38>
38: b8 00 00 00 00 mov $0x0,%eax
3d: c9 leaveq
3e: c3 retq
いま、.rela.textセクションのhogeの行にはOFFSET 15が格納されていることが分かります。そこで、.textセクションのオフセット15の位置(:=6行目の3列目)を見てみましょう。ここには00 00 00 00が格納されていますが、.rela.textの内容からリンク後にはhogeのデータ位置が格納されると予想されます。さらに、add_numはOFFSETが21なので、同様にして10行目の2列目にはリンク後にadd_numのデータ位置が格納されるとと予想されます。では、実際にリンク後の実行ファイルの.textセクションを見てみましょう。次のコマンドを実行してください:
objdump -S exec_file_sample >> piko.txt
すると、.textセクションのmain関数部分は次のようになります:
0000000000001149 <main>:
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 83 ec 10 sub $0x10,%rsp
1155: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
115c: 8b 05 b2 2e 00 00 mov 0x2eb2(%rip),%eax # 4014 <hoge>
1162: 8b 55 fc mov -0x4(%rbp),%edx
1165: 89 d6 mov %edx,%esi
1167: 89 c7 mov %eax,%edi
1169: e8 1a 00 00 00 callq 1188 <add_num>
116e: 89 c6 mov %eax,%esi
1170: 48 8d 3d 8d 0e 00 00 lea 0xe8d(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
1177: b8 00 00 00 00 mov $0x0,%eax
117c: e8 cf fe ff ff callq 1050 <printf@plt>
1181: b8 00 00 00 00 mov $0x0,%eax
1186: c9 leaveq
1187: c3 retq
確かに、exec_file_smaple.oの時点では00 00 00 00だった部分に異なる値が格納されていることが分かります。OFFSET 15の位置には、hogeシンボルの位置を表すb2 2e 00 00という値が格納されています。さらに、OFFSET 21のadd_numの位置には同様に、1a 00 00 00が格納され、これはメモリ中のadd_numの位置を表します。このように、ソースコード中で定義された変数や関数が実行ファイルの中ではメモリ上の位置として参照されます。
最後に
普段何気なくソースコード中で使う変数・関数が実行ファイルにどのように埋め込まれるのか理解していただけたかと思います。リンクによりシンボルが解決する仕組みのおかげで、分割してファイルをコンパイルすることができ、大規模なシステムを作ることができます。