这一变化实际上打破了手写的asm,它调用了以前允许的ESP对齐小于16的C函数,但是这样的二进制文件可能会继续由GCC版本的默认值创建,当这一点被注意到时,GCC版本已经广泛使用。因此,改变ABI以匹配GCC的发布版本实际上正在做的事情并不是很好,但可能不那么糟糕。可执行文件将被限制为回调函数,或旧代码调用新代码的其他方式。(新代码调用旧代码是可以的,因为提供给予16字节对齐的调用方满足了较宽松的对齐要求。) 其他操作系统避免了这种破坏旧的二进制文件和手写asm的ABI更改崩溃。 请参阅https://sourceforge.net/p/fbc/bugs/659/了解一些历史,以及我在https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838#c91上的评论,以总结i386 GNU/Linux + GCC如何意外地进入一种情况的不幸历史,在这种情况下,对i386 System V ABI的向后不兼容更改是两害相权取其轻。
1条答案
按热度按时间e37o9pze1#
函数无法知道其他函数在内部会做什么,所以真正重要的是能够将库链接在一起并链接到可执行文件,它们同意调用约定/ ABI,并且ABI为调用者设置要求,为被调用者提供关于堆栈对齐的保证。(以及其他事情。)所以它不是“当使用SIMD指令时”,除非你的意思是“如果任何被调用者实际上确实依赖于ABI保证,例如,通过在其堆栈空间上使用SIMD加载或存储”。如 * glibc scanf Segmentation faults when called from a function that doesn't align RSP *
请参阅Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment?了解更多关于我在这个答案中提到的一些事情的细节。
64位模式:始终以16对齐:x86-64 System V和Windows x64 ABI都需要在
call
之前使用RSP%16 == 0
,因此在函数条目上保证RSP % 16 == 8
。这对于16字节的向量已经足够了,但是需要alignas(32)
或更高的局部变量的函数仍然需要自己完成。32位模式:非Linux上的4字节对齐。只有Linux上使用的i386 System V ABI版本需要16字节对齐(在调用前为
ESP % 16 == 0
,在函数入口时为ESP % 16 == 12
。)甚至使用SysV ABI的其他操作系统也保留了旧的4字节对齐要求,没有采用该更改(例如 *BSD,也许还有MacOSX,在它只支持64位之前)。Windows上的32位代码也只需要/保证4字节对齐。如果你(或编译器)想要16字节对齐的局部变量(例如溢出/重新加载
__m128
),该函数需要额外的指令。(通常将EBP设置为帧指针和and esp, -16
,类似于为VLA分配空间。)**ABI要求在GNU/Linux的32位模式下的所有函数中保持16字节的堆栈对齐,这是GCC的一个意外。**当他们注意到
-mpreferred-stack-boundary=4
让GCC * 假设 * 对齐并使代码在没有对齐的情况下调用时出错的错误时,有很多二进制文件依赖于它,包括在像RedHat Enterprise Linux(RHEL)这样变化缓慢的主要发行版中。摆脱这种情况的最好方法是改变ABI以要求未来,所以-mpreferred-stack-boundary=4
成为ABI的一部分,而不仅仅是一个乐观的性能调整,就像我认为它是想象的那样,当它被设置为默认值时。这一变化实际上打破了手写的asm,它调用了以前允许的ESP对齐小于16的C函数,但是这样的二进制文件可能会继续由GCC版本的默认值创建,当这一点被注意到时,GCC版本已经广泛使用。因此,改变ABI以匹配GCC的发布版本实际上正在做的事情并不是很好,但可能不那么糟糕。可执行文件将被限制为回调函数,或旧代码调用新代码的其他方式。(新代码调用旧代码是可以的,因为提供给予16字节对齐的调用方满足了较宽松的对齐要求。)
其他操作系统避免了这种破坏旧的二进制文件和手写asm的ABI更改崩溃。
请参阅https://sourceforge.net/p/fbc/bugs/659/了解一些历史,以及我在https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838#c91上的评论,以总结i386 GNU/Linux + GCC如何意外地进入一种情况的不幸历史,在这种情况下,对i386 System V ABI的向后不兼容更改是两害相权取其轻。