printf float in nasm assembly 64-bit

cpjpxq1n  于 2023-04-21  发布在  其他
关注(0)|答案(3)|浏览(123)

我想用printf打印一个float值

global main
extern printf

section .data
   string: db `%f\n`, 0

section .bss
   rs: resq 1

[...]

   movq xmm0, [rs]
   mov rdi, string
   mov rax, 0
   call printf

rs包含浮点值1.6

(gdb) x/fg &rs
0x600ad8 <rs>:  1.6000000000000001

但程序会打印

[username@localhost folder]$ ./programname
0.000000

谁能让程序打印1.6?我做错了什么?

vmpqdwk3

vmpqdwk31#

第一个问题是您的代码将AL(通过RAX)设置为0,而它必须是1,因为您传递了一个浮点参数(有关详细信息,请参阅here)。基本上,AL应该包含xmmN寄存器中传递的变量参数的数量。
如果不进行其他更改,修复该问题将导致printfcaused by stack misalignment内部的segfault。程序在movaps指令处崩溃(该指令期望内存操作数在16字节边界上对齐):

=> 0x7ffff7a65f84 <__printf+36>:    movaps %xmm0,0x50(%rsp)
   0x7ffff7a65f89 <__printf+41>:    movaps %xmm1,0x60(%rsp)
   0x7ffff7a65f8e <__printf+46>:    movaps %xmm2,0x70(%rsp)
   0x7ffff7a65f93 <__printf+51>:    movaps %xmm3,0x80(%rsp)
   0x7ffff7a65f9b <__printf+59>:    movaps %xmm4,0x90(%rsp)
   0x7ffff7a65fa3 <__printf+67>:    movaps %xmm5,0xa0(%rsp)
   0x7ffff7a65fab <__printf+75>:    movaps %xmm6,0xb0(%rsp)
   0x7ffff7a65fb3 <__printf+83>:    movaps %xmm7,0xc0(%rsp)

当输入main(或任何其他函数)时,堆栈对齐RSP%16 == 8,但在call之前需要RSP%16 == 0。如果你解决了这个问题,程序工作正常。下面是我的测试程序(注意开始的sub rsp, 8):

global main
default rel     ; use a more efficient [rel foo] addressing mode by default
extern printf

section .data
    string db `%f\n`, 0
    rs dq 1.6

section .text

main:
    sub  rsp, 8                ; align the stack

    movsd xmm0, qword [rs]     ; load a Scalar Double
    mov  rdi, string           ; 64-bit absolute address of format string
    mov  eax, 1                ; AL=1  number of FP args in XMM regs
    call printf

    add  rsp, 8
    xor  eax, eax           ; return 0
    ret                     ; or  call exit with EDI=0,  not call _exit or syscall
; don't use _exit() after printf, it won't flush stdio buffers.
; which will be non-empty if output stdout is redirected to a file.
;       mov eax, 60         ; __NR_exit from unistd_64.h
;       xor edi, edi
;       syscall

可能的陷阱和相关的Q& A:

gwbalxhn

gwbalxhn2#

我做错了什么

第一个:请确保您使用正确的呼叫约定(堆栈、寄存器、从左到右、从右到左等).如果你的程序确实打印了一个浮点数,虽然它不是你所要求的那个然后至少格式字符串被正确地传递(或者你运气很好,printf在正确的位置找到了格式字符串的地址,即使你没有把它的地址放在那里)。
第二个:你要打印的数字...是float还是double?rs定义的是一个四字值(64位),但float是32位。所以,如果第一个点已经检查过了,没有问题,我建议你使用"%lf"作为格式,而不是"%f"

顺便问一句:你为什么把RAX = 0?这是什么意思,关于调用printf
更新:这可能对你有帮助。一个愚蠢的程序(f.c)的反汇编:

#include <stdio.h>

main()
{
  float x;

  x = 1.6;
  printf ("%f\n", x);
}

$ gcc -c -S f.c
$ less f.s

.file   "f.c"
        .section        .rodata
.LC1:
        .string "%f\n"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    $0x3fcccccd, %eax
        movl    %eax, -4(%rbp)
        movss   -4(%rbp), %xmm0
        cvtps2pd        %xmm0, %xmm0
        movl    $.LC1, %eax
        movq    %rax, %rdi
        movl    $1, %eax
        call    printf
        leave
gt0wga4j

gt0wga4j3#

; file: pf.asm
; assemble with:  nasm -f elf64 pf.asm -o pf.o
; link with: ld -pie -z noexecstack -e _start -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o pf pf.o -lc

default rel             ; Use PC relative offsets (for PIE executable)

global _start           ; Execute at _start (don't use any provided startup code)

extern printf           ; printf is our only external function

section .rodata         ; The format string in printf should be read only
    string: db "Your number is %f", 0xa, 0

section .data           ; We could make this read only, but if we want to change it
    rs dq 1.6           ; through a scanf or something we can

section .text           ; Program code

_start:
;    Uncomment stack frame stuff if stack space needed, not needed for this example

;    push rbp           ; Setup our stack frame
;    mov rbp, rsp

;    sub rsp, 8         ; Create space on the stack

    movq xmm0, qword [rs]       ; Put float in xmm0
    lea rdi, [string]   ; Load address of string, lea is needed for PC relative
    mov eax, 1          ; Using floating point numbers, 0 extend into rax
    call printf wrt ..plt  ; PC relative function call

;    add rsp, 8         ; Remove allocated space on the stack

;    pop rbp            ; Undo our stack frame 

    mov eax, 60         ; Exit syscall
    xor edi, edi        ; No error, 0 extend into rdi
    syscall

相关问题