Можно ли получить доступ к таблице привязки процедур в NASM? - PullRequest
0 голосов
/ 17 апреля 2020

В отчете, который я получил от objconv Агнера Фога, я вижу предлагаемые ошибки для исправления в разделе .plt (таблица привязки процедур), например:

SECTION .plt    align=16 execute                        ; section number 9, code

?_001:  push    qword [rel ?_086]                       ; 10F0 _ FF. 35, 00201F12(rel)
    jmp     near [rel ?_087]                        ; 10F6 _ FF. 25, 00201F14(rel)

; Filling space: 4H
; Filler type: Multi-byte NOP
;       db 0FH, 1FH, 40H, 00H

ALIGN   8
?_002:  jmp     near [rel ?_088]                        ; 1100 _    FF. 25, 00201F12(rel)

; Note: Immediate operand could be made smaller by sign extension
        push    0                                       ; 1106 _ 68, 00000000
; Note: Immediate operand could be made smaller by sign extension
        jmp     ?_001                                   ; 110B _ E9, FFFFFFE0

В двух случаях (и других в коде) не показано) это предполагает, что «непосредственный операнд может быть уменьшен путем расширения знака». Как я могу получить доступ к таблице связи процедур, чтобы внести эти изменения? Является ли это возможным?

1 Ответ

3 голосов
/ 17 апреля 2020

PLT-заглушки преднамеренно используют более длинные значения немедленных переходов и смещений, чем необходимо, поэтому они имеют постоянный размер, даже если у вас достаточно записей PLT, что для jmp ?_001 в проходном пути требуется rel32 для достижения из более поздних записей PLT .

Они автоматически генерируются компоновщиком при связывании кода, который использовал call printf wrt ..plt, или при связывании не-P IE, который только что использовал call printf.

Вы можете полностью избежать PLT, написав call [rel printf wrt ..got], как это делает G CC, когда вы компилируете с -fno-plt. Это делает раннее связывание (а не ленивое), разрешая все GOT при запуске до _start , См. Невозможно вызвать C стандартную библиотечную функцию для 64-битного Linux из кода сборки (yasm) . Использование default rel позволяет вам пропустить явную rel часть режима адресации. Эквивалентный синтаксис AT & T: call *printf@GOTPCREL(%rip)


Я не знаю, является ли этот массив фиксированных значений заглушек PLT строго необходимым для чего-либо во время выполнения. например, lazy dynamici c связывание только изменяет GOT, а не сам PLT, потому что современные PLT используют косвенный переход. push 0 выдвигает индекс записи PLT, но я не думаю, что кто-то использует его для фактического определения адреса машинного кода этой заглушки PLT, а только для индексации записи GOT.

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

Мне кажется, Вспомните исторически, рассматривая jmp rel32 как первую инструкцию о заглушках PLT в 32-битном коде, а не jmp [mem], но, возможно, это было лишь предположение о том, как работают заглушки PLT, прежде чем я действительно знал много. Если бы они когда-либо работали таким образом, ленивая динамика c, связывающая , изменила бы сам фактический PLT, чтобы зафиксировать цель относительного перехода, поэтому индексация машинного кода записи PLT была бы важна. (И поэтому важно иметь фиксированную ширину каждой записи).

Но даже 32-битный код в наши дни не использует jmp rel32, поэтому заглушки PLT доступны только для чтения. А в 64-битном коде jmp rel32 может достигать только + -2 ГБ, поэтому его нельзя использовать для доступа к библиотекам, сопоставленным со случайным адресом.


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

Заполнение может быть хорошей вещью: слишком много jmp инструкций в одном 16-байтовом блоке кода это плохо для предсказателей ветвлений на некоторых процессорах. Но я думаю, что ограничение равно 3 или 4 прыжкам в 16-байтовом блоке машинного кода для некоторых AMD или Core 2, так что в любом случае 6-байтовый jmp [RIP+rel32] + 2-байтовый push imm8 + не будет достигнут 2 байта jmp rel8.

...