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调用约定有两个重要的特点:
- 参数从右到左压入堆栈。
- 被调用者负责在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
.
我正在学习如何使用 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调用约定有两个重要的特点:
- 参数从右到左压入堆栈。
- 被调用者负责在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
.