我无法弄清楚为什么这段代码对我不起作用。我需要使用scanf函数用于double,然后printf用于相同的double。 使用此代码时结果不佳。我看到的是相当随机的字符。
.data
d1: .double
format: .asciz "%lf\n"
format2: .asciz "%lf"
.text
.globl main
main:
subq $8, %rsp
#scanf
movq $0, %rax
movq $d1, %rsi
movq $format2, %rdi
call scanf
addq $16, %rsp
#printf
movq $1, %rax
movsd d1, %xmm0
movq $format, %rdi
call printf
addq $16, %rsp
#exit
movq $1, %rdi
xorq %rax, %rax
call exit
答案 0 :(得分:1)
这是问题所在:
car
.data
d1: .double # declares zero doubles, since you used an empty list
format: .asciz "%lf\n"
和d1
具有相同的地址,因为没有args的format
汇编为空。 ( “.double
expects zero or more flonums, separated by commas. It assembles floating point numbers.”)。
因此.double
会覆盖您用于scanf
的格式字符串。这是printf
打印的随机垃圾。
修复方法是实际保留一些空间,最好是在堆栈上。但如果您真的想要静态存储,那么请使用BSS。 (This doc explains it well,即使它是关于某个特定的gcc端口。)
相反,请使用:
printf
有关编写高效asm代码的更多内容,请参阅x86标记wiki中的链接。
也可以使用,但在可执行文件中浪费了8个字节:
#.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
或者在堆栈上使用临时空间。也改变了:格式字符串的RIP相对LEA,因此这将在PIE(PIC可执行文件)中工作。在制作PIE可执行文件时,显式.data
d1: .double 0.0
是生成PLT所必需的。
@plt
您甚至可以将格式字符串存储为immediates,但是您需要保留更多的堆栈空间以保持对齐。 (例如.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"
,除了GAS语法不执行多字符整数常量。在NASM中,你真的可以push $"%lf"
来获得那3个字节+ 5个填充零。)
相关:How to print a single-precision float with printf:您不能因为C默认转化规则升级为push '%lf'
。
还相关:关于ABI对齐规则的Q& A:Printing floating point numbers from x86-64 seems to require %rbp to be saved