NASM ReadConsoleA 或 WriteConsoleA 缓冲区调试问题

NASM ReadConsoleA or WriteConsoleA Buffer Debugging Issue

我正在 Windows 上编写一个 NASM 汇编程序,让用户输入两个单位数字,将它们相加,然后输出结果。我正在尝试使用 Windows API 作为输入和输出。

不幸的是,虽然我可以让它在程序循环后立即读取一个数字以获得第二个数字,但程序结束而不是要求第二个数字。

程序的输出如下所示:

有趣的是,如果我输入 1,那么显示的值会大一个,所以它正在添加一些东西!

这也适用于输入的其他个位数 (2-9)。

我很确定这与我使用 ReadConsoleA 函数的方式有关,但我在尝试寻找解决方案时遇到了一些困难。我安装了gdb调试程序,组装如下:

但我得到以下错误:

我后来读到 NASM 不输出 Win64 格式所需的调试信息,但我对此不是 100% 确定。我相当确定我安装了 64 位版本的 GDB:

我的程序如下:

extern ExitProcess                          ;windows API function to exit process
extern WriteConsoleA                        ;windows API function to write to the console window (ANSI version)
extern ReadConsoleA                         ;windows API function to read from the console window (ANSI version)
extern GetStdHandle                         ;windows API to get the for the console handle for input/output

section .data                               ;the .data section is where variables and constants are defined

STD_OUTPUT_HANDLE   equ -11
STD_INPUT_HANDLE    equ -10

digits      db      '0123456789'            ;list of digits

input_message db   'Please enter your next number: '
length equ $-input_message

section .bss                                ;the .bss section is where space is reserved for additional variables

input_buffer:   resb 2                      ;reserve 64 bits for user input

char_written:   resb    4
chars:   resb 1                             ;reversed for use with write operation

section .text                               ;the .text section is where the program code goes

global _main                                ;tells the machine which label to start program execution from

_num_to_str:
        cmp rax, 0                          ;compare value in rax to 0
        jne .convert                        ;if not equal then jump to label
        jmp .output

.convert:
        ;get next digit value
        inc r15                             ;increment the counter for next digit

        mov rcx, 10
        xor rdx, rdx                        ;clear previous remainder result
        div rcx                             ;divide value in rax by value in rcx
                                            ;quotient (result) stored in rax
                                            ;remainder stored in rdx

        push rdx                            ;store remainder on the stack

        jmp _num_to_str

.output:
        pop rdx                             ;get the last digit from the stack

        ;convert digit value to ascii character
        mov r10, digits                     ;load the address of the digits into rsi
        add r10, rdx                        ;get the character of the digits string to display

        mov rdx, r10                        ;digit to print
        mov r8, 1                           ;one byte to be output

        call _print

        ;decide whether to loop
        dec r15                             ;reduce remaining digits (having printed one)
        cmp r15, 0                          ;are there digits left to print?
        jne .output                          ;if not equal then jump to label output

        ret

_print:
        ;get the output handle
        mov rcx, STD_OUTPUT_HANDLE          ;specifies that the output handle is required
        call GetStdHandle                   ;returns value for handle to rax

        mov rcx, rax
        mov r9, char_written

        call WriteConsoleA

        ret

_read:
        ;get the input handle
        mov rcx, STD_INPUT_HANDLE           ;specifies that the input handle is required
        call GetStdHandle

        ;get value from keyboard
        mov rcx, rax                        ;place the handle for operation

        mov rdx, input_buffer               ;set name to receive input from keyboard

        mov r8, 2                           ;max number of characters to read
        mov r9, chars                       ;stores the number of characters actually read

        call ReadConsoleA


        movzx r12, byte[input_buffer]

        ret

_get_value:
        mov rdx, input_message              ;move the input message into rdx for function call
        mov r8, length                      ;load the length of the message for function call

        call _print
        xor r8, r8
        xor r9, r9
        call _read
.end:
        ret

_main:
        mov r13, 0                          ;counter for values input
        mov r14, 0                          ;total for calculation
.loop:
        xor r12, r12
        call _get_value                     ;get value from user

        sub r12, '0'                        ;convert char to integer
        add r14, r12                        ;add value to total

        ;decide whether to loop for another character or not
        inc r13
        cmp r13, 2
        jne .loop

        ;convert total to ASCII value

        mov rax, r14                             ;num_to_str expects total in rax

        mov r15, 0                               ;num_to_str uses r15 as a counter - must be initialised
        call _num_to_str

        ;exit the program
        mov rcx, 0                          ;exit code
        call ExitProcess

如果您能提供解决问题或如何使用 gdb 解决问题的任何帮助,我将不胜感激。

