Функции Scanf и Printf в сборке, например на char * и double - PullRequest
0 голосов
/ 11 июня 2019

У меня есть задача, вызвать функции scanf и printf, используя переменные char * и double. Char * работает, но у меня проблема с двойным.

Функция: для scanf / printf char *, функция1: для scanf / printf double. Например, мои результаты после компиляции:

(зсапЕ) б

(printf) char: b

(зсапЕ) 1,3

(Printf) дважды: 99997200381866062965879955188785948733402760577162787362451212786.000000

Кажется, проблема в printf для двойной переменной, но я не знаю, как ее решить.

.data
STDIN = 0
STDOUT = 1
SYSREAD = 3
SYSWRITE = 4
SYSEXIT = 1
EXIT_SUCCESS = 0

format_inchar: .string "%c"
format_indouble: .string "%lf"
char: .ascii " "
double: .double 0.0

format_string1: .string "char: %c\n"   
format_double1: .string "double: %f\n"
.text
.global main
main:

function:

    push $char
    push $format_inchar
    call scanf

    push char
    push $format_string1
    call printf

function1:

    push $double
    push $format_indouble
    call scanf

    push double
    push $format_double1
    call printf

exit:
movl $SYSEXIT, %eax
movl $EXIT_SUCCESS, %ebx
int $0x80

1 Ответ

0 голосов
/ 11 июня 2019

32-битный код не может push 64-битный double из памяти с одной инструкцией.64-битный код не передает аргументы в стеке, поэтому я предполагаю, что это должен быть 32-битный код.(Также от использования int 0x80.)

push double в 32-битном коде равно pushl, что выдвигает младшие 4 байта .printf брал старшие 4 байта (содержащие экспоненту и наиболее значимые биты мантиссы) из всего, что было над ним в стеке.В этом случае, предполагая, что scanf не сгущает свои аргументы стека, старшие 4 байта вашего double битового шаблона были получены с адреса $format_indouble.

Возможно, вы хотите pushl double+4;pushl double чтобы сдвинуть 8-байтовый double на две половины.

(Суффикс размера операнда здесь необязателен, но я бы порекомендовал его, чтобы избежать неоднозначности, которая вас захватила.)

Или вы можете использовать SSE2 movsd для копирования 8 байт в стек.

    sub    $8, %esp
    movsd  double, %xmm0
    movsd  %xmm0, (%esp)

(push является специальным и может копировать из памяти в память. Инструкции с явным источником иназначение не может этого сделать.)

Конечно, вам не нужно статическое хранилище для ваших входов;Вы можете передавать указатели на стек в scanf.Тогда ваш double будет уже в стеке, и вы можете просто сохранить указатель на строку формата под ним.


Кстати, у вас нет стека, выровненного на 16, когда вы вызываетеscanf и printf.ABI требует этого, так что вам повезло, что ваша сборка glibc не использует movaps в памяти стека в этих функциях, что может привести к segfault.

До вызова main стек выравниваетсяна 16. Это call выдвигает обратный адрес.Таким образом, еще 3 нажатия повторно выравнивают этот стек.Одно пустое нажатие на запись функции настроит вас на функции с 8 байтами аргументов.

Также обычно вы бы add $8, %esp после вызова вернулись, чтобы удалить аргументы из стека.


Имена переменных: double - это имя типа (и ключевое слово) в C. Это делает его странным именем для программы сборки, которая вызывает функции C.Что-то вроде dbl может выглядеть лучше.

...