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
.