4

从这个问题,我看到了一个有趣的代码,它编译(尽管有警告)并产生分段错误(gcc 4.4.4;clang 2.8):

main;

如果我们展开它,结果如下:

int main = 0;

那么链接器在这里的行为是什么?

4

3 回答 3

4

链接器的行为是它定义了main在程序数据或 BSS 段中调用的符号。它长 4 个字节并初始化为 0。通常,它在程序的代码段(通常称为.text)中创建一个符号,其中包含函数的可执行代码main

C 运行时在一个固定的入口点(通常称为_start)启动,初始化一堆东西(例如设置程序的参数),然后调用main函数。当main是可执行代码时,这一切都很好,但如果它是 4 个零字节,程序会将控制权转移到那些零字节并尝试执行它们。

通常,数据和 BSS 段被标记为不可执行,因此当您尝试在那里执行代码时,处理器将引发异常,操作系统将解释该异常,然后使用信号终止您的程序。如果它所在的段以某种方式是可执行的,那么它将尝试执行由00 00 00 00. 在 x86 和 x86-64 中,这是一条非法指令,因此您也会SIGILL在 POSIX 操作系统中获得信号。

于 2012-10-11T15:41:06.087 回答
3

该符号main应该是一个函数,而不是一个整数。但是,链接器不太关心 ; 的类型main。符号已定义。如果符号main不是具有指定签名之一的函数,则调用未定义的行为。

然后启动代码调用“函数”,它实际上是程序数据段中的一个地址。出错是因为存储在该地址的“代码”无效——前 4 个字节可能为零;后来发生的事情是任何人的猜测。数据段可能被标记为不可执行,在这种情况下尝试执行数据将触发该帐户的崩溃。

当您调用未定义的行为时,任何事情都可能发生。崩溃在这里是非常明智的。

于 2012-10-11T15:39:39.353 回答
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) 
于 2012-10-11T15:40:56.737 回答