Сборка Win32 — WriteFile () на консоль не показывает вывод

#winapi #gcc #assembly #nasm #kernel32

#winapi #gcc #сборка #nasm #kernel32

Вопрос:

Сейчас я программирую некоторую собственную сборку Windows, используя NASM 2.12.01 и GCC 4.8.1 в качестве компоновщика.

Однако эта простая программа HelloWorld компилирует и связывает без каких-либо жалоб, но ничего не выводит на экран консоли.

Кажется, что GetStdHandle это не возвращает действительный дескриптор текущей консоли, поэтому вывод не отображается.

Но проблема может быть в чем-то другом.

Код:

 ; Name:     hello.asm
; Assemble: nasm.exe -fwin32 hello.asm
; Link:     gcc -mwindows -o hello hello.obj -lkernel32 -lmsvcrt
; Run:      a.exe

BITS 32
extern _GetStdHandle@4
extern _WriteFile@20
extern _ExitProcess@4
extern __getch
extern _puts

SECTION .data
    str:    db `Hello world!n`         ; C-like strings in NASM with backticks
    strlen  equ $-str
    pause:  db "Do you know where the ANY key is? :-)",0

SECTION .text
GLOBAL _main
_main:
; Stack frame for NumberOfBytesWritten
push ebp
sub esp, 4

; http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231.aspx
; HANDLE WINAPI GetStdHandle(
;   _In_  DWORD nStdHandle
; );
push -11
call _GetStdHandle@4

; http://msdn.microsoft.com/en-us/library/windows/desktop/aa365747.aspx
; BOOL WINAPI WriteFile(
;   _In_         HANDLE hFile,
;   _In_         LPCVOID lpBuffer,
;   _In_         DWORD nNumberOfBytesToWrite,
;   _Out_opt_    LPDWORD lpNumberOfBytesWritten,
;   _Inout_opt_  LPOVERLAPPED lpOverlapped
; );
push 0              ; lpOverlapped,
lea ebx, [ebp-4]    ; EBX: address of NumberOfBytesWritten
push ebx            ; lpNumberOfBytesWritten,
push strlen         ; nNumberOfBytesToWrite
push str            ; lpBuffer,
push eax            ; hFile (result from GetStdHandle
call _WriteFile@20

; msvcrt.dll (C library)
push pause
call _puts          ; http://msdn.microsoft.com/library/tf52y4t1.aspx
add esp, 4
call __getch        ; http://msdn.microsoft.com/library/078sfkak.aspx

; ExitProcess (0)
push 0
call _ExitProcess@4
  

Комментарии:

1. «Но проблема может быть в чем-то другом». — Почему бы не реализовать обработку ошибок, хотя бы для того, чтобы перестать гадать?

2. @IInspectable И как именно вы бы реализовали это в программе hello world ASM?

3. Есть ли инструмент Windows, который может отслеживать все системные вызовы, выполняемые процессом? В Linux / Unix strace отлично подходит для отладки подобных вещей. (Смотрите Пример вывода в этом случайном сообщении в блоге ).

4. Так же, как вы делаете это в любом другом приложении Windows. Следуя документированному контракту и оценивая возвращаемые значения.

5. @IInspectable: если вы только изучаете asm, вы, скорее всего, создадите больше ошибок, сбивая регистр или разбалансируя стек при написании кода обработки ошибок. Я бы рекомендовал использовать отладчик или инструмент трассировки для просмотра возвращаемых значений API-вызова в одношаговом режиме, вместо того, чтобы пытаться писать код в программе для их проверки. Вы не можете просто вставить debug-print в asm-программу без потенциального появления новых ошибок. (если у вас нет очень сложного макроса, который сохраняет / восстанавливает все, включая флаги).

Ответ №1:

Для создания консольного приложения необходимо использовать -mconsole опцию GCC. Смотрите Онлайн-документацию, раздел 3.18.55, Параметры Windows x86.

Вы используете -mwindows который создает приложение с графическим интерфейсом. Windows не создает консоль и не устанавливает стандартные дескрипторы при запуске приложений с графическим интерфейсом.

Комментарии:

1. Вау! Это именно то, в чем заключалась проблема! Просто не было никаких консольных дескрипторов для создания!

Ответ №2:

Вы никогда не правильно настраиваете фрейм стека!

Это неправильный способ:

 push ebp
sub esp, 4
  

Вы что-то упускаете? Ваш стек испорчен!

Расширение должно быть :

 push ebp
  mov   ebp, esp
  sub   esp, 4 
  

Для epilouge просто измените это.

Комментарии:

1. Нет необходимости устанавливать базовый указатель на какое-либо конкретное значение. Это просто удобство, чтобы упростить доступ к локальным файлам, а также к аргументам. Фактически, исключение указателя фрейма является одной из самых распространенных оптимизаций для любого компилятора языка высокого уровня. Извините, но это не отвечает на вопрос (я не знаю, кто проголосовал за это или почему).