为啥公共部分变量只显示在目标文件而不是可执行文件中?

Posted

技术标签:

【中文标题】为啥公共部分变量只显示在目标文件而不是可执行文件中?【英文标题】:Why do common section variables only show up in object file not the executable?为什么公共部分变量只显示在目标文件而不是可执行文件中? 【发布时间】:2013-02-12 19:21:01 【问题描述】:

我试图更多地了解可执行文件的“公共”部分,我注意到在编译代码上执行objdump 时,我可以看到仅在目标文件上的公共代码中放置的变量 (*.o ) 不在可执行文件上。

这是为什么呢?

//test.c

int i[1000];
int main()return 0;

构建命令:

> gcc -g0 -fcommon -c test.c
> gcc -g0 -fcommon test.c

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

> objdump -x test.o
  ...
  SYMBOL TABLE:
  ...
  00000fa0    O   *COM*   00000020  i

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

> objdump -x a.out
  ...
  SYMBOL TABLE:
  ...
  0804a040 g  O   .bss    00000fa0  i

如果我使用-fno-common 标志重建目标文件,它会显示在.bss 段中,就像它在可执行文件上一样。最终的可执行文件没有这个“COMMON”部分吗?

【问题讨论】:

【参考方案1】:

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

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

来自-fcommon/-fno-common上的 gcc 手册

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

所以,-fno-common-fcommon 只有在有多个名为 i 的全局变量时才会有所不同[并且它们应该具有相同的大小,否则您的程序会变得无效,这会差一个档次比未定义的行为!]

【讨论】:

好的...那么公共部分的意义何在? fcommonfno-common 的最终结果是未初始化的变量最终出现在可执行文件的 .bss 段中;还是因为与您写"[a typical]" 相同的原因而存在?表示有例外 @Mike 但是编译器不知道这一点 - 这是链接器的工作来解决这个问题。对于提供 .bss 部分的目标(和可执行格式),通常在运行时分配和初始化 bss 部分。在不具备这种能力的目标上,公共部分最终位于可执行文件中,如果您有一个 10Mb 的全局数组,那么您的可执行文件最终会变成 10Mb 大。这也允许您编写自己的链接器脚本,并将这些公共部分放置在您想要的位置。 -fno-common 还有其他用途,但 gcc 手册页对此进行了说明。 我说“典型”是因为在某些架构中可以添加额外的部分,并且这些部分也可以分配各种属性。并非所有编译器、链接器等都支持这一点,gcc 能够在具有大量操作系统架构的大量系统上运行。我不想有人指出,在 OS glurf 上,使用链接器 X,你有一些名为 ...的部分,等等。 AFAIK,如果在使用 -fno-common 编译的两个不同目标文件中有两个名为 i 的全局变量,并且您尝试链接它们,则链接器将出错。 我不确定我观察到了什么,但是当尝试在 Apple 平台上测试某些程序时,Valgrind 点亮了一棵圣诞树。苹果要么(1)将一些初始化的全局变量放在一起,要么(2)对它们所在的部分执行延迟初始化。我可以阻止 Valgrind 发现的唯一方法是在问题变量。我们可能应该在命令行中添加-fno-common

以上是关于为啥公共部分变量只显示在目标文件而不是可执行文件中?的主要内容,如果未能解决你的问题,请参考以下文章

为啥要先编译成目标文件?

为啥在构建依赖库之前没有开始编译目标可执行文件?

ARM编译错误,可执行文件使用的VFP寄存器,而不是目标文件

3dmax贴图打包保存可为啥显示没有有效的max文件路径

为啥我的可执行文件大小从 780K 增加到 1.1MB?

为啥在 Mailcore2 Sample 中的“MCOMessageView”文件是一个 c++ 文件而不是目标 c 文件?