Что означает `rep ret`?

Я тестировал код на Visual Studio 2008 и заметил security_cookie . Я могу понять суть этого, но я не понимаю, какова цель этой инструкции.

  rep ret /* REP to avoid AMD branch prediction penalty */ 

Конечно, я могу понять комментарий 🙂 но что это за префикс exaclty в контексте с ret и что происходит, если ecx is! = 0? По-видимому, количество циклов из ecx игнорируется, когда я его отлаживаю, что и следовало ожидать.

Код, где я нашел это, был здесь (введенный компилятором для обеспечения безопасности):

 void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) { /* x86 version written in asm to preserve all regs */ __asm { cmp ecx, __security_cookie jne failure rep ret /* REP to avoid AMD branch prediction penalty */ failure: jmp __report_gsfailure } } 

Там есть целый блог, названный в честь этой инструкции. И первое сообщение описывает причину этого: http://repzret.org/p/repzret/

В принципе, в предикторе ветвления AMD возникла проблема, когда однобайтовый ret сразу же следовал за условным переходом, как в коде, который вы цитировали (и в нескольких других ситуациях), и обходным путем было добавить префикс rep , который игнорируется CPU, но фиксирует штраф предиктора.

По-видимому, некоторые предсказатели отраслевых процессоров AMD ведут себя плохо, когда целью или падением филиала является команда ret , и добавление префикса rep позволяет избежать этого.

Что касается значения rep ret , в инструкции по набору инструкций Intel не упоминается эта последовательность команд , и документация rep не очень помогает:

Поведение префикса REP не определено при использовании с нестроковыми инструкциями.

Это означает, что, по крайней мере, rep не должен вести себя повторяющимся образом.

Теперь из справочника набора инструкций AMD (1.2.6 Repeat Prefixes):

Префиксы должны использоваться только с такими строковыми инструкциями.

В общем случае префиксы повтора должны использоваться только в строковых инструкциях, перечисленных в таблицах 1-6, 1-7 и 1-8 выше [которые не содержат ret].

Так что это действительно похоже на неопределенное поведение, но можно предположить, что на практике процессоры просто игнорируют префиксы rep на инструкциях ret .

Как указывает ответ Триллиана, AMD K8 и K10 имеют проблему с предсказанием ветвей, когда ret является целью ветвления или следует условной ветви.

Руководство по оптимизации AMD для K10 (Barcelona) рекомендует 3-байтный ret 0 в тех случаях, который выдает нулевые байты из стека, а также возвращает. Эта версия значительно хуже, чем rep ret на Intel. По иронии судьбы, это также хуже, чем rep ret на более поздних процессорах AMD (Bulldozer и далее). Поэтому неплохо, что никто не изменил использование ret 0 основе обновления руководства AMD Family 10 для оптимизации.


Руководства для процессора предупреждают, что будущие процессоры могут по-разному интерпретировать комбинацию префикса и инструкции, которую он не модифицирует. Это верно в теории, но никто не собирается создавать процессор, который не может запускать много существующих двоичных файлов.

gcc по-прежнему использует rep ret по умолчанию (без -mtune=intel или -march=haswell или что-то еще). Таким образом, большинство бинарных файлов Linux имеют где-то repz ret .

gcc, вероятно, перестанет использовать rep ret в течение нескольких лет, как только K10 будет полностью устаревшим. Спустя еще 5 или 10 лет почти все двоичные файлы будут построены с использованием gcc более новой версии. Еще через 15 лет производитель ЦП может подумать о повторении последовательности байтов f3 c3 как (часть) другой инструкции.

По-прежнему будут существовать старые двоичные файлы с закрытым исходным кодом, использующие rep ret которые не имеют более свежих сборок, и что кто-то должен продолжать работать. Таким образом, какова бы ни была новая функция f3 c3 != rep ret , она должна быть отключена (например, с настройкой BIOS), и эта настройка действительно изменяет поведение инструкции-декодера, чтобы распознать f3 c3 как rep ret . Если эта обратная совместимость для устаревших двоичных файлов невозможна (потому что она не может быть эффективно реализована с точки зрения мощности и транзисторов), IDK, на какой временной шкале вы будете смотреть. Гораздо больше, чем 15 лет, если только это не было процессором только для части рынка.

