Порядок, в котором связаны библиотеки, имеет значение, если библиотеки зависят друг от друга. В общем случае, если библиотека A
зависит от библиотеки B
, тогда libA
ДОЛЖЕН появляться перед libB
в флагах компоновщика.
Например:
// B.h
#ifndef B_H
#define B_H
struct B {
B(int);
int x;
};
#endif
// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}
// A.h
#include "B.h"
struct A {
A(int x);
B b;
};
// A.cpp
#include "A.h"
A::A(int x) : b(x) {}
// main.cpp
#include "A.h"
int main() {
A a(5);
return 0;
};
Создайте библиотеки:
$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o
ar: creating libB.a
a - B.o
Скомпилируйте:
$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out
Чтобы повторить снова, порядок имеет значение!
Существует целый блог, названный в честь этой инструкции. И первое сообщение описывает причину этого: http://repzret.org/p/repzret/
В принципе, в предсказании ветви AMD произошла ошибка, когда один -byte ret
сразу же следует за условным прыжком, как в коде, который вы цитировали (и в нескольких других ситуациях), и обходным путем было добавить префикс rep
, который игнорируется CPU, но фиксирует штраф предиктора.
Как указывает ответ Триллиана, 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
. Однако в тех случаях, когда это приводит к дополнительному заполнению, это будет наихудший случай, когда требуется 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/<pid>/map_files/
недоступен для владельца процесса, но /proc/<pid>/maps
. Если UID = корневой процесс (например, из двоичного файла suid-root) mmap(2)
sa 0666, который находится в каталоге 0700, то делает setuid(nobody)
, любой, кто работает с этим двоичным файлом, может обойти ограничение доступа, наложенное отсутствием x for other
разрешение на каталог.
По-видимому, некоторые предсказатели ветвлений процессоров AMD плохо себя ведут, когда цель или провал ветки является инструкцией ret
, и добавление префикса rep
позволяет избежать этого.
Что касается значения rep ret
, эта инструкция не упоминается в Справочном наборе инструкций Intel , а документация по rep
не очень полезна:
Поведение префикс REP не определен при использовании с нестроковыми инструкциями.
blockquote>Это означает, что по крайней мере,
rep
не должен вести себя повторяющимся образом.Теперь из набора инструкций AMD (1.2.6 Repeat Prefixes):
Префиксы должны использоваться только с такими строковыми инструкциями.
Как правило, префиксы повтора должны использоваться только в строковых инструкциях, перечисленных в таблицах 1-6, 1-7 и 1-8 выше [которые не содержат ret].
blockquote>Так что это действительно похоже на неопределенное поведение, но можно предположить, что на практике e, процессоры просто игнорируют префиксы
rep
в инструкцияхret
.