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