在汇编Linux中将浮点值打印到STDOUT

时间:2019-05-28 16:55:30

标签: assembly x86 gas

我正在尝试将浮点值打印到控制台,但是它给出了意外的结果,太大的数字具有不同的符号。

程序

.data
    float_: .asciz "%f\n"
    n1: .float 10.4
        n2: .float 10.3
.text

.globl main

main:
    sub $1, %esp
    finit
    flds n1
    fsubs n2
    fsts 1(%esp)

    movl 1(%esp), %eax
    call pfl
    addl $1, %esp

.exit:
    movl $1, %eax
    movl $0, %ebx
    int $0x80

pfl:
    pushal
    push %eax
    push $float_
    call printf
    add $8, %esp
    popal
    ret

输出

每次都不同,但介于-400 ... 0.0 ...至-500000..0.0 ...

1 个答案:

答案 0 :(得分:3)

您的代码存在各种问题。最重要的是,%f期望double,但是您传递了float。参见man 3 printf或C书。此外,float是4个字节,因此建议您分配4个字节而不是1个。更糟糕的是,您甚至没有使用分配的1个字节,因为它位于(%esp),但是您使用了1(%esp)。对于双精度,您将需要8个字节。接下来,您忘记从FPU弹出值。另外,即使在32位模式下,当前的调用约定也需要16字节对齐的堆栈。

最后,建议不要直接在main或其他使用libc函数的代码中使用退出系统调用。相反,如果您确实坚持要确保发生libc清理(如刷新stdio缓冲区),则只需retcall exit。否则,如果将stdout重定向到文件以使其具有完整缓冲,则不会获得任何输出。

以下是可能的版本,可解决上述所有问题:

.data
    float_: .asciz "%f\n"
    n1: .float 10.4
    n2: .float 10.3
.text

.globl main

main:
    sub $12, %esp      # 8 bytes for double, + 4 bytes for alignment
    flds n1
    fsubs n2
    fstpl (%esp)       # pop double from fpu

    movl (%esp), %eax  # low 4 bytes
    movl 4(%esp), %edx # high 4 bytes
    call pfl
    addl $12, %esp

    ret

## input: EDX:EAX = the bit-pattern for a double
pfl:
    push %edx
    push %eax
    push $float_      # 3x push realigns the stack by 16 again
    call printf
    add $12, %esp
    ret

不需要通过整数寄存器来启动double;如果将call printf内联到主函数中,则可能只是使用fstpldouble放在堆栈上指向格式字符串的指针的正上方。

或者使您的pfl函数在%st(0)中输入其输入,而不是在整数寄存器中输入,因为无论如何您都在建立自定义调用约定。

(我想您的下一个问题将是为什么它会打印0.099999而不是0.1:))