正如 Trillian 的回答所指出的,AMD K8 和 K10 在何时ret
是分支目标或遵循条件分支(作为贯穿目标)时存在分支预测问题。那是因为ret
只有 1 个字节长。
repz ret:为什么这么麻烦?有一些额外的细节,说明了为什么这给 K8 和巴塞罗那带来了困难的具体微架构原因。
避免将 1 字节ret
作为可能的分支目标:
AMD 的 K10 (Barcelona) 优化指南ret 0
在这些情况下建议使用 3 字节,这会从堆栈中弹出零字节并返回。该版本比rep ret
英特尔的版本差得多。具有讽刺意味的是,它也比后来的 AMD 处理器(Bulldozer 及更高版本)更糟糕。因此,根据 AMD 的 Family 10 优化指南更新,rep ret
没有人改变使用它是一件好事。ret 0
处理器手册警告说,未来的处理器可能会以不同的方式解释前缀和它不修改的指令的组合。这在理论上是正确的,但没有人会制造出不能运行大量现有二进制文件的 CPU。
gcc 仍然rep ret
默认使用(没有-mtune=intel
,-march=haswell
或其他东西)。所以大多数 Linux 二进制文件的repz ret
某个地方都有一个。
一旦 K10 彻底过时, gcc 可能会rep ret
在几年内停止使用。再过 5 年或 10 年,几乎所有的二进制文件都将使用更新的 gcc 构建。再过 15 年,CPU 制造商可能会考虑将f3 c3
字节序列重新用作不同指令的(一部分)。
仍然会有遗留的闭源二进制文件使用rep ret
没有更新的可用版本,但是有人需要继续运行。因此,无论新功能f3 c3 != rep ret
是什么的一部分,都需要禁用(例如,使用 BIOS 设置),并让该设置实际更改指令解码器行为以识别f3 c3
为rep ret
. 如果传统二进制文件的向后兼容性是不可能的(因为它不能在功率和晶体管方面有效地完成),IDK 你会看什么样的时间框架。比 15 年长得多,除非这只是部分市场的 CPU。
所以使用它是安全的rep ret
,因为其他人都已经在这样做了。使用ret 0
是个坏主意。rep ret
在新代码中,再使用几年可能仍然是一个好主意。周围可能没有太多 AMD PhenomII CPU,但它们足够慢,没有额外的返回地址错误预测或问题出在。
成本相当小。在大多数情况下,它最终不会占用任何额外的空间,因为nop
无论如何它通常都会跟着填充。但是,在确实导致额外填充的情况下,最坏的情况是需要 15B 的填充才能到达下一个 16B 边界。在这种情况下,gcc 只能对齐 8B。(.p2align 4,,10;
如果它需要 10 个或更少的 nop 字节,则与 16B 对齐,然后 a.p2align 3
始终与 8B 对齐。用于gcc -S -o-
将 asm 输出生成到 stdout 以查看它何时执行此操作。)
因此,如果我们估计 16 分之一的人rep ret
最终ret
会在 a 刚刚达到所需对齐的地方创建额外的填充,并且额外的填充达到 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)很重要。总之,rep ret
影响很小。
我花了一分钟才想到一个/proc/<pid>/map_files/
流程所有者无法理解的原因,但/proc/<pid>/maps
可以。如果 UID=root 进程(例如,来自 suid-root 二进制文件)mmap(2)
sa 0666 文件位于 0700 目录中,那么setuid(nobody)
运行该二进制文件的任何人都可以绕过由于目录权限不足而施加的访问限制x for other
。