printf float в сборке nasm 64-бит

Я хочу напечатать значение с плавающей запятой с помощью printf

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? Что я делаю не так?


person user2798943    schedule 15.12.2013    source источник
comment
Проблема с выравниванием дублирует печать чисел с плавающей запятой из x86-64, по-видимому, требует сохранения% rbp, но первая часть (про AL = количество аргументов FP в регистрах) нет.   -  person Peter Cordes    schedule 06.06.2018


Ответы (2)


Я подозреваю, что проблема связана с настройкой вашего кода с rax на 0, тогда как это должно быть 1, потому что вы передаете аргумент с плавающей запятой (см. здесь для подробностей). В основном rax должно содержать количество переменных аргументов, переданных в xmmN регистрах.

Редактировать:

Сбой в printf, по-видимому, вызван смещением стека, поскольку программа аварийно завершает работу на инструкции 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 стек не выравнивается по 16 байтам, но если это исправить, программа работает нормально. Ниже моя тестовая программа (обратите внимание на sub rsp, 8 в начале):

global main
extern printf

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

section .text

main:
    sub rsp, 8
    movq xmm0, qword [rs]
    mov rdi, string
    mov rax, 1
    call printf
    add rsp, 8
    mov eax, 0x60
    xor edi, edi
    syscall
person szx    schedule 15.12.2013
comment
Я изменил код, но теперь он вылетает из-за ошибки seg в printf. - person user2798943; 15.12.2013
comment
Вы должны ret из main, если вы используете функции stdio. sys_exit не сбрасывает буферы, поэтому вы потеряете вывод, если перенаправите эту программу во что-то еще (поэтому stdio сделает stdout полностью буферизованным, а не строковым буфером). Кроме того, movsd — самая идиоматическая инструкция для загрузки скаляра double. - person Peter Cordes; 18.04.2018

Что я делаю не так?

Во-первых: убедитесь, что вы используете правильное соглашение о вызовах (стек, регистры, слева направо, справа налево и т. д.). Если ваша программа действительно печатает число с плавающей запятой, хотя это не то, что вам нужно, то, по крайней мере, строка формата передается правильно (или вам очень повезло, и printf нашел адрес строки формата справа место, даже если вы не указали его адрес).

Второе: число, которое вы пытаетесь напечатать... это число с плавающей запятой или двойное число? rs определено для хранения значения quadword (64 бита), но числа с плавающей запятой — 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
person mcleod_ideafix    schedule 15.12.2013
comment
rax на самом деле должен содержать количество используемых регистров xmm, я изменил его на один, но теперь программа вылетает из-за ошибки seg в printf. плавающие аргументы printf хранятся в регистрах xmm (ассемблер amd64), строковый адрес хранится в rdi. - person user2798943; 15.12.2013
comment
странно то, что когда я помещаю 1 в rax, printf падает с ошибкой seg, а когда я помещаю 0 в rax, он печатает 0,000, потому что rax - это число аргументов с плавающей запятой - person user2798943; 15.12.2013
comment
%f для printf требуется double из-за правил повышения типа C. %lf является избыточным. - person Peter Cordes; 23.12.2017