В чем разница между __i686.get_pc_thunk и __x86.get_pc_thunk? - PullRequest
1 голос
/ 22 апреля 2020

Эти вспомогательные функции используются G CC и Clang в 32-разрядном независимом от позиции коде x86 для получения текущего адреса выполнения в регистр, например:

call    __i686.get_pc_thunk.bx
addl    $_GLOBAL_OFFSET_TABLE_, %ebx
movl    $2, 4(%esp)
leal    .LC0@GOTOFF(%ebx), %eax
movl    %eax, (%esp)
call    dlopen@PLT

Кажется, реализации эквивалентны:

__x86.get_pc_thunk.bx:
    movl    (%esp), %ebx
    ret

__i686.get_pc_thunk.bx:
    movl (%esp), %ebx
    ret

Есть ли какая-либо разница, кроме смены имени (кажется, i686 старше)? И есть ли причина для префикса i686 вместо i386?

Ответы [ 2 ]

1 голос
/ 24 апреля 2020

Итак, после некоторого изучения истории коммитов и отслеживания ошибок, я думаю, что я в основном понял это.

Долгое время go, glib c имел собственную обработку PI C код, который использовал шаблон вызова / поп для получения адреса GOT.

Примерно в 2002 , __i686.get_pc_thunk.*, который выполнял аналогичную задачу, был добавлен в G CC, изначально как внутренний символ.

Вскоре после этого также оказался в glib c, вероятно, чтобы избежать дублирования кода при компиляции с G CC.

Однако при сборке для Pentium 2 или более поздняя версия (-march=i686), G CC определил макрос препроцессора __i686=1, прервав компиляцию glib c кода-заглушки. Проблема была обнаружена довольно рано, но в течение нескольких лет glib c использовал различные обходные пути , чтобы справиться с этим.

В 2011 году (G CC 4.7?) имя было изменено на __x86.get_pc_thunk.* и glib c добавило несколько проверок для использования подходящего имени. В конце концов поддержка старых версий G CC была прекращена вместе со старым именем. И G CC, и glib c теперь используют только __x86.get_pc_thunk.* (хотя G CC также может генерировать версию для встроенного вызова / pop).

Итак, в итоге:

Между ними нет фактической разницы, смена имени является просто исторической из-за предопределенной коллизии макросов.

Ссылки:

https://gcc.gnu.org/git/?p=gcc.git&a=search&h=HEAD&st=commit&s=get_pc_thunk

https://sourceware.org/git/?p=glibc.git&a=search&st=commit&s=get_pc_thunk

https://sourceware.org/bugzilla/show_bug.cgi?id=411

https://sourceware.org/bugzilla/show_bug.cgi?id=4507

1 голос
/ 22 апреля 2020

Просто другой выбор имени, незначительный AFAIK.

i686 - это стандартное имя для 32-битного кода, использующее новые инструкции PPro, такие как CMOV и FCOMI, и 586 CMPGXCHG и CPUID. Современные дистрибутивы GNU / Linux обычно конфигурируют g cc, чтобы использовать его в качестве цели по умолчанию для -m32 32-битного кода вместо действительно базовой версии i386. например, g cc -v покажет i686-linux-gnu для 32-битной сборки G CC.

Обычно clang использует call next_insn / pop reg для чтения EIP в регистр. (Интересный факт: на самом деле не не нарушает предсказание обратного адреса на процессорах, отличных от оригинального Pentium-Pro или Via Nano3000: http://blog.stuffedcow.net/2018/04/ras-microbenchmarks/#call0 - особый случай процессоров call rel32=0 как не являясь реальным вызовом и не помещая адрес возврата в стек предикторов.)


get_pc_thunk.bx включает в себя имя регистра для возврата. Используется 32-битный код PI C использовать EBX только в качестве регистра указателя GOT, но теперь G CC может выбрать любой удобный регистр и выдать за него функцию thunk, например ....get_pc_thunk.ax, чтобы конечным функциям не приходилось сохранять / восстанавливать EBX.

P IE делает исполняемые файлы медленнее, возможно, на 15% для 32-битного кода против пары процентов для 64-битного кода. x86-64 имеет RIP-относительную адресацию, что устраняет необходимость в этих блоках. IMO 32-битный P IE не стоит своей цены, если только вам не нужно больше защищаться от атак ROP и Spectre, имея ASLR основного исполняемого файла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...