.bss vs COMMON:啥去哪儿了?

Posted

技术标签:

【中文标题】.bss vs COMMON:啥去哪儿了?【英文标题】:.bss vs COMMON: what goes where?.bss vs COMMON:什么去哪儿了? 【发布时间】:2013-05-26 00:02:39 【问题描述】:

从我的书中:

.bss:

未初始化的全局 C 变量

常见:

尚未分配的未初始化数据对象

我不得不说,我看不出明显的区别。我什至不太明白什么是未初始化、未分配的数据对象……似乎什么都没有。我使用 GNU 的 readelf 工具尝试查看一些简单的 C 代码,但找不到单个 COMMON 符号。我读过诸如 FORTRAN 的 COMMON 类型是 COMMON 符号的示例之类的东西 - 但我不知道 FORTRAN

谁能帮我区分这两者?如果可能的话,希望有一个 C 示例?非常感谢。

编辑:来自this的帖子,这里的变量c:

int c;
int main()  ...

应该是常见的。但是使用 objdump -t 对我来说表明 c 在 .bss 中...

困惑

【问题讨论】:

计算机系统,第 2 版。作者:布莱恩特,奥哈拉隆 【参考方案1】:
// file a.c
// file-scope

int a = 0;  // goes into BSS

a.c 编译成目标文件a.o 后,a 符号进入 BSS 部分。

// file b.c
// file-scope

int b;  // goes into COMMON section

在将b.c 编译成目标文件b.o 后,b 符号进入 COMMON 部分。

a.ob.o 链接后,ab 符号都进入 BSS 部分。通用符号只存在于目标文件中,不存在于可执行文件中。 Unix 中 COMMON 符号的思想是允许在特定条件下在单个公共符号下对同一个变量(在不同编译单元中)进行多个外部定义。

【讨论】:

啊,这很有意义!将其标记为正确,因为它比 Art 的答案更紧凑 int a = 0; 不一定要进入 .bss。这实际上是对 gcc 的最新更改。将某些东西初始化为 0 用于将符号强制转换为旧 gcc 版本的数据,并且通常在您需要编辑二进制文件同时保持初始化变量为 0 时使用。最常 (ab) 由内核使用。另请注意,公共不一定必须进入 bss,有可能将一堆公共解析为数据部分中的符号。 这是特定于 Unix 还是 .elf 格式?我习惯 .elf 但不习惯 Unix,而且我以前从未听说过“COMMON”。 @Lundin 我认为它最初来自 Unix,后来以 ELF 格式重用。 这也是 C++ 不同的地方之一。至少 gcc 的行为就像为 C++ 代码指定了 -fno-common 一样。【参考方案2】:

Common 仅出现在链接阶段之前。 Commons 是后来进入 bss 或数据的内容,但由链接器决定它的去向。这允许您在不同的编译单元中定义相同的变量。据我所知,这主要是为了允许一些包含int foo; 而不是extern int foo; 的古老头文件。

它是这样工作的:

$ cat > a.c
int foo;
$ cat > b.c
int foo;
$ cat > main.c
extern int foo;
int main(int argc, char **argv)  return foo; 
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
$ objdump -t a.o | grep foo
0000000000000004       O *COM*  0000000000000004 foo
$ objdump -t b.o | grep foo
0000000000000004       O *COM*  0000000000000004 foo
$ objdump -t x | grep foo
0000000000600828 g     O .bss   0000000000000004              foo
$

请注意,这仅在不同编译单元中的最多一个变量被初始化时才有效。

$ echo "int foo = 0;" > a.c
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
$ echo "int foo = 0;" > b.c
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
b.o:(.bss+0x0): multiple definition of `foo'
a.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status
$

这是可怕的东西,与古老的系统兼容,你永远不应该依赖它。正确地做事——在所有编译单元中只定义一个全局变量,通过头文件在其他任何地方声明它。

【讨论】:

我使用readelf -S a.o时没有这个COMMON部分,为什么? (与int a; 在交流) @elinx Commons 不是一个部分,而是在链接阶段放置在正确部分的特殊标记符号。 readelf -S 转储部分。当您使用readelf -s 转储符号时,Commons 将显示。【参考方案3】:

如果您允许common 在链接不同的单元时可以声明相同的变量,链接器会将它们定位在相同的位置。类型甚至不需要相同,所以它是某种链接时间联合。这是 Fortran 的 COMMON 功能。如果您不允许common 链接C 那么,这种情况将导致链接时间错误。这种common 链接仅适用于未初始化的全局变量,否则不清楚应该进行哪个初始化。

转到bss 的全局变量只是 C 定义为初始化为 0 的未初始化全局变量。大多数对象格式支持仅给出大小的部分,加载器将用零填充整个部分。

P.S:如果您使用 gcc,您可以使用 -fno-common 选项将 common 符号强制添加到 bss 部分,正如 Art 认为的那样,这是一种很好的可取做法。

【讨论】:

【参考方案4】:

静态变量最终位于 .bss 部分。未初始化的全局变量(非静态)位于 .common 部分。

static a;  //bss
int c;   //.common
main()

【讨论】:

COMMON 不是一个部分。

以上是关于.bss vs COMMON:啥去哪儿了?的主要内容,如果未能解决你的问题,请参考以下文章

从 sonar-runner 迁移到 MSBuild Runner。 sonar-project.properties 文件去哪儿了?

去哪儿网怎么沦为骗子的平台了,一步步揭开去哪儿网欺骗消费者的把戏

去哪儿网消息队列设计与实现

我提交的文件去哪儿了?

POST 请求去哪儿了?

AutowirePartialView 可绑定属性去哪儿了?