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

0x00000000004004b6 <main+30>:   callq  0x400398 <printf@plt>

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

ОБНОВЛЕНИЕ

Почему два 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>

person gdb    schedule 29.03.2011    source источник
comment
Откуда взялась эта первая строка вывода? objdump Думаю?   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 31.05.2015


Ответы (2)


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

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

<printf@plt+0>: jmpq  *0x2004c2(%rip)  ; 0x600860 <_GOT_+24>

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

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

При использовании независимого от позиции кода вместе с PLT и глобальной таблицей смещений (GOT) первый вызов функции printf@plt (в PLT) является многоступенчатой ​​операцией, в которой следующие действия происходить:

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

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

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

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

person paxdiablo    schedule 29.03.2011
comment
Почему два disas printf дают разный результат? - person gdb; 29.03.2011
comment
Заглушка стоит до того, как я наберу r, а другая - после break. - person gdb; 29.03.2011
comment
Его можно воспроизвести с любым исполняемым файлом с помощью gdb. - person gdb; 29.03.2011
comment
Я много раз пытался прочитать эту статью, но с трудом понял ... Что за plt ?? - person gdb; 30.03.2011
comment
Это таблица привязки процедур. Я обновлю некоторую дополнительную информацию. - person paxdiablo; 30.03.2011
comment
@paxdiablo, спасибо! Но одно сомнение, The offset, $0×10, that was pushed onto the stack tells the linker code the offset of the symbol in the relocation table, однако в GOT нет смещения $ 0x10 ... как оно на самом деле совпадает? - person gdb; 30.03.2011
comment
@paxdiablo Есть ли способ принудительно зафиксировать запись GOT для конкретного вызова функции при загрузке изображения, чтобы при первом вызове версия GOT указывала на реальную функцию? - person LuisABOL; 25.02.2013
comment
@ Луис, я не уверен, что есть. Я думаю, что установочный код на самом деле загружает разделяемую библиотеку как часть своего процесса (а ASLR означает, что он будет загружаться по произвольному адресу), поэтому адрес фактически не будет известен до тех пор, пока не будет получен этот адрес. - person paxdiablo; 28.12.2016

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

person rkachach    schedule 04.09.2012