GCC – 如何重新排列堆栈?

我尝试构建一个使用pthreads和__m128 SSE类型的应用程序。 根据GCC手册,默认堆栈对齐是16个字节。 为了使用__m128,要求是16字节对齐。

我的目标CPU支持SSE。 我使用的GCC编译器不支持运行时堆栈重组(例如-mstackrealign)。 我不能使用任何其他GCC编译器版本。

我的测试应用程序如下:

#include  #include  void *f(void *x){ __m128 y; ... } int main(void){ pthread_t p; pthread_create(&p, NULL, f, NULL); } 

应用程序生成exception并退出。 经过简单的调试(printf“%p”,&y)后,我发现变量y不是16字节对齐的。

我的问题是:如何在不使用任何GCC标志和属性(它们没有帮助)的情况下正确地重新对齐堆栈(16字节)? 我应该在这个线程函数f()中使用GCC内联汇编程序吗?

在堆栈上分配一个比sizeof(__m128)大15个字节的数组,并使用该数组中的第一个对齐地址。 如果需要多个,请将它们分配在一个具有15个字节边距的数组中进行对齐。

我不记得分配一个unsigned char数组是否使编译器不受严格别名优化的影响,或者它是否仅适用于其他方式。

 #include  void *f(void *x) { unsigned char y[sizeof(__m128)+15]; __m128 *py = (__m128*) (((uintptr_t)&y) + 15) & ~(uintptr_t)15); ... } 

这不应该首先发生,但要解决问题,您可以尝试:

 void *f(void *x) { __m128 y __attribute__ ((aligned (16))); ... } 

另一个解决方案是使用填充函数,它首先对齐堆栈然后调用f 。 因此,不是直接调用f ,而是调用pad ,首先pad堆栈,然后使用对齐的堆栈调用foo

代码如下所示:

 #include  #include  #define ALIGNMENT 16 void *f(void *x) { __m128 y; // other stuff } void * pad(void *val) { unsigned int x; // to get the current address from the stack unsigned char pad[ALIGNMENT - ((unsigned int) &x) % ALIGNMENT]; return f(val); } int main(void){ pthread_t p; pthread_create(&p, NULL, pad, NULL); } 

我已经解决了这个问题。 这是我的解决方案:

 void another_function(){ __m128 y; ... } void *f(void *x){ asm("pushl %esp"); asm("subl $16,%esp"); asm("andl $-0x10,%esp"); another_function(); asm("popl %esp"); } 

首先,我们将堆栈增加16个字节。 其次,我们使最不重要的半字节等于0x0。 我们使用push / pop操作数保留堆栈指针。 我们调用另一个函数,它具有16字节对齐的所有自己的局部变量。 所有嵌套函数的局部变量也都是16字节对齐的。

它的工作原理!

抱歉复活旧线程……

对于那些使用比OP更新的编译器的人,OP提到了一个-mstackrealign选项,它引导我__attribute__((force_align_arg_pointer)) 。 如果您的function正在优化以使用SSE,但%ebp未对齐,那么如果需要,这将透明地执行运行时修复。 我还发现这只是i386上的一个问题。 x86_64 ABI保证参数对齐到16个字节。

__attribute__((force_align_arg_pointer)) void i_crash_when_not_aligned_to_16_bytes() { ... }

对于那些可能想要了解更多信息的人来说,这篇文章很酷: http : //wiki.osdev.org/System_V_ABI