从这个question开始,我看到了一个有趣的代码编译(尽管有警告)并产生分段错误(gcc 4.4.4; clang 2.8):
main;
如果我们扩展它,结果如下:
int main = 0;
那么链接器的行为是什么?
答案 0 :(得分:4)
链接器的行为是它在程序的数据或BSS段中定义了一个名为main
的符号。它长4个字节并初始化为0.通常,它在程序的代码段(通常称为.text
)中创建一个带有main
函数可执行代码的符号。
C运行时在固定的入口点(通常称为_start
)启动,初始化一堆东西(例如设置程序的参数),并调用main
函数。当main
是可执行代码时,这一切都很好,但是如果它是4个零字节,程序会将控制转移到那些零字节并尝试执行它们。
通常,数据和BSS段被标记为不可执行,因此当您尝试在那里执行代码时,处理器将引发异常,操作系统将解释该异常,然后使用信号终止程序。如果它所处的段是可执行的,那么它将尝试执行00 00 00 00
定义的机器指令。在x86和x86-64中,这是非法指令,因此您还可以在POSIX操作系统中获得SIGILL
信号。
答案 1 :(得分:3)
符号main
应该是一个函数,而不是整数。但是,链接器并不太关心main
的类型;符号已定义。如果符号main
不是具有指定签名之一的函数,则调用未定义的行为。
启动代码然后调用'function',它实际上是程序数据段中的一个地址。它出错了,因为存储在该地址的“代码”无效 - 前4个字节可能是零;后来的是任何人的猜测。数据段可能被标记为不可执行,在这种情况下,尝试执行数据将触发该帐户崩溃。
当您调用未定义的行为时,任何事情都可能发生。崩溃是非常明智的。
答案 2 :(得分:3)
在我的系统(CentOS 6.3)下,main被放入BSS并包含全部0,因此崩溃:
Program received signal SIGSEGV, Segmentation fault.
0x00000000006007f0 in main ()
(gdb) where
#0 0x00000000006007f0 in main ()
(gdb) l
"main" is not a function
(gdb) disass 0x6007f0
Dump of assembler code for function main:
=> 0x00000000006007f0 <+0>: add %al,(%rax)
0x00000000006007f2 <+2>: add %al,(%rax)
End of assembler dump.
(gdb) info symbol &main
main in section .bss of /home/ajd/tmp/x
(gdb) x/16b 0x6007f0
0x6007f0 <main>: 0 0 0 0 0 0 0 0
0x6007f8: 0 0 0 0 0 0 0 0
(gdb)