まず、私は一緒に遊んだおもちゃのプログラム:なぜリンカは.rela.pltで一見役に立たない再配置を生成しますか?
prog.cの:
int func1();
int main(int argc, char const *argv[])
{
func1();
return 0;
}
lib.c:
int func1()
{
return 0;
}
ビルドで:
gcc -O3 -g -shared -fpic ./lib.c -o liba.so
gcc prog.c -g -la -L. -o prog -Wl,-rpath=$PWD
そして、完成のため:
$ gcc --version
gcc (GCC) 6.3.1
$ ld --version
GNU ld version 2.26.1
今、私の質問です。私はジャンプの次の命令に、その右バックPLTでfunc1
ポイントの最初にGOTエントリすなわち 、私がどのように動作するか遅延結合の動的シンボルのについて読んだことを確認しました:
$ gdb prog
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400666 <+0>: push rbp
0x0000000000400667 <+1>: mov rbp,rsp
0x000000000040066a <+4>: sub rsp,0x10
0x000000000040066e <+8>: mov DWORD PTR [rbp-0x4],edi
0x0000000000400671 <+11>: mov QWORD PTR [rbp-0x10],rsi
0x0000000000400675 <+15>: mov eax,0x0
0x000000000040067a <+20>: call 0x400560 <[email protected]> <<< call to shared lib via PLT
0x000000000040067f <+25>: mov eax,0x0
0x0000000000400684 <+30>: leave
0x0000000000400685 <+31>: ret
End of assembler dump.
(gdb) disassemble 0x400560
Dump of assembler code for function [email protected]:
0x0000000000400560 <+0>: jmp QWORD PTR [rip+0x200ab2] # 0x601018 <<< JMP to address stored in GOT
0x0000000000400566 <+6>: push 0x0 <<< ... which initially points right back here
0x000000000040056b <+11>: jmp 0x400550
End of assembler dump.
(gdb) x/g 0x601018
0x601018: 0x400566 <<< GOT point back right after the just-executed jump
これは、良い。今度は0x601018
の.got.pltセクションを調べて、0x400566
に戻ると、ロード時に動的リンカが何もしなくても、バイナリ自体がアドレスを保持していることがわかります。これは、このアドレスはリンク時に知られているので、理にかなって:
$ readelf -x .got.plt ./prog
Hex dump of section '.got.plt':
NOTE: This section has relocations against it, but these have NOT been applied to this dump.
0x00601000 ???????? ???????? ???????? ???????? ..`.............
0x00601010 ???????? ???????? 66054000 ???????? [email protected]
(66054000
はリトルエンディアン担当者がどこにあるか、最初はGOTに格納されたアドレス0x400566
ため。)
ファイン、ファイン。しかし...
$ readelf -r prog
<...>
Relocation section '.rela.plt' at offset 0x518 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000601018 000100000007 R_X86_64_JUMP_SLO 0000000000000000 func1 + 0
なぜこのGOTアドレスの再配置エントリがありますか?リンク時に正しいアドレスがバイナリに既に存在していることがわかりました。 私はこの移転を実験的に編集して無効にし、プログラムはうまく実行されます。だから、再配置は何も寄与していないようだ。それが何をしているのですか、より一般的には、rela.plt
の移転が実際に関係するシナリオは何ですか?
更新#1:
明確にすること、これはPICコードと64ビットのコードについてです。私がやった、
は、プロセスイメージは変更されません.rela.plt
ためのセクションヘッダーの編集:
$ readelf -S ./prog
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 9] .rela.dyn RELA 00000000004004e8 000004e8
0000000000000030 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000400518 00000518
0000000000000018 0000000000000018 AI 5 23 8
[12] .plt PROGBITS 0000000000400550 00000550
0000000000000020 0000000000000010 AX 0 0 16
[21] .dynamic DYNAMIC 0000000000600e00 00000e00
00000000000001f0 0000000000000010 WA 6 0 8
[22] .got PROGBITS 0000000000600ff0 00000ff0
0000000000000010 0000000000000008 WA 0 0 8
[23] .got.plt PROGBITS 0000000000601000 00001000
0000000000000020 0000000000000008 WA 0 0 8
更新#2:ここでは、関連するセクションのアドレスはアドレスが属する場所を明確に支援するために、あります再配置を無効にしないでください。 私はrelocアドレスを(別の書き込み可能なアドレスに)変更しようとしましたが、どちらも違いはないようですが、レイジーバインディング中にアドレスがリゾルバによって使用されないことが判明しました。再配置があります。アドレス自体は、遅延バインディングがLD_BIND_NOW
でオフになっている場合にのみ使用されます。
これは既知の問題です。 https://ewontfix.com/18/ – technosaurus
@technosaurusを参照してください、ありがとう、その記事は、関連しているが異なる問題をカバーしています。特に、( '.rela.dyn'セクションの)コード中の* function call *の再配置について議論しています。私の質問は、GOTエントリをターゲットとする再配置に関するものです(' .rela.plt'セクションにあります) )。それにもかかわらず、面白い読書、それを感謝します。 –
@technosaurus ewontfixの記事では、32ビットx86上の非効率なPLTコードgenについて説明しているので、おそらくこの質問とは無関係です。 – yugr