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 字节对齐错误的主要内容,如果未能解决你的问题,请参考以下文章