编写玩具编译器 - 调用是段错误

Writing a toy compiler - call is segfaulting

这个问题是我写一个玩具编译器时提出的,但我认为没有必要展示这个问题的编译器方面 - 这完全是我对汇编经验不足的问题。

我正在努力用我的语言实现功能。我生成了以下应该打印 6 的程序集 (AT&T)。 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 , %rax
push %rax
mov , %rax
push %rax
mov -8(%rbp), %rax
call *%rax
add 16, %rsp
call print
mov [=10=], %rax
mov %rbp, %rsp
pop %rbp
ret

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

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

接下来,在f0中你的帧指针偏移是错误的。 16(%rbp)是压入的第二个参数,不是第一个,8(%rbp)是return地址。不要忘记考虑 push %rbp 本身对堆栈指针的影响。所以你最终计算了 2 次 return 地址而不是 1 次 2。使那些 24(%rbp)16(%rbp).

最后,调用printf时需要确保堆栈对齐到16字节。库函数在不存在时会表现出不可预测的行为。有时他们可能碰巧没有做任何需要对齐的事情,但他们似乎一切正常;其他时候他们可能会崩溃。