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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在C中编译并运行没有main()的程序相关的知识,希望对你有一定的参考价值。

我正在尝试编译并运行以下程序,而不使用main()中的C函数。我使用以下命令编译了我的程序。

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...
");
        printf("Successfully run without main...
");
}

输出:

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

注意:

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

答案

让我们看一下你的程序生成的assembly

.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...
");
    printf("Successfully run without main...
");
    exit(0);
}

事实上,现在你的函数表现得非常像常规的main函数,因为从main返回后,exit函数被调用main的返回值。

另一答案

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

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

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

现在,程序有点被截断而没有返回地址或者__end__,事实上它假设堆栈处的那个(__end__)位置是返回地址,但不幸的是它没有,因此它崩溃了。

以上是关于在C中编译并运行没有main()的程序的主要内容,如果未能解决你的问题,请参考以下文章

C语言程序是如何执行的

在C中挣扎着TicTacToe

C程序从编译到运行

main函数是必须的吗

JNI用C加载JDK产生JVM虚拟机,并运行JAVA类main函数(MACOS/LINUX/WINDOWS)

JNI用C加载JDK产生JVM虚拟机,并运行JAVA类main函数(MACOS/LINUX/WINDOWS)