代码之家  ›  专栏  ›  技术社区  ›  psihodelia

如何重新排列堆栈?

  •  7
  • psihodelia  · 技术社区  · 14 年前

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

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

    我的测试应用程序看起来像:

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

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

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

    5 回复  |  直到 7 年前
        1
  •  7
  •   Pascal Cuoq    11 年前

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

    我不记得是否分配了 unsigned char 如果编译器对数组进行了严格的别名优化,或者数组只起到相反的作用,那么数组可以使您安全地进行别名优化。

    #include <stdint.h>
    
    void *f(void *x)
    {
       unsigned char y[sizeof(__m128)+15];
       __m128 *py = (__m128*) (((uintptr_t)&y) + 15) & ~(uintptr_t)15);
       ...
    }
    
        2
  •  3
  •   Paul R    14 年前

    这本来就不应该发生,但要解决这个问题,您可以尝试:

    void *f(void *x)
    {
       __m128 y __attribute__ ((aligned (16)));
       ...
    }
    
        3
  •  1
  •   ablaeul    14 年前

    另一个解决方案是,使用padding函数,它首先对齐堆栈,然后调用 f . 所以不要打电话 f 你直接打电话 pad ,它先填充堆栈,然后调用 foo 有一个对齐的堆栈。

    代码如下所示:

    #include <xmmintrin.h>
    #include <pthread.h>
    
    #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);
    }
    
        4
  •  0
  •   psihodelia    14 年前

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

    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字节对齐。

    而且有效!

        5
  •  0
  •   AStupidNoob    7 年前

    很抱歉让一根旧线复活…

    对于那些使用比op更新的编译器的人,op提到 -mstackrealign 选项,这使我 __attribute__((force_align_arg_pointer)) . 如果您的函数被优化为使用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