Подтвердить что ты не робот

Что здесь означает @plt?

0x00000000004004b6 <main+30>:   callq  0x400398 <[email protected]>

Кто-нибудь знает?

UPDATE

Почему два disas printf дают мне другой результат?

(gdb) disas printf
Dump of assembler code for function [email protected]:
0x0000000000400398 <[email protected]+0>:  jmpq   *0x2004c2(%rip)        # 0x600860 <_GLOBAL_OFFSET_TABLE_+24>
0x000000000040039e <[email protected]+6>:  pushq  $0x0
0x00000000004003a3 <[email protected]+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>
4b9b3361

Ответ 1

Это способ получить исправления кода (корректируя адреса в зависимости от места расположения кода в виртуальной памяти, которые могут различаться в разных процессах) без необходимости поддерживать отдельную копию кода для каждого процесса. PLT - это таблица связывания процедур, одна из структур, которая упрощает использование динамической загрузки и связывания.

[email protected] - это на самом деле небольшая заглушка, которая (в конце концов) вызывает настоящую функцию printf, изменяя способ ускорения последующих вызовов.

Реальная функция printf может быть отображена в любое место в данном процессе (виртуальное адресное пространство), как и код, который пытается ее вызвать.

Таким образом, чтобы разрешить правильное совместное использование кода вызывающего кода (слева внизу) и вызываемого кода (справа внизу), вы не хотите применять какие-либо исправления непосредственно к вызывающему коду, поскольку это ограничит его расположение в другие процессы.

Таким образом, PLT - это меньшая область, специфичная для процесса, с надежно рассчитанным адресом во время выполнения, который не распределяется между процессами, поэтому любой конкретный процесс может изменить его по своему усмотрению, без негативных последствий.


Изучите следующую диаграмму, которая показывает и ваш код, и код библиотеки, сопоставленный с разными виртуальными адресами в двух разных процессах, 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 сопоставляется с фиксированным местоположением. В вашем сценарии он расположен относительно текущего счетчика программы, о чем свидетельствует ваш поиск относительно счетчика программ:

<[email protected]+0>: jmpq  *0x2004c2(%rip)  ; 0x600860 <_GOT_+24>

Я просто использовал фиксированную адресацию, чтобы упростить пример.

Первоначальный способ совместного использования кода означал, что они должны быть загружены в одну и ту же область памяти в каждом виртуальном адресном пространстве каждого процесса, который его использовал. Либо это, либо он не может быть разделен, так как сам процесс исправления одной общей копии для одного процесса полностью заполняет другие процессы, в которых он был сопоставлен с другим местоположением.

Используя независимый от позиции код вместе с PLT и глобальной таблицей смещений (GOT), первый вызов функции [email protected] (в PLT) является многоступенчатой операцией, в которой выполняются следующие действия:

  • Вы вызываете [email protected] в PLT.
  • Он вызывает версию GOT (через указатель), которая первоначально указывает на некоторый код настройки в PLT.
  • Этот установочный код загружает соответствующую разделяемую библиотеку, если это еще не сделано, затем модифицирует указатель GOT так, чтобы последующие обращения непосредственно к реальному printf а не к установочному коду PLT.
  • Затем он вызывает загруженный код printf по правильному адресу для этого процесса.

При последующих вызовах, поскольку указатель GOT был изменен, многоэтапный подход упрощен:

  • Вы вызываете [email protected] в PLT.
  • Он вызывает версию GOT (через указатель), которая теперь указывает на реальный printf.

Хорошую статью можно найти здесь, подробно описывая, как glibc загружается во время выполнения.

Ответ 2

Не уверен, но, вероятно, то, что вы видели, имеет смысл. При первом запуске команды disas printf еще не вызывается, поэтому он не разрешен. Когда ваша программа вызывает метод printf при первом обновлении GOT, и теперь printf разрешается, и GOT указывает на реальную функцию. Таким образом, следующий вызов команды disas показывает реальную сборку printf.