Это способ получить исправления кода (корректировка адресов в зависимости от того, где находится код в виртуальной памяти, которое может отличаться в разных процессах), без необходимости поддерживать отдельную копию кода для каждого процесса. 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
objdump
Думаю? - person Ciro Santilli 新疆再教育营六四事件ۍ   schedule 31.05.2015