2017-01-28 11 views
2

まず、私は一緒に遊んだおもちゃのプログラム:なぜリンカは.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でオフになっている場合にのみ使用されます。

+0

これは既知の問題です。 https://ewontfix.com/18/ – technosaurus

+0

@technosaurusを参照してください、ありがとう、その記事は、関連しているが異なる問題をカバーしています。特に、( '.rela.dyn'セクションの)コード中の* function call *の再配置について議論しています。私の質問は、GOTエントリをターゲットとする再配置に関するものです(' .rela.plt'セクションにあります) )。それにもかかわらず、面白い読書、それを感謝します。 –

+0

@technosaurus ewontfixの記事では、32ビットx86上の非効率なPLTコードgenについて説明しているので、おそらくこの質問とは無関係です。 – yugr

答えて

1

@yugr

おかげでこれは結構です。さて、。0x601018のpltセクションでは、に0x400566に戻ってこのポインタが含まれていますが、 はバイナリ自体がアドレスを保持していて、ロード時には何もしません。

実際はありません。 0x400566のコードは0x400550にジャンプします。つまり、PLTスタブにより前の16バイトにジャンプします。 0x400550のコードはスタック上のGOTのアドレスをプッシュし、ダイナミックリンカを呼び出します。詳細はthis presentation(スライド14)をご覧ください。理にかなって

このアドレスはリンク時に知られているので、

はそれですか?アドレスは、ASLRのために起動時にランダムアドレスにロードされる共有ライブラリから来るので、スタティックリンカがアドレスを知る方法はありません...

なぜこのGOTアドレスの再配置エントリがありますか? ?

PLTスタブが(最初の呼び出しで)ダイナミックリンカを呼び出すと、GOTエントリのアドレスが渡されます。動的リンカーは.rela.pltを検索して、GOTエントリ(関数名とオフセット)の再配置方法を調べます。

+0

@RahmUrwin(1) - Hm、PLTは0x400560にあり、リゾルバは0x400550にあります。私はそれがリンカ(2)を呼び出すことに言及したと思います。0x400566は、最初の 'f'の呼び出しで' _dl_runtime_resolve'にジャンプすることを目的とするスタブを指しています。それはもちろんリンク時には知られていますが、 'f'(3)とは無関係です。どうしてこれが私の答えと矛盾するかわかりませんか? .rela.plt _is_は、遅延リンク中でも関数アドレスを解決するために使用されます。 – yugr

+0

@RahmUrwin "Snarky" - 決して、申し訳ありません。 – yugr

+0

@RahmUrwin(1)うーん、あなたの声明に上記の投稿がどのように違うのか分からないので、何か誤解があるはずです。はい、0x400550はPLTセクションの一部で、 '_dl_runtime_resolve'の呼び出しを含み、' f'のPLTスタブに先行しています(PLTの最初のエントリであることを指摘しています)、プログラムが最初に 'f '。私の投稿のいずれもこれらのステートメントを対象としていません... – yugr

関連する問題