section .text
global _start
_start:
nop
main:
mov eax, 1
mov ebx, 2
xor eax, eax
ret
我用这些命令编译:
nasm -f elf main.asm
ld -melf_i386 -o main main.o
当我运行代码时,Linux 抛出分段错误错误
(我使用的是 Linux Mint Nadia 64 位)。为什么会产生这个错误?
因为ret
这不是在 Linux、Windows 或 Mac 中退出程序的正确方法!!!!
_start
不是函数,堆栈上没有返回地址,因为没有用户空间调用者可以返回。用户空间中的执行从这里开始(在静态可执行文件中),在进程入口点。(或者使用动态链接,它在动态链接器完成后跳转到这里,但结果相同)。
在 Linux / OS X 上,堆栈指针指向argc
入口_start
(有关进程启动环境的更多详细信息,请参阅 i386 或 x86-64 System V ABI 文档);内核在启动用户空间之前将命令行参数放入用户空间堆栈内存。(因此,如果您尝试这样做ret
,EIP/RIP = argc = 一个小整数,而不是有效地址。如果您的调试器在地址0x00000001
或其他地方显示错误,这就是原因。)
对于 Windows,它ExitProcess
是系统调用,Linux 是系统调用 -
int 80H
using sys_exit
, for x86 或 using syscall
using 60
for 64Bit 或exit
从 C 库中调用(如果您链接到它)。
32 位 Linux
mov eax, sys_exit ; sys_exit = 1
xor ebx, ebx
int 80H
64 位 Linux
mov rax, 60
xor rdi, rdi
syscall
视窗
push 0
call ExitProcess
或针对 C 库的 Windows/Linux 链接
call exit
exit
(与原始退出系统调用或 libc 不同_exit
)将首先刷新 stdio 缓冲区。如果您使用printf
from _start
,请使用exit
以确保在退出之前打印所有输出,即使 stdout 被重定向到文件(使 stdout 全缓冲,而不是行缓冲)。
通常建议如果您使用 libc 函数,则编写一个main
函数并与 gcc 链接,以便您可以通过正常的 CRT 启动函数调用它ret
。
定义main
为_start
落入的东西并没有使它特别,main
如果它不像一个准备在返回后退出的 Cmain
函数,那么使用标签只会令人困惑。_start
main