.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.o
和 b.o
链接后,a
和 b
符号都进入 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 文件去哪儿了?