OS X - x64:堆栈不是 16 字节对齐错误

Posted

技术标签:

【中文标题】OS X - x64:堆栈不是 16 字节对齐错误【英文标题】:OS X - x64: stack not 16 byte aligned error 【发布时间】:2017-09-07 08:32:44 【问题描述】:

我知道 OS X 是 16 字节堆栈对齐,但我真的不明白为什么它会在这里导致错误。

我在这里所做的只是将一个对象大小(为 24)传递给 %rdi,然后调用 malloc。这个错误是否意味着我必须要求 32 个字节?

错误信息是:

libdyld.dylib`stack_not_16_byte_aligned_error: -> 0x7fffc12da2fa : movdqa %xmm0, (%rsp) 0x7fffc12da2ff : int3

libdyld.dylib`_dyld_func_lookup: 0x7fffc12da300 : pushq %rbp 0x7fffc12da301 : movq %rsp, %rbp

代码如下:

Object_copy:
    pushq %rbp
    movq %rbp, %rsp

    subq $8, %rsp
    movq %rdi, 8(%rsp)          # save self address
    movq obj_size(%rdi), %rax   # get object size
    imul $8, %rax          
    movq %rax, %rdi 
    callq _malloc             <------------------- error in this call

    # rsi old object address
    # rax new object address
    # rdi object size, mutiple of 8

    # rcx temp reg

    # copy object tag
    movq 0(%rsi), %rcx
    movq %rcx, 0(%rax)

    # set rdx to counter, starting from 8
    movq $8, %rdx

    # add 8 to object size, since we are starting from 8
    addq $8, %rdi

    start_loop:
        cmpq %rdx, %rdi
        jle end_loop

        movq (%rdx, %rsi, 1), %rcx
        movq %rcx, (%rdx, %rax, 1)

        addq $8, %rdx
        jmp start_loop

    end_loop:
        leave 
        ret



Main_protoObj:
    .quad    5                          ; object tag
    .quad    3                          ; object size
    .quad    Main_dispatch_table        ; dispatch table

_main:
    leaq Main_protoObj(%rip), %rdi
    callq Object_copy                # copy main proto object
    subq $8, %rsp                    # save the main object on the stack
    movq %rax, 8(%rsp)
    movq %rax, %rdi                 # set rdi point to SELF
    callq Main_init
    callq Main_main

    addq $8, %rsp                    # restore stack

    leaq _term_msg(%rip), %rax
    callq _print_string

【问题讨论】:

在 64 位代码 CALL 之前的点 RSP 必须能被 16 整除。在堆栈上并将控制传输到例程,堆栈变得错位 8 个字节(现在返回堆栈上的值)。 pushq %rbp 在堆栈上放置了额外的 8 个字节,因此堆栈现在与 16 字节边界对齐。然后您执行subq $8, %rsp,这意味着 RSP 现在再次错位 8。然后你用未对齐的堆栈调用malloc。而不是 subq $8, %rsp 尝试 subq $16, %rsp 保持 16 字节对齐。 @MichaelPetch 嘿,迈克尔,非常感谢您的回答,它真的帮了我很大的忙。您是否介意以实际答案的形式发表您的评论,以便更明显地为其他遇到此问题的人解决此问题?如果您不想这样做,我可以这样做:) 如果你愿意,你可以拿走你发现的东西并自己回答你的问题。 【参考方案1】:

就像你说的,MacOS X 有 16 字节的堆栈对齐,这意味着机器期望堆栈上的每个变量从当前堆栈指针的 16 倍数的字节开始。

当堆栈未对齐时,这意味着我们开始尝试从那个 16 字节窗口的中间读取变量,并且通常会出现分段错误。

在代码中调用例程之前,需要确保堆栈正确对齐;在这种情况下,意味着基指针寄存器可以被 16 整除。

subq $8, %rsp               # stack is misaligned by 8 bytes
movq %rdi, 8(%rsp)          #
movq obj_size(%rdi), %rax   #
imul $8, %rax               #
movq %rax, %rdi             #
callq _malloc               # stack is still misaligned when this is called

要解决此问题,您可以将subq %rsp 设置为 16 而不是 8。

subq $16, %rsp               # stack is still aligned
movq %rdi, 16(%rsp)          #
...                          #
callq _malloc                # stack is still aligned when this is called, good

【讨论】:

这意味着编译器希望每个变量都定义在 16 字节的内存槽中。不,不是这个意思。堆栈上 arg 槽的宽度为 8,但 第一个 位于 16 字节对齐的地址。但是是的,函数序言中的push %rbp 将堆栈对齐了 16,因此在保留堆栈空间时需要减去 16 的倍数。 @PeterCordes 啊,我明白了,感谢您的指正!还有什么问题或者我应该在这里添加吗? 表示基指针寄存器可以被16整除。 rbp的对齐与任何事情无关; ABI 没有具体说明如何使用 RBP。 RBP 在 OP 的损坏代码中是 16 字节对齐的,因为当您制作传统的堆栈帧时,它总是设置为 rsp_on_entry-8,因此这显然不是充分条件。另外,您的第一段仍然是错误的。第一个 arg 从 16 字节对齐地址开始,但后面的 args(如果有)仅 8 字节对齐,除非该特定 arg 具有更高的对齐要求(例如 long double__m128)。

以上是关于OS X - x64:堆栈不是 16 字节对齐错误的主要内容,如果未能解决你的问题,请参考以下文章

gcc x86-32堆栈对齐并调用printf

x64 CPU 上的原子 16 字节读取

GCC - 如何重新对齐堆栈?

x86-64 上检查指针范围是不是跨越 N 字节对齐地址的最快方法?

如何在 GCC 的 32 字节边界处对齐堆栈?

C++11:16 字节原子<> 变量是不是在 16 字节边界上自动对齐,允许 CMPXCHG16B 指令?