为什么以下 x86_64 程序集会出现分段错误?

mos*_*smo 0 assembly x86-64

到目前为止,我一直在学习编写一些 x86_64 程序集。我读到可以减去 RSP 来向下增长堆栈并分配空间,所以我编写了以下代码:

push %rbp
movq %rsp, %rbp
subq $16, %rsp
movq $200, -8(%rsp)
movq $300, -16(%rsp)
popq %rbp
retq
Run Code Online (Sandbox Code Playgroud)

根据我的理解,这将创建一个函数,在其中设置堆栈帧,然后在堆栈上分配 16 个字节,并将 -8 和 -16 的值分别设置为 200 和 300。但是,当我使用 gcc 运行此程序时,出现分段错误。不过,如果我删除sub程序的一部分,它就可以完美运行。我想我误解了一些东西,那么这里到底发生了什么?

Pet*_*des 5

正如 Jester 所说,问题在于,当您pop %rbp/ret时,堆栈指针指向其他地方,因此您无法获得旧地址%rbp和返回地址。(由于另一个潜在的错误,您永远不会写入您弹出的位置,因此我无法准确告诉您您ret要发送到哪个无效地址。)

如果您创建了一个堆栈帧 ( mov %rsp, %rbp),那么使用相对于 的偏移量是正常的%rbp。有趣的事实:movq $200, -8(%rbp)同等movq $200, 8(%rsp). (%rsp不幸的是,用作基址寄存器总是需要 SIB 字节来编码有效地址。)

使用%rbp还意味着即使您推送/弹出内容,引用任何给定堆栈地址的表达式也不会改变(在具有 stack-args ABI 的 32 位代码中常见,但在 64 位代码中很少见。64 位 gcc 切换到 -fomit-frame -32位之前的指针)。


movq $200, -8(%rsp)使用了您预留的 16B 之外的空间。这是我之前提到的“其他潜在错误”。

使用低于当前值的 128B%rsp实际上并不是SysV ABI 中的错误:异步事件(信号处理程序等)避免破坏红色区域,因此不调用任何其他函数的小函数可以避免花费指令修改%rsp以保留空间。x86-64 有 15 个通用寄存器(不包括堆栈指针),因此中小型函数通常不需要使用堆栈,除了保存/恢复调用保留的寄存器之外。或者对于本地数组。

Windows ABI 不使用红色区域,因此%rsp即使您自己不使用call.

有关调用约定/ABI 的链接,请参阅