为什么下面的x86_64程序集给我一个分段错误?

时间:2016-03-07 23:00:17

标签: 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

根据我的理解,这将创建一个设置堆栈帧的函数,然后它在堆栈上分配16个字节,并将值分别设置为-8和-16到200和300。 但是,当我用gcc运行它时,我得到一个分段错误。虽然如果删除程序的sub部分,它可以完美地运行。我认为我误解了一些事情,所以这里究竟发生了什么?

1 个答案:

答案 0 :(得分:4)

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

如果您完全构建堆栈帧(mov %rsp, %rbp),那么使用相对于%rbp的偏移量是正常的。有趣的事实:movq $200, -8(%rbp)需要one byte less machine code而不是等效的movq $200, 8(%rsp)。 (遗憾的是,使用%rsp作为基址寄存器总是需要一个SIB字节来编码有效地址。)

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

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

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

Windows ABI不使用红色区域,因此%rsp下方的内存可能会被踩到,即使您没有使用call自行执行此操作。< / p>

有关调用约定/ ABI的链接,请参阅标记wiki。