【问题标题】:GCC function padding valueGCC 函数填充值
【发布时间】:2010-12-20 01:45:50
【问题描述】:

每当我编译启用优化的 C 或 C++ 代码时,GCC 都会将函数对齐到 16 字节边界(在 IA-32 上)。如果函数小于 16 个字节,GCC 会用一些字节填充它,这似乎根本不是随机的:

19:   c3                      ret
1a:   8d b6 00 00 00 00       lea    0x0(%esi),%esi

它似乎总是8d b6 00 00 00 00 ...8d 74 26 00

函数填充字节有什么意义吗?

【问题讨论】:

  • 我的理解是/曾经是 GCC 使用 0 进行函数对齐填充,但由于我无法指出可能不是很有帮助的来源...

标签: c++ c gcc


【解决方案1】:

填充是由汇编程序创建的,而不是 gcc。它只看到.align 指令(或等效指令)并且不知道要填充的空间是在函数内部(例如循环对齐)还是在函数之间,因此它必须插入某种NOPs。现代 x86 汇编器使用尽可能大的 NOP 操作码,目的是在填充用于循环对齐时花费尽可能少的周期。

就个人而言,我非常怀疑对齐作为一种优化技术。我从来没有见过它有多大帮助,而且通过极大地增加总代码大小(和缓存利用率)肯定会受到伤害。如果您使用-Os 优化级别,默认情况下它是关闭的,因此无需担心。否则,您可以使用正确的 -f 选项禁用所有对齐。

【讨论】:

  • 看起来是正确的。 -S 输出没有填充无操作。
【解决方案2】:

汇编器首先看到.align 指令。由于不知道这个地址是否在函数体内,所以不能输出NULL0x00字节,必须生成NOPs(0x90)。

但是:

lea    esi,[esi+0x0] ; does nothing, psuedocode: ESI = ESI + 0

执行的时钟周期少于

nop
nop
nop
nop
nop
nop

如果这段代码恰好落在函数体中(例如,循环对齐),lea 版本会快得多,同时仍然“什么都不做”。

【讨论】:

    【解决方案3】:

    指令lea 0x0(%esi),%esi 只是将%esi 中的值加载到%esi - 它是无操作的(或NOP),这意味着如果它被执行,它将没有任何效果。

    这恰好是一条指令,6 字节 NOP。 8d 74 26 00 只是同一条指令的 4 字节编码。

    【讨论】:

    • 但这不是一条可达指令(在ret之后),是吗?
    • @Alex B:无法直接访问(但可以跳转到)。通常有理由在函数内产生可执行的填充,因此 GCC 很可能只是为函数的结尾重用了相同的填充算法。
    猜你喜欢
    • 1970-01-01
    • 2014-06-07
    • 1970-01-01
    • 2021-10-04
    • 1970-01-01
    • 2020-09-02
    • 2018-11-06
    • 2015-03-06
    • 2021-09-21
    相关资源
    最近更新 更多