在 C 语言中编译并运行不带 main()的程序

我试图编译和运行以下程序没有 C中的 main()函数。我使用以下命令编译了我的程序。

gcc -nostartfiles nomain.c

编译器会给出警告

/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340

好的,没有问题。然后,我运行可执行文件(a.out) ,两个 printf语句都成功打印,然后得到 内存区段错误

我的问题是 为什么在成功执行 print 语句后内存区段错误?

我的代码:

#include <stdio.h>


void nomain()
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
}

产出:

Hello World...
Successfully run without main...
Segmentation fault (core dumped)

注:

在这里,-nostartfiles gcc 标志阻止编译器在链接时使用标准启动文件

10770 次浏览

在 C 语言中,当函数/子例程被调用时,堆栈被填充为(按顺序) :

  1. 争吵,
  2. 回邮地址,
  3. 局部变量,—— > 堆栈的顶部

Main ()作为开始点,ELF 以这样一种方式构造程序: 无论什么指令先出现,都会先被推送,在这种情况下,printfs 是。

现在,程序在没有返回地址或者 __end__的情况下被截断了,事实上它假设在那个(__end__)位置的堆栈上的任何东西都是返回地址,但不幸的是它不是,因此它崩溃了。

让我们看一下程序生成的 集合:

.LC0:
.string "Hello World..."
.LC1:
.string "Successfully run without main..."
nomain:
push    rbp
mov     rbp, rsp
mov     edi, OFFSET FLAT:.LC0
call    puts
mov     edi, OFFSET FLAT:.LC1
call    puts
nop
pop     rbp
ret

注意 ret语句。您的程序的入口点被确定为 nomain,这样就可以了。但是一旦函数返回,它就会尝试跳转到调用堆栈上的一个地址... ... 这个地址不会被填充。这是非法进入,后面还有内存区段错误。

一个快速的解决方案是在程序结束时调用 exit()(假设 C11,我们也可以将函数标记为 _Noreturn) :

#include <stdio.h>
#include <stdlib.h>


_Noreturn void nomain(void)
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
exit(0);
}

实际上,现在函数的行为非常类似于常规的 main函数,因为从 main返回之后,将使用 main的返回值调用 exit函数。