全局变量和 .data 部分

Global variables and the .data section

存储在.data节中的变量是否定义为具有程序作用域的全局变量?换句话说,这两个词是同义的,一个暗示另一个,或者,例如,是否有可能有一个 global 变量未存储在 .data 部分中,或者 label/variable 那不是全局的?

举一个基本的例子:

// this is compiled as in the .data section with a .globl directive
char global_int = 11;

int main(int argc, char * argv[])
{

}

会编译成类似的东西:

global_int:
        .byte   11
main:
    ...

但我正在查看这两个术语 -- 全局和“在 .data 部分中”是否是同一事物,或者是否存在反例。

有两个不同的概念:变量进入哪个“部分”及其“可见性”


为了比较,我添加了一个 .bss 部分变量:

char global_int = 11;
char nondata_int;

int
main(int argc, char *argv[])
{
}

cc -S 编译产生:

    .file   "fix1.c"
    .text
    .globl  global_int
    .data
    .type   global_int, @object
    .size   global_int, 1
global_int:
    .byte   11
    .comm   nondata_int,1,1
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
    movq    %rsi, -16(%rbp)
    movl    [=11=], %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 8.3.1 20190223 (Red Hat 8.3.1-2)"
    .section    .note.GNU-stack,"",@progbits

注意 .dataglobal_int 变量放入数据部分。并且,.commnondata_int 放入 .bss 部分

此外,请注意 .globl 以使变量具有全局可见性(即可以被其他 .o 文件看到)。

松散地,.data and/or .bss 是变量被放入的部分。而且,全局 [.globl] 是可见性。如果你这样做了:

static int foobar = 63;

然后,foobar 将进入 .data 部分,但在本地。在下面的 nm 输出中,将 d 代替 D 来指示 local/static 可见性。其他 .o 个文件 不能 看到这个 [或 link 到它]。


.o 程序的 nm 产生:

0000000000000000 D global_int
0000000000000000 T main
0000000000000001 C nondata_int

并且,最终可执行文件的 nm -g 生成:

000000000040401d B __bss_start
0000000000404018 D __data_start
0000000000404018 W data_start
0000000000401050 T _dl_relocate_static_pie
0000000000402008 R __dso_handle
000000000040401d D _edata
0000000000404020 B _end
0000000000401198 T _fini
000000000040401c D global_int
                 w __gmon_start__
0000000000401000 T _init
0000000000402000 R _IO_stdin_used
0000000000401190 T __libc_csu_fini
0000000000401120 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
0000000000401106 T main
000000000040401e B nondata_int
0000000000401020 T _start
0000000000404020 D __TMC_END__

更新:

thanks for this answer. Regarding And, .comm to put nondata_int into the .bss section. Could you please explain that a bit? I don't see any reference to .bss so how are those two related?

当然可以。可能有更严格的解释,但比较松散,当您这样做时:

int nondata_int;

你定义的是“common”段变量[历史渊源来自Fortran的common]。

当link[创建最终的可执行文件]时,如果没有其他.o[或.a]已经声明了一个值为此,它将作为 B 符号放入 .bss 部分。

但是,如果另一个 .o 已经定义了它(例如define_it.c):

int nondata_int = 43;

在那里,define_it.o 将把它作为 D 符号放在 .data 部分

然后,当你 link 两个:

gcc -o executable fix1.o define_it.o

然后,在executable中,它将作为D符号进入.data部分。

因此,.o 文件 have/use .comm [汇编指令] 和 C 公共部分。

可执行文件只有.data.bss。因此,给定 .o 文件,如果 从未 被初始化,则公共符号会 [被提升为] .bss 并且 .data 如果 any .o 已初始化.

松散地,.comm/C 是建议,.data.bss 是“承诺”

这是一个很好的选择。从技术上讲,在 fix1.c 中,如果我们事先 知道 我们将 link 与 define_it.o 编辑,我们 [可能] 想要做:

extern char nondata_int;

然后,在 fix1.o 中, 将被标记为“未定义”符号(即 nm 将显示 U)。

但是,如果 fix1.o 不是 linked 到定义符号的任何东西,linker 会抱怨一个未定义的符号。

公共符号允许我们拥有多个 .o 个文件,每个 执行:

int nondata_int;

它们都产生C个符号。 linker 将所有组合起来产生一个 单个 符号。

所以,常见的 C 符号是:

我想要一个名为 X 的全局变量,并且我希望它与在任何其他 .o 文件中找到的 X 相同,但不要抱怨符号被多重定义。如果一个 [和一个] .o 文件给它一个初始化值, 我想从这个价值中受益。

历史上...

IIRC [我对此可能是错的],common linker 被添加到 linker 以支持 Fortran COMMON declarations/variables.

也就是说,所有的 fortran .o 文件只是将一个符号声明为通用的 [它的全局概念],但是 fortran linker 应该将它们组合起来。

Classic/old fortran 只能将变量指定为 COMMON(即在 C 中,相当于 int val;)但 fortran 没有全局初始化器(即 没有 extern int val;int val = 1;

这个公共对 C 很有用,所以在某些时候添加了它。

在过去的美好时光 (tm),常见的 linker 类型 不存在 并且必须有一个明确的 extern但是一个 .o 文件和一个 [并且只有一个] 声明了它。声明它的 .o 可以用一个值(例如)int val = 1; 来定义它而没有 (例如)int val; 但所有其他 .o 文件必须使用 extern int val;