SIGSEGV,“ret”时出现分段错误

时间:2016-07-07 12:40:23

标签: assembly segmentation-fault masm32

我正在学习如何使用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”,但在尝试“退回”后出现问题:“编程接收信号SIGSEGV,分段故障。”。我该怎么办?

1 个答案:

答案 0 :(得分:0)

由于无法正确清理堆栈,您无法平衡堆栈。

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

.model flat, stdcall

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

  1. 从右到左将参数压入堆栈。
  2. 被调用者负责在返回之前清理堆栈。
  3. 第一个特征与其他常见调用约定cdecl相同,但第二个特征正好相反。这是你在这种情况下出错的原因。 (实际上,你的代码根本不会清理堆栈,所以不管调用约定它都会被破坏!)

    基本上,当您使用stdcall调用约定时,您将使用立即作为参数的ret instruction版本。该参数指定返回时从堆栈中弹出的字节数。

    在这种情况下,您有一个参数,即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将控制权返回给Windows。 All the MASM32 samples我见过do it this way

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

    但我可能错了。我实际上并没有使用MASM32 SDK。通常,您的入口点将是一个cdecl函数,它只会返回ret的控件。