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

Compile and run program without main() in C

我试图在 C 中编译并 运行 跟随没有 main() 函数的程序。我已经使用以下命令编译了我的程序。

gcc -nostartfiles nomain.c

并且编译器给出警告

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

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

所以,我的问题是,为什么成功执行打印语句后会出现段错误?

我的代码:

#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 标志阻止编译器在链接时使用标准启动文件

在 C 中,当调用 functions/subroutines 时,堆栈填充为(按顺序):

  1. 参数,
  2. Return地址,
  3. 局部变量,--> 栈顶

main() 作为起点,ELF 以这样一种方式构建程序,即无论先到的指令都会先被推送,在这种情况下,printfs 就是。

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

让我们看看您的程序生成的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,一切都很好。但是一旦函数returns,它就会尝试跳转到调用堆栈上的一个地址……没有填充。这是非法访问,随后出现分段错误。

一个快速的解决方案是在程序结束时调用 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 调用 return 之后,exit 函数使用 main的return值。