‘pop rbp’指令出现分段错误

问题描述 投票:0回答:1

以下汇编代码会导致在

pop rbp
指令处从主函数中退出分段错误。这段代码是由我正在编写的编译器生成的,所以不要介意多余的指令:

.intel_syntax noprefix
.global main

.text
add:
    push rbp
    mov rbp, rsp
    sub rsp, 4
    mov dword [rbp - 4], edi
    sub rsp, 4
    mov dword [rbp - 8], esi

    mov eax, dword [rbp - 4]
    mov ebx, dword [rbp - 8]
    add eax, ebx
    mov rsp, rbp
    pop rbp
    ret

main:
    push rbp
    mov rbp, rsp

    mov eax, 60
    mov edi, eax
    mov eax, 9
    mov esi, eax
    call add
    mov rsp, rbp
    pop rbp
    ret
    add rsp, 4

我已经仔细检查过我是否保持了堆栈的顺序,所以我不明白这个错误是如何发生的。

我尝试过用GDB调试它,但没有成功。

assembly x86 segmentation-fault stack x86-64
1个回答
2
投票

你想要

dword ptr

简单来说,

dword
被定义为
4
,就像在MASM中一样,所以
mov dword [rbp - 4], edi
实际上是
mov 4[rbp - 4], edi
=
mov [rbp + 0], edi
,覆盖保存的RBP。 (所以你的
add
函数会破坏调用者的 RBP)。

在单步执行时具有“反汇编”视图的 GDB 中(

layout asm
),这一点立即显而易见;我已经对这段代码产生了怀疑,因为那些低效的
sub rsp, 4
/
mov
对 - 你应该在函数入口处使用
sub rsp, 8
更改 RSP 一次,以保留你需要的所有堆栈空间,在两个存储之前.

(或者也许使用

push rdi
/
mov [rbp-4], esi
,但如果您打算为变量进行寄存器分配而不是总是将它们溢出到函数入口处,则大多数函数不需要这种优化;主流 C 编译器不看即使在调试版本中,它们总是会溢出所有内容。)

对于调试这样的东西,

watch -l *(long*)$rbp
内的
add
(在
push rbp
/
mov rbp, rsp
之后)也有效:从那里继续显示
mov  DWORD PTR [rbp+0x0],edi
更改该内存位置。

另一个线索是,当

0x7fff0000003c
故障时,RSP =
pop rbp
,这应该让你怀疑低半部分已被小整数覆盖。 (由于我们刚刚运行了
mov rsp, rbp
,并且 RBP 值也同样损坏,因此这是明显值得怀疑的事情。)



此外,使用 AT&T 语法或 NASM 不会出现此问题,其中像

dword
这样的关键字不是地雷,如果您使用错误,它们会默默地破坏您的代码。

GAS

.intel_syntax
没有那么强大。例如,如果你有一个名为
long rax;
int offset;
的全局变量,GCC 本身就会导致汇编损坏。 在gcc中使用“-masm=intel”时,名为“offset”的变量导致出现“Error: invalid use of register”,但在AT&T模式下没有错误

可以使用引号

call "rax"
来避免将其视为注册名称,但 GCC 不会这样做;
-masm=intel
是 GCC 的二等公民,主要供人类阅读 asm,显然不用于生产用途。 (消除 Intel 语法中寄存器名称标签的歧义)在 NASM 中,您需要
call $rax
强制将其视为符号。

GAS

.intel_syntax
也有歧义,具体取决于
.equ
出现在使用它的代码之前或之后。 区分内存和 GNU 中的常量为 .intel_syntax

我一直更喜欢使用 Intel 语法进行反汇编,但大多数使用手写汇编的项目并不将其用于源代码。对于自动代码生成,AT&T 可能是更好的选择,除非您想使用 Intel 语法支持内联汇编。然后请注意局限性。

© www.soinside.com 2019 - 2024. All rights reserved.