Ray*_*oal 9 floating-point assembly x86-64
当我编写一个简单的汇编语言程序,与C库链接,在Ubuntu上使用gcc 4.6.1,我尝试打印一个整数,它工作正常:
.global main
.text
main:
mov $format, %rdi
mov $5, %rsi
mov $0, %rax
call printf
ret
format:
.asciz "%10d\n"
Run Code Online (Sandbox Code Playgroud)
按预期打印5.
但是现在如果我进行一些小改动,并尝试打印浮点值:
.global main
.text
main:
mov $format, %rdi
movsd x, %xmm0
mov $1, %rax
call printf
ret
format:
.asciz "%10.4f\n"
x:
.double 15.5
Run Code Online (Sandbox Code Playgroud)
这个程序赛格故障不打印任何东西.只是一个悲伤的段错误.
但我可以通过推动和弹出来解决这个问题%rbp.
.global main
.text
main:
push %rbp
mov $format, %rdi
movsd x, %xmm0
mov $1, %rax
call printf
pop %rbp
ret
format:
.asciz "%10.4f\n"
x:
.double 15.5
Run Code Online (Sandbox Code Playgroud)
现在它可以工作,打印15.5000.
我的问题是:为什么推送和弹出%rbp使应用程序工作?根据ABI,%rbp是被调用者必须保留的寄存器之一,因此printf不能搞砸它.事实上,printf在第一个程序中工作时,只传递了一个整数printf.所以问题必定在其他地方吗?
NPE*_*NPE 10
我怀疑这个问题没有任何关系%rbp,而是与堆栈对齐有关.引用ABI:
ABI要求堆栈帧在16字节边界上对齐.具体而言,参数区域的末尾(%rbp + 16)必须是16的倍数.此要求意味着帧大小应填充为16个字节的倍数.
输入时堆栈已对齐main().调用printf()将返回地址压入堆栈,将堆栈指针移动8个字节.您可以通过将另外八个字节压入堆栈来恢复对齐(恰好%rbp可能是其他东西).
这是gcc生成的代码(也在Godbolt编译器资源管理器上):
.LC1:
.ascii "%10.4f\12\0"
main:
leaq .LC1(%rip), %rdi # format string address
subq $8, %rsp ### align the stack by 16 before a CALL
movl $1, %eax ### 1 FP arg being passed in a register to a variadic function
movsd .LC0(%rip), %xmm0 # load the double itself
call printf
xorl %eax, %eax # return 0 from main
addq $8, %rsp
ret
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,它通过从%rsp开头减去8 并在最后添加它来处理对齐要求.
你可以改为对你喜欢的任何寄存器进行虚拟推/弹而不是%rsp直接操作; 一些编译器确实使用虚拟推送来对齐堆栈,因为这在现代CPU上实际上可以更便宜,并节省了代码大小.
| 归档时间: |
|
| 查看次数: |
1561 次 |
| 最近记录: |