Linux x86 NASM-подпрограмма: печать dword из EAX
Итак, я изучаю сборку x86 Linux с синтаксисом NASM (О боже, только не это, вы все думаете). Я пытаюсь сделать подпрограмму, которая просто напечатает значение в EAX в stdout. Код запускается и выходит без ошибок, но ничего не печатается. Не могу понять почему. Прежде всего, вот файл, в котором я работаю:
segment .bss
to_print: resd 1
segment .text
global print_eax_val
print_eax_val: ; (top)
push dword ebx ;Stack: edx
push dword ecx ; ecx
push dword edx ; ebx
; (bot)
mov ecx,eax ;ecx = eax
mov [to_print],ecx ;to_print = ecx
mov eax, 4 ;sys_write
mov ebx, 1 ;to stdout
add ecx, 47 ;add 47 for ASCII numbers
mov edx, 2 ;double word = 2 bytes
int 0x80
mov eax, [to_print] ;eax = original val
pop edx ;pop the registers back from the stack
pop ecx
pop ebx ;Stack: empty
ret
это вызывается из моего основного файла, который выглядит так (это, вероятно, не имеет значения, если я чего-то не хватает резкий.)
segment .data
hello db "Hello world!", 0
newline db 0xA
len equ $ - hello
len2 equ $ - newline
segment .text
extern print_nl
extern print_eax_val
global main
main:
enter 0,0
call print_nl
mov eax, 1
call print_eax_val
mov ebx, 0 ;exit code = 0 (normal)
mov eax, 1 ;exit command
int 0x80 ;ask kernel to quit
print_nl
- это еще одна подпрограмма, которая определяет и печатает новую строку. Это успешно работает и печатает новую строку, как ожидалось.
проблема связана с параметром length для my sys_write
звонок? Я даю ему 2, который имеет размер dword
, что составляет размер EAX
регистрация и мой to_print
ярлык, который я забронировал с resd 1
. В отчаянии я попытался изменить длину на 1, 4, 8, 16 и 32... Ничего работал.
EDIT: для всех, кто интересуется, вот как я исправил код: (я поставлю звездочки на строки, которые я изменил):
segment .bss
to_print: resd 1
segment .text
global print_eax_val
print_eax_val: ; (top)
push dword ebx ;Stack: edx
push dword ecx ; ecx
push dword edx ; ebx
; (bot)
mov ecx,eax ;ecx = eax
mov [to_print],ecx ;to_print = ecx
**** add dword [to_print], 48
mov eax, 4 ;sys_write
mov ebx, 1 ;to stdout
**** mov ecx, to_print
mov edx, 2
int 0x80
**** sub dword [to_print], 48
mov eax, [to_print] ;eax = original val
pop edx ;pop the registers back from the stack
pop ecx
pop ebx ;Stack: empty
ret
по сути, ecx
должны быть адрес блока, который вы хотите напечатать, а не само значение. Как указано в выбранном ответе, это будет только работа, если eax находится в диапазоне 0-9.
правка 2: поэтому я был немного смущен 2-й параметр для sys_write (тот, который хранится в edx
). Я думаю, что это просто относится к числу байтов. Так dword
, как я использовал, было бы правильно использовать 4 есть, ибо двойное слово-4 байта, или 32 бита. Я предполагаю, что это сработало, потому что x86 мало-endian. Итак, в памяти шестнадцатеричное значение to_print
будет выглядеть так:
90 00 00 00
и с предоставленной длиной два, sys_write получает:
90 00
таким образом, значение к счастью, не повреждается.
позже я изменил код на store to_print
как побайтно, используя resb 1
и доступ к нему с помощью byte
вместо dword
... Байт здесь хорош, потому что я знаю, что не собираюсь давать to_print
значение выше 9.
2 ответов
Я очень ржавый в ассемблере, но похоже, что ECX содержит значение 48, когда оно должно содержать адрес буфера для записи.
Я предполагаю, что вы намеревались в print_eax_val
Это взять двоичное значение EAX, добавить смещение ASCII в цифру 0
(который должен был быть 48, а не 47), а затем распечатать этот единственный символ. Для этого добавьте 48 перед сохранением значения в to_print
, поместить адрес to_print
в ECX и установите длину (EDX) в 1, потому что вы пишу только одного персонажа.
теперь помните, что это будет работать только для значений EAX между 0x0000 и 0x0009. Когда вы пройдете мимо 9, вы получите другие символы ASCII.
объяснение того, как взять произвольное двоичное значение EAX и преобразовать его в десятичную строку, которая может быть напечатана, выходит далеко за рамки SO.
на write
системный вызов принимает буфер символов для печати, указал на %ecx
С длиной выданный %edx
. Регистр, как %eax
С другой стороны содержит 32-разрядное целое число.
если вы действительно хотите напечатать %eax
, вы должны сначала преобразовать это число в последовательность символов. Вы можете скрыть его до ASCII decimal, или hexadecimal, или любой другой базы, которую вы хотите, но вам нужно сделать его символами.