web-dev-qa-db-ja.com

ここで@pltはどういう意味ですか?

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>
69
gdb

これは、プロセスごとにコードのコピーを個別に保持することなく、コードの修正(コードが仮想メモリのどこにあるかに基づいてアドレスを調整します。プロセスによって異なる場合があります)を取得する方法です。 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呼び出しはマルチステージになります操作。次のアクションが実行されます。

  • PLTでprintf@pltを呼び出します。
  • GOTバージョンを(ポインターを介して)呼び出します。これは最初はPLTのセットアップコードを指します。
  • このセットアップコードは、まだ行われていない場合は関連する共有ライブラリをロードし、GOTポインターを変更して、後続のPLTセットではなく実際のprintfへの呼び出しを行います。アップコード。
  • 次に、このプロセスの正しいアドレスでロードされたprintfコードを呼び出します。

後続の呼び出しでは、GOTポインターが変更されているため、マルチステージアプローチが簡素化されます。

  • PLTでprintf@pltを呼び出します。
  • (ポインターを介して)GOTバージョンを呼び出しますが、これはrealprintfを指します。

良い記事は here にあり、実行時にglibcがどのようにロードされるかを詳しく説明しています。

109
paxdiablo

確かではありませんが、おそらくあなたが見たことは理にかなっています。 disasコマンドを初めて実行するとき、printfはまだ呼び出されていないため、解決されません。 GOTが初めて更新されたときにプログラムがprintfメソッドを呼び出すと、今度はprintfが解決され、GOTは実際の関数を指します。したがって、次にdisasコマンドを呼び出すと、実際のprintfアセンブリが表示されます。

4
rkachach