Поэтому безопасно использовать rep ret , потому что все остальные уже делают это. Использование ret 0 – плохая идея. В новом коде может еще неплохо использовать rep ret еще пару лет. Вероятно, не так уж много процессоров AMD PhenomII по-прежнему вокруг, но они достаточно медленны без лишних ошибочных ошибок обратного адреса или проблемы с сетью.


Стоимость довольно маленькая. В большинстве случаев он не занимает лишнего места, потому что в любом случае за ним обычно следует nop padding. Однако в тех случаях, когда это приводит к дополнительному заполнению, это будет наихудший случай, когда требуется 15 бит заполнения для достижения следующей границы 16B. В этом случае gcc может выравниваться только на 8B. (с .p2align 4,,10; для выравнивания до 16B, если он будет принимать 10 или меньше nop-байтов, тогда .p2align 3 всегда будет соответствовать 8B. Используйте gcc -S -o- для вывода asm-выхода в stdout, чтобы увидеть, когда он делает это.)

Поэтому, если мы предположим, что один из 16 rep ret конечном итоге создаст дополнительное дополнение, в котором ret только что достигнет желаемого выравнивания, и что дополнительное заполнение идет до границы 8B, это означает, что каждый rep имеет среднюю стоимость 8 * 1 / 16 = половина байт.

rep ret не используется достаточно часто, чтобы скомпенсировать многое. Например, firefox со всеми библиотеками, которые он отобразил, имеет только ~ 9k экземпляров rep ret . Так что это около 4k байт, во многих файлах. (И меньше оперативной памяти, чем это, поскольку многие из этих функций в динамических библиотеках никогда не называются.)

 # disassemble every shared object mapped by a process. ffproc=/proc/$(pgrep firefox)/ objdump -d "$ffproc/exe" $(sudo ls -l "$ffproc"/map_files/ | awk '/\.so/ {print $NF}' | sort -u) | grep 'repz ret' -c objdump: '(deleted)': No such file # I forgot to restart firefox after the libexpat security update 9649 

Это считает rep ret во всех функциях во всех библиотеках, которые отображал firefox, а не только о функциях, которые он когда-либо звонил. Это несколько актуально, потому что более низкая плотность кода по функциям означает, что ваши вызовы распределены по большему количеству страниц памяти. ITLB и L2-TLB имеют ограниченное количество записей. Локальная плотность имеет значение для L1I $ (и uop-cache от Intel). Во всяком случае, rep ret оказывает очень незначительное влияние.

Мне потребовалась минута, чтобы подумать о причине, что /proc//map_files/ недоступен для владельца процесса, но /proc//maps . Если UID = корневой процесс (например, из двоичного файла suid-root) mmap(2) sa 0666, который находится в каталоге 0700, то делает setuid(nobody) , любой, кто работает с этим двоичным файлом, может обойти ограничение доступа, наложенное отсутствием x for other разрешений в каталоге.

  • Оказывание программы для конвейера в процессорах Intel Sandybridge
  • Изменение режима округления с плавающей запятой
  • что делает инструкция movsbl?
  • Можно ли сообщить предсказателю ветви, насколько вероятно, что он должен следовать за веткой?
  • Печать шестнадцатеричных цифр со сборкой
  • Для чего предназначен регистр «FS» / «GS»?
  • Что такое механизм стека в микроархитектуре Sandybridge?
  • Разница между JA и JG в сборке
  • Сборка, печать номер ascii
  • 16-разрядные режимы адресации NASM x86
  • Может ли x86's MOV быть «свободным»? Почему я не могу воспроизвести это вообще?
  • Давайте будем гением компьютера.