这个问题来自于我编写一个玩具编译器,但我认为没有必要展示这个问题的编译器方面 - 这严格来说是我对汇编不太有经验的问题。
我正在努力用我的语言实现功能。我生成了以下程序集 (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/