ASM x64 scanf printf double, GAS [英] ASM x64 scanf printf double, GAS

查看:34
本文介绍了ASM x64 scanf printf double, GAS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白为什么这段代码对我不起作用.我需要对 double 使用 scanf 函数,然后对同一个 double 使用 printf 函数.使用此代码时效果不佳.我看到的是非常随机的字符.

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
"
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
"

d1format 具有相同的地址,因为没有 args 的 .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.)

相反,使用这个:

#.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
"     # 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 代码的更多内容,请参阅 标签维基.

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 可执行文件)中工作.显式的 @plt 是生成 PIE 可执行文件时生成 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
"
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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