0x00000000004004b6 <main+30>: callq 0x400398 <printf@plt>
誰でも知っていますか?
[〜#〜] update [〜#〜]
なぜ2つのdisas printf
別の結果をくれますか?
(gdb) disas printf
Dump of assembler code for function printf@plt:
0x0000000000400398 <printf@plt+0>: jmpq *0x2004c2(%rip) # 0x600860 <_GLOBAL_OFFSET_TABLE_+24>
0x000000000040039e <printf@plt+6>: pushq $0x0
0x00000000004003a3 <printf@plt+11>: jmpq 0x400388
(gdb) disas printf
Dump of assembler code for function printf:
0x00000037aa44d360 <printf+0>: sub $0xd8,%rsp
0x00000037aa44d367 <printf+7>: mov %rdx,0x30(%rsp)
0x00000037aa44d36c <printf+12>: movzbl %al,%edx
0x00000037aa44d36f <printf+15>: mov %rsi,0x28(%rsp)
0x00000037aa44d374 <printf+20>: lea 0x0(,%rdx,4),%rax
0x00000037aa44d37c <printf+28>: lea 0x3f(%rip),%rdx # 0x37aa44d3c2 <printf+98>
これは、プロセスごとにコードのコピーを個別に保持することなく、コードの修正(コードが仮想メモリのどこにあるかに基づいてアドレスを調整します。プロセスによって異なる場合があります)を取得する方法です。 PLTは、プロシージャのリンクテーブルであり、動的なロードとリンクを使いやすくする構造の1つです。
printf@plt
は実際には小さなスタブであり、(最終的に)実際のprintf
関数を呼び出し、その後の呼び出しを高速化するために途中で変更します。
realprintf
関数は、特定のプロセス(仮想アドレス空間)のanyの場所にマッピングできます。それを呼び出そうとしています。
したがって、呼び出し元コード(下の左側)と呼び出し先コード(下の右側)の適切なコード共有を可能にするために、呼び出し元のコードにフィックスアップを直接適用したくありません。 otherプロセス。
したがって、PLT
は、isn共有されていない、実行時に信頼性のある計算が行われるアドレスの小さいプロセス固有エリアですプロセス間で、任意のプロセスは自由に変更できますが、悪影響はありません。
2つの異なるプロセスProcA
およびProcB
の異なる仮想アドレスにマップされたコードとライブラリコードの両方を示す次の図を調べます。
Address: 0x1234 0x9000 0x8888
+-------------+ +---------+ +---------+
| | | Private | | |
ProcA | | | PLT/GOT | | |
| Shared | +---------+ | Shared |
========| application |=============| library |==
| code | +---------+ | code |
| | | Private | | |
ProcB | | | PLT/GOT | | |
+-------------+ +---------+ +---------+
Address: 0x2020 0x9000 0x6666
この特定の例は、PLTが固定された場所にマッピングされる単純なケースを示しています。 yourシナリオでは、プログラムカウンター相対ルックアップによって証明されるように、現在のプログラムカウンターに相対して配置されます。
<printf@plt+0>: jmpq *0x2004c2(%rip) ; 0x600860 <_GOT_+24>
例を簡単にするために、固定アドレスを使用しました。
コードを共有するoriginalの方法は、使用するすべてのプロセスの各仮想アドレス空間のsameメモリ位置にロードする必要があることを意味しましたそれ。あるプロセスのsingle共有コピーを修正する行為は、別の場所にマップされた他のプロセスを完全に詰め込むため、それまたは共有できませんでした。
PLTおよびグローバルオフセットテーブル(GOT)とともに位置に依存しないコードを使用することにより、(PLT内の)関数printf@plt
へのfirst呼び出しはマルチステージになります操作。次のアクションが実行されます。
printf@plt
を呼び出します。printf
への呼び出しを行います。アップコード。printf
コードを呼び出します。後続の呼び出しでは、GOTポインターが変更されているため、マルチステージアプローチが簡素化されます。
printf@plt
を呼び出します。printf
を指します。良い記事は here にあり、実行時にglibc
がどのようにロードされるかを詳しく説明しています。
確かではありませんが、おそらくあなたが見たことは理にかなっています。 disasコマンドを初めて実行するとき、printfはまだ呼び出されていないため、解決されません。 GOTが初めて更新されたときにプログラムがprintfメソッドを呼び出すと、今度はprintfが解決され、GOTは実際の関数を指します。したがって、次にdisasコマンドを呼び出すと、実際のprintfアセンブリが表示されます。