在Linux / OS X上,堆栈指针指向_start入口上的argc(有关进程启动环境的更多详细信息,请参见i386或x86-64 System V ABI文档);内核在启动用户空间之前将命令行参数放入用户空间堆栈内存。(因此,如果您确实尝试ret,EIP/RIP = argc =一个小整数,而不是一个有效的地址。如果您的调试器在地址0x00000001或其他位置显示错误,这就是原因。) 对于Windows,它是ExitProcess,Linux是系统调用-int 80H(使用sys_exit),对于x86或使用syscall(使用60)(对于64位),或者从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(amd 64)
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)
1条答案
按热度按时间7qhs6swi1#
因为
ret
不是在Linux、Windows或Mac中退出程序的正确方式!!!!_start
不是函数,堆栈上没有返回地址因为没有要返回的用户空间调用方。用户空间中的执行从此处(在静态可执行文件中)的进程入口点开始。(或者使用动态链接,它在动态链接器完成后跳到此处,但结果相同)。在Linux / OS X上,堆栈指针指向
_start
入口上的argc
(有关进程启动环境的更多详细信息,请参见i386或x86-64 System V ABI文档);内核在启动用户空间之前将命令行参数放入用户空间堆栈内存。(因此,如果您确实尝试ret
,EIP/RIP = argc =一个小整数,而不是一个有效的地址。如果您的调试器在地址0x00000001
或其他位置显示错误,这就是原因。)对于Windows,它是
ExitProcess
,Linux是系统调用-int 80H
(使用sys_exit
),对于x86或使用syscall
(使用60
)(对于64位),或者从C库调用exit
(如果您链接到它)。32位Linux(i386)
64位Linux(amd 64)
(In GAS你实际上可以通过
#include <sys/syscall.h>
或<asm/unistd.h>
来获得你要组装.S
的模式的正确数字,但是NASM不能很容易地使用C预处理器(参见Polygot include file for nasm/yasm and C以获得提示)。32位Windows(x86)
或Windows/Linux链接到C库
(Or对于32位x86 Windows,为
call _exit
,因为C名称前面会加上下划线,这与x86-64 Windows不同。如果Windows有POSIX_exit
函数,则该函数为call __exit
。)Windows x64的调用约定包括调用者必须保留的影子空间,但是
exit
不会返回,所以可以让它在返回地址上方的空间上步进。此外,除了32位Windows之外,在call exit
之前的调用约定要求16字节堆栈对齐,但是对于像exit()
这样的简单函数,通常不会实际崩溃。call exit
(与原始退出系统调用或libc_exit
不同)将首先刷新stdio缓冲区。如果您从_start
使用了printf
,请使用exit
确保在退出前打印所有输出,即使stdout被重定向到文件(使stdout成为全缓冲区,而不是行缓冲区)。如果使用libc函数,通常建议您编写一个
main
函数并与gcc链接,以便由您可以ret
调用的正常CRT启动函数调用它。另请参阅
将
main
定义为_start
福尔斯的东西并没有使它变得特别,只是如果它不像_start
调用的Cmain
函数那样,在main
返回后准备退出,那么使用main
标签会让人感到困惑。