这就是问题所在:
.data
d1: .double # declares zero doubles, since you used an empty list
format: .asciz "%lf\n"
d1 和 format 具有相同的地址,因为没有 args 的 .double 汇编为空。 (".double expects zero or more flonums, separated by commas. It assembles floating point numbers.")。
所以scanf 会覆盖您用于printf 的格式字符串。这是printf 打印的随机垃圾。
解决方法是实际保留一些空间,最好是在堆栈上。但是,如果您真的想要静态存储,请使用 BSS。 (This doc explains it well,尽管它是关于一些特定的 gcc 端口。)
改用这个:
#.bss
# .p2align 3
# d1: .skip 8 ### This is the bugfix. The rest is just improvements
# or just use .lcomm instead of switching to the .bss and back
.lcomm d1, 8
.section .rodata
print_format: .asciz "%f\n" # For printf, "%f" is the format for double. %lf still works to print a double, though. Only %llf or %Lf is long double.
scan_format: .asciz "%lf" # scanf does care about the trailing whitespace in the format string: it won't return until it sees something after the whitespeace :/ Otherwise we could use the same format string for both.
.text
.globl main
main:
subq $8, %rsp
xor %eax,%eax
mov $d1, %esi # addresses for code and static data are always in the low 2G in the default "small" code model, so we can save insn bytes by avoiding REX prefixes.
mov $scan_format, %edi
call scanf
mov $1, %eax
movsd d1, %xmm0
mov $print_format, %edi
call printf
add $8, %rsp
ret
#xor %edi,%edi # exit(0) means success, but we can just return from main instead. It's not a varargs function, so you don't need to zero rax
#call exit
有关编写高效 asm 代码的更多信息,请参阅x86 标签 wiki 中的链接。
也可以,但在您的可执行文件中浪费了 8 个字节:
.data
d1: .double 0.0
或使用堆栈上的暂存空间。还更改了:格式字符串的相对于 RIP 的 LEA,因此这将在 PIE(PIC 可执行文件)中工作。生成 PIE 可执行文件时需要显式的@plt。
.globl main
main:
xor %eax, %eax # no FP args. (double* is a pointer, aka integer)
push %rax # reserve 8 bytes, and align the stack. (sub works, push is more compact and usually not slower)
mov %rsp, %rsi # pointer to the 8 bytes
lea scan_format(%rip), %rdi
call scanf@plt
# %eax will be 1 if scanf successfully converted an arg
movsd (%rsp), %xmm0
mov $1, %eax # 1 FP arg in xmm registers (as opposed to memory)
lea print_format(%rip), %rdi
pop %rdx # deallocate 8 bytes. add $8, %rsp would work, too
jmp printf@plt # tailcall return printf(...)
.section .rodata
print_format: .asciz "%f\n"
scan_format: .asciz "%lf"
您甚至可以将格式字符串存储为立即数,但是您需要保留更多堆栈空间以保持对齐。 (例如push $"%lf",除了 GAS 语法不做多字符整数常量。在 NASM 中你真的可以做 push '%lf' 来获得这 3 个字节 + 5 个零填充。)
相关:How to print a single-precision float with printf:你不能因为 C 默认转换规则提升为double。
还相关:关于 ABI 对齐规则的问答:Printing floating point numbers from x86-64 seems to require %rbp to be saved