Це спосіб отримати виправлення коду (коригуючи адреси на основі того, де знаходиться код у віртуальній пам’яті, який може відрізнятися в різних процесах), не маючи необхідності зберігати окрему копію коду для кожного процесу. 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
завантажується час роботи.
objdump
Я уявляю?