在C中编译并运行没有main()的程序

msc*_*msc 78 c gcc program-entry-point segmentation-fault gcc-warning

我正在尝试编译并运行以下程序而没有main()函数C.我使用以下命令编译了我的程序.

gcc -nostartfiles nomain.c
Run Code Online (Sandbox Code Playgroud)

编译器发出警告

/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340
Run Code Online (Sandbox Code Playgroud)

好的,没问题.然后,我已经运行了可执行文件(a.out),两个printf语句都成功打印,然后得到分段错误.

那么,我的问题是,为什么在成功执行print语句后出现分段错误?

我的代码:

#include <stdio.h>

void nomain()
{
        printf("Hello World...\n");
        printf("Successfully run without main...\n");
}
Run Code Online (Sandbox Code Playgroud)

输出:

Hello World...
Successfully run without main...
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)

注意:

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

Sto*_*ica 129

让我们来看看生成汇编程序的:

.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
Run Code Online (Sandbox Code Playgroud)

请注意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);
}
Run Code Online (Sandbox Code Playgroud)

实际上,现在你的函数的行为非常类似于常规main函数,因为在返回之后main,exit函数被调用了main返回值.

  • 我认为有一些架构/操作系统组合,你可以从程序中"返回"; MS-DOS .COM可执行文件?无论如何,我们深入了解特定于实现的行为. (6认同)
  • @ pjc50 - 我们确实如此.尽管OP中的路径提出了Unix变体.加上某些架构和指令集的流行,这是我觉得在答案中呈现生成的程序集的唯一原因. (4认同)

Mil*_*ore 21

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

  1. 争论,
  2. 退货地址,
  3. 局部变量, - > 堆栈顶部

main()是起点,ELF以这样的方式构造程序,即首先推送的任何指令都会先被推送,在这种情况下是printfs.

现在,程序有点被截断而没有返回地址OR,__end__并且事实上它假设在该__end__位置的堆栈上的任何内容都是返回地址,但不幸的是它没有,因此它崩溃了.

  • 堆栈数据的顺序是由C标准定义的吗?我认为这取决于系统架构 (4认同)