为什么常见的部分变量只出现在目标文件而不是可执行文件中?

Mik*_*ike 5 c gcc compiler-flags objdump

我正在尝试更多地了解可执行文件的"常见"部分,我注意到objdump在编译代码时,我可以看到只在对象文件(*.o而不是可执行文件)上放置在公共代码中的变量.

这是为什么?

//test.c

int i[1000];
int main(){return 0;}
Run Code Online (Sandbox Code Playgroud)

构建命令:

> gcc -g0 -fcommon -c test.c
> gcc -g0 -fcommon test.c
Run Code Online (Sandbox Code Playgroud)

objdump显示i在符号表的公共部分中:

> objdump -x test.o
  ...
  SYMBOL TABLE:
  ...
  00000fa0    O   *COM*   00000020  i
Run Code Online (Sandbox Code Playgroud)

除非我在可执行文件上运行它:

> objdump -x a.out
  ...
  SYMBOL TABLE:
  ...
  0804a040 g  O   .bss    00000fa0  i
Run Code Online (Sandbox Code Playgroud)

如果我使用-fno-common标志重建目标文件,而不是.bss像在可执行文件上那样显示在段中.最终的可执行文件没有这个"COMMON"部分吗?

Mat*_*son 6

常见部分是链接器知道的内容.它基本上将所有common内容放入[典型]可执行文件所具有的三个或四个实际部分之一(代码或文本,数据,bss - 有时也有一个rodata).

因此,在这种情况下,您的变量最终会以.bss结尾,因为它们未初始化.

从gcc手册上-fcommon/-fno-common

在C代码中,控制未初始化的全局变量的放置.Unix C编译器传统上允许通过将变量放在公共块中而在不同的编译单元中对这些变量进行多种定义.这是-fcommon指定的行为,是大多数目标上GCC的默认行为.另一方面,ISO C不要求这种行为,并且在某些目标上可能会对变量引用带来速度或代码大小的损失.-fno-common选项指定编译器应将未初始化的全局变量放在目标文件的数据部分中,而不是将它们生成为公共块.这样做的结果是,如果在两个不同的编译中声明相同的变量(没有extern),则在链接它们时会出现多重定义错误.在这种情况下,您必须使用-fcommon进行编译.使用-fno-common进行编译对于提供更好性能的目标非常有用,或者如果您希望验证程序是否可以在其他始终以这种方式处理未初始化变量声明的系统上运行.

所以,-fno-common或者-fcommon只会在有多个全局变量被称为i[并且它们应该具有相同的大小,或者您的程序变得无效,这比未定义的行为更差一个级别时才会有所作为!]

  • @Mike但编译器并不知道这一点 - 这是联系人的工作来解决这个问题.对于提供.bss节的目标(和可执行格式),通常在运行时分配和初始化bss部分.在没有该功能的目标上,公共部分最终在可执行文件中,如果您有一个10Mb的全局数组,则您的可执行文件最终会达到10Mb.这也允许您编写自己的链接描述文件,并将这些常用部分放在您想要的位置.-fno-common有其他用途,gcc手册页解释了这一点. (2认同)