Що тут означає @plt?


76
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>

Звідки взявся той перший вихідний рядок? objdumpЯ уявляю?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Відповіді:


121

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


1
Чому двоє disas printfдають мені різний результат?
gdb

2
Заглушка до того, як я наберу r, а інша - післяbreak
gdb

Його можна відтворити за допомогою будь-якого виконуваного файлу з gdb.
gdb

3
Це таблиця зв’язку процедур. Я оновлю додаткову інформацію.
paxdiablo

1
@ Луїс, я не впевнений, що є. Я думаю, що код встановлення фактично завантажує спільну бібліотеку як частину її процесу (а ASLR означає, що вона буде завантажуватися за якоюсь довільною адресою), тому адреса буде фактично невідома до того моменту.
paxdiablo

6

Не впевнений, але, мабуть, побачене має сенс. Під час першого запуску команди катастрофи printf ще не викликається, тому її не вирішено. Як тільки ваша програма викликає метод printf при першому оновленні GOT, і тепер printf вирішується, і GOT вказує на реальну функцію. Таким чином, під час наступного виклику команди crash відображається справжня збірка printf.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.