Linuxダイナミックリンク(3)のPLTとGOTについて話す-パブリックGOTエントリ

前の記事(Linuxダイナミックリンク(2)でのPLTとGOTについて-遅延再配置)では、すべてのダイナミックライブラリ関数のplt命令が最終的にパブリックplt実行にジャンプするので、パブリックplt命令のアドレスは何ですか?

テスト実行可能ファイルのパブリックpltを貼り付けます。

080482a0 <common@plt>:
 80482a0:  pushl 0x80496f0
 80482a6:  jmp *0x80496f4
 ...

最初の文、pushl 0x80496f0は、アドレスをスタックにプッシュすることです。つまり、最後に呼び出された関数にパラメーターを渡します。 
2番目の文、jmp * 0x80496f4は、実行する最後の関数にジャンプすることですが、考えられることは、動的ライブラリ関数のアドレスを解決できるコードにジャンプすることです。

0x80496f4聖人はどこですか?gdbデバッガーを使用して、それを引き出します。
 

$ gdb -q ./test
...
(gdb)x/xw 0x80496f4
0x80496f4 <_GLOBAL_OFFSET_TABLE_+8>:    0x00000000
(gdb) b main
Breakpoint 1 at 0x80483f3
(gdb) r
Starting program: /home/ivan/test/test/test

Breakpoint 1, 0x80483f3 in main ()
(gdb) x/xw 0x80496f4
0x80496f4 <_GLOBAL_OFFSET_TABLE_+8>:    0xf7ff06a0

デバッグプロセスから、0x80496f4はGOTテーブルのアイテムに属していることがわかります。プロセスが実行されていない場合、その値は0x00000000です。プロセスが実行されている場合、その値は0xf7ff06a0になります。さらにデバッグを行うと、このアドレスは動的リンカーにあり、対応する関数は_dl_runtime_resolveです。

さて、あなたは何か考えましたか?すべての動的ライブラリ関数は、最初に呼び出されたときに、XXX @ plt-> public @ plt-> _dl_runtime_resolve呼び出し関係によるアドレス解決と再配置に使用されます。

そういえば、実際にはパズルに対する答えはありません。例としてprintf関数を考えてみましょう。
 

_dl_runtime_resolve是怎么知要查找printf函数的
_dl_runtime_resolve找到printf函数地址之后,它怎么知道回填到哪个GOT表项
到底_dl_runtime_resolve是什么时候被写到GOT表的

最初の2つの質問では、必要な情報は1つだけです。この情報は、関数に対応するxxx @ pltテーブルで非表示になっています。例として、printf @ pltを考えます。

printf@plt>:
   jmp *0x80496f8
   push $0x00
   jmp common@plt

2番目の命令は秘密です。各xxx @ pltの2番目の命令プッシュのオペランドは異なり、これは関数のIDと同等であり、ダイナミックリンカーはどの関数を解析するかを知ることができます。 。

そのような神はいますか?これは神ではなく、コンパイラリンカとダイナミックリンカによって意図的に配置された偶然の一致です。

readelf -r testコマンドを使用して、.rel.pltセクションに大きな秘密があるテスト実行可能ファイルの再配置情報を表示します
 

$ readelf -r test
....
Relocation section '.rel.plt' at offset 0x25c contains 3 entries:
 Offset     Info     Type             Sym.Value  Sym. Name
 080496f8   00000107 R_386_JUMP_SLOT  00000000   puts
 080496fc   00000207 R_386_JUMP_SLOT  00000000   __gmon_start__
 08049700   00000407 R_386_JUMP_SLOT 000000000   __libc_start_main    

プッシュルックPLT機能命令の動作回数: 
printf関数の対応する0x0のプッシュ 
gmon_start 0x8というプッシュに対応する 
対応プッシュ0x10の__libc_start_main

これらの3つのプッシュオペランドは、.rel.pltセクションの3つの関数のオフセットに正確に対応してます_dl_runtime_resolve関数では、オフセットと.rel.pltセクションの情報に基づいて、解決する関数がわかります。GOTエントリのアドレスである.rel.pltの左端にあるオフセットフィールドをもう一度見てください。つまり、_dl_runtime_resolveがシンボルの解決を完了した後、書き戻されたスペースを再配置します。

3番目の質問:_dl_runtime_resolveがGOTテーブルに書き込まれたのはいつですか? 
答えは簡単です.exeによってLinuxカーネルがロードされた後、実行可能ファイルは直接実行されませんが、最初に動的リンカー(ld-linux-XXX)にジャンプして実行されます。_dl_runtime_resolveアドレスをld-linux-XXXのGOTエントリに書き込みます。

実際、i386アーキテクチャでは、GOTエントリに_dl_runtime_resolveアドレスが事前に書き込まれるだけでなく、GOTエントリを占有する各関数に加えて、GOTエントリは3つのパブリックエントリを保持します。 3つのアイテム、個別に保存:

got [0]:ELF動的セグメント(.dynamicセグメント)のロードアドレス 
got [1]:ELFのlink_mapデータ構造記述子の 
アドレスgot [2]:_dl_runtime_resolve関数のアドレス

ELFをロードした後、ダイナミックリンカーはこれら3つのアドレスをGOTテーブルの最初の3つの項目に書き込みます。 
実際、上記のパブリックplt命令には分析されないオペランドがあります。実際、これはgot [1](このELFのlink_map)のアドレスです。これは、link_map構造体と.rel.pltセクションのオフセットを組み合わせるだけで、エルフの.rel.pltエントリを実際に見つけてください。

関心のある読者はgdbを使用できます。main関数が実行されたら、GOTテーブルの3つのデータを調べて確認します。

ここで、PLTとGOTのメカニズムをより明確に理解していますか?最後の記事では、グラフィック構造を使用して、PLT / GOTメカニズム全体をつなぎ合わせます。
 

元の記事を25件公開 賞賛された8件 20,000回以上の閲覧

おすすめ

転載: blog.csdn.net/boazheng/article/details/104316998