2017-02-19 128 views
69

我试图在C中编译并运行下面的程序,但没有main()函数。我使用以下命令编译了我的程序。编译并运行没有main()的程序在C中为

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标志链接

时防止编译器使用标准的启动文件
+32

我很惊讶这项作品。坦率地说,我认为链接器的这种处理方式是错误的(或者至少是一件坏事):没有入口点,所以链接器只是让它从任何函数都很方便而产生幻觉。布莱什。 – imallett

+4

@imallett,至少该链接器足以引起人们对它的警告,并解释它正在采取的回退行动!不过,你说得对,这可能更好,因为错误而不仅仅是警告。 –

+0

你为什么不用main? –

回答

116

让我们来看看你的程序产生的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...\n"); 
    printf("Successfully run without main...\n"); 
    exit(0); 
} 

事实上,现在你的函数的行为很像一个常规的main函数,因为从main返回后,exit函数调用main的返回值。

+6

我认为有一些架构/操作系统组合,你可以从程序中“返回”; MS-DOS .COM可执行文件?无论如何,我们深入到特定于实现的行为。 – pjc50

+4

@ pjc50 - 我们的确如此。尽管OP中的路径建议使用Unix变体。加上某些体系结构和指令集的普及,我觉得在回答中提供生成的程序集的唯一原因。 – StoryTeller

+1

只是一个观察。 '-nostartfiles'也可以使C库不可用。如果不执行_C_启动,则对_C_库函数的后续调用可能会意外失败。在Linux上,如果你想用'-nostartupfiles'和'-static'编译,你可能会发现程序会出错。像MUSL这样的_C_库不需要预先初始化,它们可以在这种环境下工作。 –

20

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

  1. 的参数,
  2. 返回地址,
  3. 局部变量, - >顶部的堆栈

的main()是在任何指令自带第一会得到先推了,在这种情况下,用printfs是这样的起点,ELF结构的程序。

现在,程序是有点截断,没有返回地址或__end__和INFACT它假定,无论是那里的堆栈在上(__end__)的位置是返回地址,但遗憾的是它没有因此崩溃。

+4

C标准定义了堆栈数据的顺序吗?我认为这取决于系统架构 –

+1

这就是为什么我提到ELF(可执行文件和可链接文件格式),这是通过在所需的操作系统上针对特定ARCH类型进行交叉编译而生成的。 –

+0

挑剔的是,即使在没有堆栈的系统上,也可以使用ELF格式。这种系统的一个例子是使用Codewarrior编译器生成ELF链接器文件的[Freescale RS08](https://en.wikipedia.org/wiki/Freescale_RS08)。 – Lundin