リンカによるシンボル解決(3)ファイル内関数のCALL
今回は、関数の呼び出し部分の実行コードについて、リンク前とリンク後で比較してみます。
d: e8 f3 ff ff ff call 5
80483c1: e8 f3 ff ff ff call 80483b9
ありゃ、該当のコード「e8 f3 ff ff ff」には、まったく変化なしですね。局所変数の場合、オブジェクトファイルでは.bssや.dataの先頭からのオフセット値が仮の値として埋め込まれており、リンク工程で論理アドレスに展開されていましたが、局所関数の場合、その手の変換は行われていないようです。
では、「f3 ff ff ff」とは、何を示す値か?というと、<ジャンプ先関数の先頭アドレス − 次に実行される命令のアドレス>
です。
CALL命令に到達した時点でのプログラム・カウンタ(PC)の値は、CALL先から戻ってきて次に実行される命令のアドレスが格納されているため、CALL命令によるジャンプ先のアドレスは、「CALL命令到達時点でのPCの値 + オフセット0xfffffff3(10進数表現で-13)」となります。
実行コードを確認してみると、
080483b9: 80483b9: 55 push ebp 80483ba: 89 e5 mov ebp,esp 80483bc: 5d pop ebp 80483bd: c3 ret 080483be : 80483be: 55 push ebp 80483bf: 89 e5 mov ebp,esp 80483c1: e8 f3 ff ff ff call 80483b9 80483c6: 8b 15 28 a0 04 08 mov edx,DWORD PTR ds:0x804a028 80483cc: a1 18 a0 04 08 mov eax,ds:0x804a018 80483d1: 01 d0 add eax,edx 80483d3: 5d pop ebp 80483d4: c3 ret
main()関数内のCALL命令(アドレス:80483c1)の到達時点で、PCの値は80483c6となっているはずです。ここからfunc()の先頭アドレスにジャンプするわけですが、ジャンプ先のアドレスは「80483c6 + fffffff3 = 80483b9」となり、確かにfunc()関数の先頭アドレスと一致しています。
なお、オブジェクトファイルの再配置テーブル(.rel.text)(以下)には、関数func()のエントリは存在しません。CALL命令によるジャンプ先はオフセットアドレスで指定されるため、アドレスの展開が不要となるためです。
Relocation section '.rel.text' at offset 0x490 contains 2 entries: Offset Info Type Sym.Value Sym. Name 00000014 00000401 R_386_32 00000000 .bss 00000019 00000301 R_386_32 00000000 .data