Я изучаю разработку ассемблера в Windows 10 (64-разрядной версии) и использую IntelliJ IDEA для написания ассемблерного кода. Я пытаюсь зарегистрировать несколько выходных данных с помощью WriteConsoleA
, но появляется только первое сообщение журнала. Второе сообщение журнала игнорируется.
Сначала я попытался реализовать арифметическую логику, чтобы получить сумму двух чисел. Это не сработало, и я попытался жестко запрограммировать значение и получить результат журнала, а затем понял эту проблему.
Вот моя подробная настройка и код, который я использую.
Среда:
Код:
section .data
sum_msg db 'Sum: ', 0
sum db '15', 0
section .bss
num_bytes_written resq 1
section .text
global _start
extern GetStdHandle
extern WriteConsoleA
extern ExitProcess
_start:
; Get the handle for stdout
mov ecx, -11 ; STD_OUTPUT_HANDLE
call GetStdHandle
; Check if handle is valid
cmp rax, -1
je .exit
; Write the "Sum: " message to stdout
mov rcx, rax ; handle to stdout
lea rdx, [rel sum_msg] ; pointer to message
mov r8d, 5 ; message length
lea r9, [rel num_bytes_written] ; pointer to number of bytes written
call WriteConsoleA
; Write the hardcoded sum to stdout
mov rcx, rax ; handle to stdout
lea rdx, [rel sum] ; pointer to sum string
mov r8d, 2 ; sum string length (including null terminator)
lea r9, [rel num_bytes_written] ; pointer to number of bytes written
call WriteConsoleA
; Infinite loop to prevent immediate exit
.loop:
nop
jmp .loop
.exit:
; Exit
xor ecx, ecx ; exit code 0
call ExitProcess
Команды: Собрать код:
nasm -f win64 sum.asm -o sum.obj
Код ссылки:
gcc -nostdlib -o sum.exe sum.obj -lkernel32
Ожидаемый результат:
Sum: 15
Фактический результат:
Sum:
Попытки исправить:
WriteConsoleA
(сверху: «15», снизу: «Сумма:»), и она записала результат «15».Что именно мне нужно знать
Любая помощь или предложения будут очень признательны.
Вы также вызвали все функции в смещенном стеке, не выделили теневое пространство и не объявили коды очистки.
Соглашение о вызовах Windows x64 описано здесь.
Спасибо, что указали на это. Я обновил свой код, чтобы гарантировать, что все пять параметров передаются в WriteConsoleA. Кроме того, я сохранил значение GetStdHandle, чтобы избежать его перезаписи возвращаемым значением WriteConsoleA. Эта настройка решила проблему.
Превращаем комментарии в ответ:
Здесь есть несколько проблем. Вероятно, наиболее важным является то, что возвращаемое значение вашего первого вызова WriteConsoleA перезаписывает значение eax, в котором вы сохраняете дескриптор, возвращенный из GetStdHandle. В результате второй вызов WriteConsoleA использует неверный дескриптор.
Во-вторых, вы передаете только 4 параметра в процедуру, которая принимает 5. Хотя это может сработать, поскольку параметр 5 зарезервирован (и, следовательно, вероятно, полностью не используется), это риск, поскольку невозможно узнать, что вызываемая функция может делать с это или пространство стека, где оно должно храниться.
В-третьих, вы не можете выделить теневое пространство для параметров (обсуждается здесь). Опять же, вам это может сойти с рук в зависимости от того, как работает вызываемая функция. Но если он потерпит неудачу, сбой может произойти через сотни инструкций, что очень затруднит его отслеживание.
Аналогично: стек всегда будет поддерживаться по 16-байтовому выравниванию, за исключением пролога (например, после отправки адреса возврата). Вы вызываете функции со стеком, не выровненным по границам 16 байт.
Рэймонд также ссылается на коды очистки. Хотя я сам их не указываю, я обычно не использую обработку исключений. Вероятно, ужасные вещи произойдут, если вы опустите их в коде, который выполняет throw
, или даже находится в стеке вызовов при срабатывании исключения.
Написание кода, который не соответствует должным образом соглашениям о вызовах, — ужасная идея. Может показаться, что это работает, но это портит вещи, которые вы не можете легко увидеть. Проще сделать все правильно с первого раза, чем потом тратить время на отслеживание.
Спасибо за организованный и разъясненный ответ :)
Похоже, вы передаете в WriteConsoleA только 4 параметра, но функция принимает 5. Кроме того, WriteConsoleA возвращает значение в eax, что означает, что он удаляет значение, сохраненное там GetStdHandle.