x86 - 编写一个玩具编译器 - 调用出现段错误

标签 x86 compiler-construction x86-64

这个问题来自于我编写一个玩具编译器,但我认为没有必要展示这个问题的编译器方面 - 这严格来说是我对汇编不太有经验的问题。

我正在努力用我的语言实现功能。我生成了以下程序集 (AT&T),它应该打印 6。程序在 call *%rax 处出现段错误,我认为这意味着我没有正确保存函数的地址。应该发生的情况是,函数 f0 应该从堆栈中读取参数,将它们相乘,然后将结果保留在 %rax 中。 print 函数只是 C 语言中 printf 函数的包装,用于打印 %rax

我运行的是 Ubuntu 20.04,并且代码是使用 gcc test.S -o test.out -no-pie 编译的,以防万一。

.data
numfmt:
.asciz "%d\n"

.text
print:
push %rbx
mov %rax, %rsi
mov $numfmt, %rdi
xor %rax, %rax
call printf
pop %rbx
ret

.globl main
main:
push %rbp
mov %rsp, %rbp
jmp s0
f0:
push %rbp
mov %rsp, %rbp
mov 16(%rbp), %rax
push %rax
mov 8(%rbp), %rax
pop %rcx
imul %rcx, %rax
mov %rbp, %rsp
pop %rbp
ret
s0:
mov $f0, %rax
push %rax
mov $1, %rax
push %rax
mov $2, %rax
push %rax
mov -8(%rbp), %rax
call *%rax
add 16, %rsp
call print
mov $0, %rax
mov %rbp, %rsp
pop %rbp
ret

最佳答案

在 AT&T 语法中,mov f0, %rax 是间接移动:它使用位于地址 f0 的 qword 加载 %rax。但您想加载地址本身。这是立即移动,因此操作数 f0 需要以 $ 为前缀以指示它是立即移动。因此它应该是 mov $f0, %rax

出于类似的原因,add 16, %rsp 是错误的,因为它尝试将位于地址 16 的值添加到 %rsp。该页面未映射,因此出现段错误。同样,您需要添加 16 美元,%rsp

接下来,在f0中,您的帧指针偏移量是错误的。 16(%rbp) 是推送的第二个参数,而不是第一个,8(%rbp) 是返回地址。不要忘记考虑 push %rbp 本身对堆栈指针的影响。因此,您最终计算的是返回地址的 2 倍,而不是 1 乘以 2。将这些设为 24(%rbp)16(%rbp)

最后,您需要确保调用 printf 时堆栈与 16 字节对齐。如果不是,库函数的行为就会不可预测。有时,他们可能碰巧没有做任何需要协调的事情,而他们似乎一切都正常;其他时候它们可能会崩溃。

关于x86 - 编写一个玩具编译器 - 调用出现段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62824340/

相关文章:

c - 旧的 EBP 和返回地址在哪里?

c - 局部变量在栈上的分配顺序

java - 运行时动态绑定(bind)和类继承的区别

java - 如何将普通的java代码放入网站中

linux - 如何在 GAS 汇编中使用 "repne scasb"?

c - 选择外部调用的特定寄存器

x86 - 如何设置DS和ES寄存器等于CS?

c - IF 语句 ASM 和 CPU 分支

compiler-construction - 如何创建上下文无关文法?

assembly - 如何使用 64 位绝对地址执行调用指令?