我发现您的代码存在以下问题:

  1. Microsoft x86-64 convention 要求 rsp 16 字节对齐。
  2. 您必须为堆栈中的参数保留 space,即使您在寄存器中传递它们也是如此。
  3. 您的 chars 变量需要 4 个字节而不是 1 个字节。
  4. ReadConsole 需要 5 个参数。
  5. 你应该读取 3 个字节,因为 ReadConsole returns CR LF。或者你可以忽略领先的白色space.
  6. 如果输入 0,你的 _num_to_str 就坏了。

根据 Jester 的 建议,这是最终程序:

extern ExitProcess                          ;windows API function to exit process
extern WriteConsoleA                        ;windows API function to write to the console window (ANSI version)
extern ReadConsoleA                         ;windows API function to read from the console window (ANSI version)
extern GetStdHandle                         ;windows API to get the for the console handle for input/output

section .data                               ;the .data section is where variables and constants are defined

STD_OUTPUT_HANDLE   equ -11
STD_INPUT_HANDLE    equ -10

digits      db      '0123456789'            ;list of digits

input_message db   'Please enter your next number: '
length equ $-input_message

NULL        equ     0

section .bss                                ;the .bss section is where space is reserved for additional variables

input_buffer:   resb 3                      ;reserve 64 bits for user input

char_written:   resb    4
chars:   resb 4                             ;reversed for use with write operation

section .text                               ;the .text section is where the program code goes

global _main                                ;tells the machine which label to start program execution from

_num_to_str:
        sub rsp, 32
        cmp rax, 0
        jne .next_digit
        push rax
        inc r15
        jmp .output
.next_digit:
        cmp rax, 0                          ;compare value in rax to 0
        jne .convert                        ;if not equal then jump to label
        jmp .output

.convert:
        ;get next digit value
        inc r15                             ;increment the counter for next digit

        mov rcx, 10
        xor rdx, rdx                        ;clear previous remainder result
        div rcx                             ;divide value in rax by value in rcx
                                            ;quotient (result) stored in rax
                                            ;remainder stored in rdx

        sub rsp, 8                          ;add space on stack for value
        push rdx                            ;store remainder on the stack

        jmp .next_digit

.output:
        pop rdx                             ;get the last digit from the stack
        add rsp, 8                          ;remove space from stack for popped value

        ;convert digit value to ascii character
        mov r10, digits                     ;load the address of the digits into rsi
        add r10, rdx                        ;get the character of the digits string to display

        mov rdx, r10                        ;digit to print
        mov r8, 1                           ;one byte to be output

        call _print

        ;decide whether to loop
        dec r15                             ;reduce remaining digits (having printed one)
        cmp r15, 0                          ;are there digits left to print?
        jne .output                          ;if not equal then jump to label output
        add rsp, 32
        ret

_print:
        sub rsp, 40
        ;get the output handle
        mov rcx, STD_OUTPUT_HANDLE          ;specifies that the output handle is required
        call GetStdHandle                   ;returns value for handle to rax

        mov rcx, rax
        mov r9, char_written

        mov rax, qword 0                    ;fifth argument
        mov qword [rsp+0x20], rax

        call WriteConsoleA
        add rsp, 40
        ret

_read:
        sub rsp, 40
        ;get the input handle
        mov rcx, STD_INPUT_HANDLE           ;specifies that the input handle is required
        call GetStdHandle

        ;get value from keyboard
        mov rcx, rax                        ;place the handle for operation
        xor rdx, rdx
        mov rdx, input_buffer               ;set name to receive input from keyboard



        mov r8, 3                           ;max number of characters to read
        mov r9, chars                       ;stores the number of characters actually read

        mov rax, qword 0                    ;fifth argument
        mov qword [rsp+0x20], rax

        call ReadConsoleA


        movzx r12, byte[input_buffer]
        add rsp, 40
        ret

_get_value:
        sub rsp, 40

        mov rdx, input_message              ;move the input message into rdx for function call
        mov r8, length                      ;load the length of the message for function call

        call _print
        call _read
.end:
        add rsp, 40
        ret

_main:
        sub rsp, 40
        mov r13, 0                          ;counter for values input
        mov r14, 0                          ;total for calculation
.loop:
        call _get_value                     ;get value from user

        sub r12, '0'                        ;convert char to integer
        add r14, r12                        ;add value to total

        ;decide whether to loop for another character or not
        inc r13
        cmp r13, 2
        jne .loop

        ;convert total to ASCII value

        mov rax, r14                             ;num_to_str expects total in rax

        mov r15, 0                               ;num_to_str uses r15 as a counter - must be initialised
        call _num_to_str

        ;exit the program
        mov rcx, 0                          ;exit code

        call ExitProcess
        add rsp, 40
        ret

事实证明,我实际上也遗漏了 WriteConsole 函数中的第 5 个参数。