我的系统:在 x86_64 上运行的 Ubuntu 22.04.3。 GCC 版本 11.4.0
我读到 System V ABI 强制要求使用红色区域。来自 GCC 手册:
红色区域是 x86-64 ABI 强制规定的,它是超出堆栈指针位置的 128 字节区域,不会被信号或中断处理程序修改,因此可以用于临时数据而无需调整堆栈指针。标志
禁用此红色区域。-mno-red-zone
我的问题:
如果我在 gcc 中使用
-mno-red-zone
标志,红色区域是否仍然存在?
如果红色区域被“禁用”,这是否意味着我不再遵守系统 V ABI?
这会产生什么后果(不符合 System V ABI)?
即使您编译代码不使用它,红色区域仍然存在。
gcc -mno-red-zone
纯粹是一个代码生成选项,类似于-fno-omit-frame-pointer
,或MIPS GCC的-fno-delayed-branch
留下仅用NOP填充的延迟槽有用的说明。 gcc -mno-red-zone
只是避免依赖 RSP 下面的空间来保持其值,就像 GCC 在非叶函数中总是做的那样。
使用
-mno-red-zone
编译的代码与使用默认 -mred-zone
编译的代码是 ABI 兼容的。 您可以自由地混合这些选项并将生成的 .o
文件链接到同一个可执行文件/库中,或者链接到不同的库或无论如何。
内核的信号传递代码和其他任何代码仍然会保留 RSP 下面的 128 字节红色区域不变。它不知道也不关心当前正在执行的机器代码不会重新加载该数据(在这种情况下,因为编译器选择不在那里保留任何内容。)没有任何机制可以让可执行文件向内核发出它应该重新加载的信号。不使用替代堆栈传递信号时,不要在当前堆栈指针下方留下 128 字节的间隙。 (因为一次保存少量的堆栈空间并不重要,也不值得使用任何额外的内核代码来使其成为条件。信号处理程序不会嵌套得很深,如果有的话,所以它不会增加大量的总未使用空间。)
尽管它是一个
-m
选项,但它不必更改 ABI(就像 gcc -m32 -mregparm=3
所做的那样),只是不以利用 ABI 这部分优势的方式进行优化。
但是,内核代码通常不能使用红区(除非它使用 TSS 和 IDT 机制来使用备用堆栈进行中断,即使在内核模式下也是如此,而 Linux 则不然),因此必须使用
-mno-red-zone
进行编译以匹配该 ABI。 可能这就是为什么它是一个 -m
选项:您可以将其视为告诉 GCC,其目标 ABI 不提供红色区域。 x86-64 SysV with 红色区域可以运行为 x86 编译的代码-64 SysV 无红色区域,但反之则不然。
相关: