ASM x64 scanf printf double,GAS [英] ASM x64 scanf printf double, GAS
问题描述
我不知道为什么此代码对我不起作用。我需要将scanf函数用于double,然后将printf用于相同的double。
使用此代码时效果不佳。我看到的是相当随机的字符。
I can't figure out why this code isn't working for me. I need to use scanf function for double and then printf for same double. When using this code results are not good. What I see are pretty random characters.
.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"
d1
和 format
具有相同的地址,因为没有参数的 .double
不会汇编成任何东西。 ( .double
期望为零或)。
d1
and format
have the same address, since .double
with no args assembles to nothing. (".double
expects zero or more flonums, separated by commas. It assembles floating point numbers.").
所以 scanf
覆盖用于 printf
的格式字符串。这是 printf
打印的随机垃圾。
So scanf
overwrites the format string you use for printf
. This is the random garbage that printf
prints.
解决方法是实际保留一些空间,最好是在堆栈上。但是,如果您真的想要静态存储,请使用BSS。 (此文档对其进行了很好的解释,即使它与某些特定的gcc端口有关。 )
The fix is to actually reserve some space, preferably on the stack. But if you really want static storage then use the BSS. (This doc explains it well, even though it's about some specific gcc port.)
相反,请使用以下命令:
Instead, use this:
#.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。
For more stuff about writing efficient asm code, see the links in the x86 tag wiki.
本来也可以,但是在可执行文件中浪费了8个字节:
Also would have worked, but wasted 8 bytes in your executable:
.data
d1: .double 0.0
或在堆栈上使用暂存空间。还更改了:用于格式字符串的RIP相对LEA,因此它将在PIE(PIC可执行文件)中工作。在使PIE可执行文件时,显式 @plt
是生成PLT所必需的。
Or to use scratch space on the stack. Also changed: RIP-relative LEA for the format strings, so this will work in a PIE (PIC executable). The explicit @plt
is necessary to generate PLT when making a PIE executable.
.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个填充零。)
You could even store your format strings as immediates, too, but then you need to reserve more stack space to keep it aligned. (e.g. push $"%lf"
, except GAS syntax doesn't do multi-character integer constants. In NASM you really could do push '%lf'
to get those 3 bytes + 5 zeros of padding.)
相关:如何使用printf打印单精度浮点数:由于C默认转换规则,您不能这样做升级为 double
。
Related: How to print a single-precision float with printf: you can't because of C default-conversion rules that promote to double
.
还与以下内容有关:ABI对齐规则的问答:从x86-64打印浮点数似乎需要保存%rbp
Also related: a Q&A about the ABI alignment rules: Printing floating point numbers from x86-64 seems to require %rbp to be saved
这篇关于ASM x64 scanf printf double,GAS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!