SIGSEGV,执行 "ret" 时出现分段错误

SIGSEGV, Segmentation fault while doing "ret"

我正在学习如何使用 masm32 中的过程,所以我编写了一个写数字的过程:

.386
.model flat, stdcall
option casemap : none

include \masm32\include\masm32.inc
include \masm32\include\kernel32.inc
include \masm32\macros\macros.asm
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\kernel32.lib

.data
    number dw 397
    temp db 10
    symbol dw ?
    i dw ?

.code
    printnumber proc num:WORD
    mov ecx, 0
    mov ax, num
    @@:
    mov edx, 0
    div temp
    mov bh, 0
    mov bl, ah    
    push bx
    inc cx
    cmp al, 0
    mov bl, al
    mov ax, bx
    jnz @B
    mov i, cx
    @@:
    pop symbol
    add symbol, 48
    mov ax, symbol
    print ADDR symbol
    dec i
    cmp i, 0
    jnz @B
    ret
printnumber endp

start:
    push number
    call printnumber
    ret ;here program fails
end start

程序成功打印“397”,但在尝试 "ret" 后出现问题:"Program received signal SIGSEGV, Segmentation fault."。我该怎么办?

您未能正确清理堆栈,导致堆栈失衡。

在你的代码文件的顶部,你有这个指令:

.model flat, stdcall

重要的部分是 stdcall 指令,它指定您的函数将使用的调用约定。这是 Windows 程序最常用的调用约定,与 Windows API 使用的相同。 stdcall调用约定有两个重要的特点:

  1. 参数从右到左压入堆栈。
  2. 被调用者负责在returns之前清理堆栈。

第一个特征与另一个常见的调用约定cdecl相同,但第二个特征恰恰相反。在这种情况下,这就是您的错误。 (实际上,您的代码根本没有清理堆栈,因此无论调用约定如何,它都会被破坏!)

基本上,当您使用 stdcall 调用约定时,您将使用采用立即数作为参数的 ret instruction 版本。该参数指定在 return.

时从堆栈中弹出的字节数

在这种情况下,您有一个参数,WORD 大小的 number,因此您将使用 ret 2:

printnumber proc num:WORD
    mov ecx, 0
    mov ax, num
    @@:
    mov edx, 0
    div temp
    mov bh, 0
    mov bl, ah    
    push bx
    inc cx
    cmp al, 0
    mov bl, al
    mov ax, bx
    jnz @B
    mov i, cx
    @@:
    pop symbol
    add symbol, 48
    mov ax, symbol
    print ADDR symbol
    dec i
    cmp i, 0
    jnz @B
    ret 2         ; clean up the stack by popping 2 bytes,
                  ;  since 2 bytes were pushed by the caller
printnumber endp

除此之外,我相信使用MASM32的start入口点标签,你必须调用ExitProcess到return控制到 Windows。 All the MASM32 samples I've seen do it this way:

start:
    push number
    call printnumber
    invoke ExitProcess, 0
end start

但我对此可能是错误的。我实际上并没有使用 MASM32 SDK。通常,您的入口点是一个 cdecl 函数,它可以简单地 return 控制 ret.