ASM x64 scanf printf double,GAS

我无法弄清楚为什么这段代码对我不起作用。 我需要使用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 

这就是问题:

 .data d1: .double # declares zero doubles, since you used an empty list format: .asciz "%lf\n" 

d1format具有相同的地址,因为.double没有args汇编为.double 。 (“ .double期望零或多个flonums,用逗号分隔。它汇集浮点数。 ”)。

因此scanf覆盖用于printf的格式字符串。 这是printf打印的随机垃圾。

修复是实际保留一些空间,最好是在堆栈上。 但如果您真的想要静态存储,那么请使用BSS。 ( 这个文档解释得很好 ,即使它是关于某个特定的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是生成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" 

你甚至可以将你的格式字符串存储为immediates,但是你需要保留更多的堆栈空间以保持对齐。 (例如, push $"%lf" ,除了GAS语法不执行多字符整数常量。在NASM中,你真的可以push '%lf'来获得那3个字节+5个零填充。)

相关: 如何使用printf打印单精度浮点数 :您不能因为C默认转换规则而提升为double

还有关:关于ABI对齐规则的问答: 从x86-64打印浮点数似乎需要保存%rbp