因为在Linux、Windows或者Mac中,ret
不是退出程序的正确方式!!!
_start
不是一个函数,在栈上没有返回地址,因为没有用户空间的调用者可以返回。程序执行从这里开始(在静态可执行文件中),在进程入口点。(或者在动态链接时,它在动态链接器完成后跳转到这里,但结果相同)。
在Linux / OS X中,进程启动时,栈指针指向 _start
的 argc
参数(有关进程启动环境的更多详细信息,请参见 i386 或 x86-64 系统 V ABI 文档);内核在启动用户空间之前将命令行参数放入用户空间堆栈内存中。(所以如果你尝试使用 ret
,EIP/RIP = argc = 一个小整数,而不是一个有效地址。如果你的调试器显示地址为 0x00000001
或其他值的故障,那就是原因。)
在 Windows 中,使用 ExitProcess
,在 Linux 中使用系统调用 - 使用 sys_exit
,对于 x86 可以使用 int 80H
,对于 64 位可以使用 syscall
并使用 60
,或者如果你链接到 C 库,可以调用 exit
。
32 位 Linux (i386)
%define SYS_exit 1 ; call number __NR_exit from <asm/unistd_32.h>
mov eax, SYS_exit ; use the NASM macro we defined earlier
xor ebx, ebx ; ebx = 0 exit status
int 80H ; _exit(0)
64位 Linux (amd64)
mov rax, 60 ; SYS_exit aka __NR_exit from asm/unistd_64.h
xor rdi, rdi ; edi = 0 first arg to 64-bit system calls
syscall ; _exit(0)
(在GAS中,您实际上可以使用#include <sys/syscall.h>
或<asm/unistd.h>
来获取您为之汇编.S
文件的模式所需的正确数字,但NASM无法轻松使用C预处理器。请参见nasm/yasm和C的多语言包含文件获取提示。)
32位Windows(x86)
push 0
call ExitProcess
链接C库的Windows/Linux
; pass an int exit_status as appropriate for the calling convention
; push 0 / xor edi,edi / xor ecx,ecx
call exit
对于32位x86 Windows平台,应该使用call _exit
,因为C函数名前会加一个下划线而不像在x86-64 Windows上那样没有。如果Windows平台有POSIX的_exit
函数,它将是call __exit
。
Windows x64调用约定包括阴影空间,调用者必须预留这个空间,但exit
函数不会返回,所以让它占用返回地址上面的空间是可以的。在call exit
之前除了32位Windows外,调用约定要求16字节栈对齐,但对于像exit()
这样的简单函数,通常不会出现问题。
call exit
(与原始的退出系统调用或libc库的_exit
不同)将首先刷新stdio缓冲区。如果您从_start
使用printf
,请使用exit
确保在退出之前打印所有输出,即使stdout被重定向到文件(将stdout设置成全缓冲而不是行缓冲)。
通常建议如果您使用libc函数,编写一个main
函数并链接gcc,以便普通CRT启动函数调用它,您可以通过ret
返回。
另请参阅:
将main
定义为_start
可以跳转到的内容并不使它特殊,如果不像C main
函数那样由准备在main
返回后退出的_start
调用,则使用main
标签是令人困惑的。
mov eax,1 mov ebx,0 int 80h
我认为你在最后一行使用了ret
,导致出现了分段错误。 - pearcoding