ASM x64 scanf printf double, GAS
ASM x64 scanf printf double, GAS
我不明白为什么这段代码对我不起作用。我需要对 double 使用 scanf 函数,然后对相同的 double 使用 printf。
使用此代码时效果不好。我看到的是非常随机的字符。
.data
d1: .double
format: .asciz "%lf\n"
format2: .asciz "%lf"
.text
.globl main
main:
subq , %rsp
#scanf
movq [=10=], %rax
movq $d1, %rsi
movq $format2, %rdi
call scanf
addq , %rsp
#printf
movq , %rax
movsd d1, %xmm0
movq $format, %rdi
call printf
addq , %rsp
#exit
movq , %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
expects zero or more flonums, separated by commas. It assembles floating point numbers.").
因此 scanf
覆盖了您用于 printf
的格式字符串。这是 printf
打印的随机垃圾。
解决方法是实际保留一些 space,最好是在堆栈上。但是如果你真的想要静态存储然后使用 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 , %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 , %eax
movsd d1, %xmm0
mov $print_format, %edi
call printf
add , %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
或者在堆栈上使用 scratch space。还更改了:格式字符串的 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 , %eax # 1 FP arg in xmm registers (as opposed to memory)
lea print_format(%rip), %rdi
pop %rdx # deallocate 8 bytes. add , %rsp would work, too
jmp printf@plt # tailcall return printf(...)
.section .rodata
print_format: .asciz "%f\n"
scan_format: .asciz "%lf"
您甚至可以将格式字符串存储为立即数,但是您需要保留更多堆栈 space 以保持其对齐。 (例如 push $"%lf"
,除了 GAS 语法不做多字符整数常量。在 NASM 中你真的可以做 push '%lf'
来获得这 3 个字节 + 5 个零填充。)
相关::你不能因为 C 默认转换规则提升到 double
。
另外相关:关于ABI对齐规则的问答:Printing floating point numbers from x86-64 seems to require %rbp to be saved
我不明白为什么这段代码对我不起作用。我需要对 double 使用 scanf 函数,然后对相同的 double 使用 printf。 使用此代码时效果不好。我看到的是非常随机的字符。
.data
d1: .double
format: .asciz "%lf\n"
format2: .asciz "%lf"
.text
.globl main
main:
subq , %rsp
#scanf
movq [=10=], %rax
movq $d1, %rsi
movq $format2, %rdi
call scanf
addq , %rsp
#printf
movq , %rax
movsd d1, %xmm0
movq $format, %rdi
call printf
addq , %rsp
#exit
movq , %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
expects zero or more flonums, separated by commas. It assembles floating point numbers.").
因此 scanf
覆盖了您用于 printf
的格式字符串。这是 printf
打印的随机垃圾。
解决方法是实际保留一些 space,最好是在堆栈上。但是如果你真的想要静态存储然后使用 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 , %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 , %eax
movsd d1, %xmm0
mov $print_format, %edi
call printf
add , %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
或者在堆栈上使用 scratch space。还更改了:格式字符串的 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 , %eax # 1 FP arg in xmm registers (as opposed to memory)
lea print_format(%rip), %rdi
pop %rdx # deallocate 8 bytes. add , %rsp would work, too
jmp printf@plt # tailcall return printf(...)
.section .rodata
print_format: .asciz "%f\n"
scan_format: .asciz "%lf"
您甚至可以将格式字符串存储为立即数,但是您需要保留更多堆栈 space 以保持其对齐。 (例如 push $"%lf"
,除了 GAS 语法不做多字符整数常量。在 NASM 中你真的可以做 push '%lf'
来获得这 3 个字节 + 5 个零填充。)
相关:double
。
另外相关:关于ABI对齐规则的问答:Printing floating point numbers from x86-64 seems to require %rbp to be saved