技術情報

実行ファイルはどう作られるか? (4)

シンボルの置き換え

今回は、シンボルの置き換えについて解説します。次のコマンドを実行し、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の位置を表します。このように、ソースコード中で定義された変数や関数が実行ファイルの中ではメモリ上の位置として参照されます。

最後に

普段何気なくソースコード中で使う変数・関数が実行ファイルにどのように埋め込まれるのか理解していただけたかと思います。リンクによりシンボルが解決する仕組みのおかげで、分割してファイルをコンパイルすることができ、大規模なシステムを作ることができます。

D.S S.O
データサイエンティストのS.Oです。