全局变量和 .data 部分

sam*_*249 0 c assembly static global-variables sections

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

举一个基本的例子:

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

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

}
Run Code Online (Sandbox Code Playgroud)

会编译成类似的东西:

global_int:
        .byte   11
main:
    ...
Run Code Online (Sandbox Code Playgroud)

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

Cra*_*tey 5

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


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

char global_int = 11;
char nondata_int;

int
main(int argc, char *argv[])
{
}
Run Code Online (Sandbox Code Playgroud)

编译产生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    $0, %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
Run Code Online (Sandbox Code Playgroud)

请注意.data将变量放在global_int数据部分中。并且,.comm放入nondata_int.bss部分

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

松散地说,.data和/或是变量放入的部分.bss。并且,全局 [ ] 是可见性。如果你这样做:.globl

static int foobar = 63;
Run Code Online (Sandbox Code Playgroud)

然后,foobar将进入该.data部分,但是本地的。在nm下面的输出中,D它不是 ,而是d指示本地/静态可见性。其他.o文件将无法看到此[或链接到它]。


nm该程序的一个.o结果是:

0000000000000000 D global_int
0000000000000000 T main
0000000000000001 C nondata_int
Run Code Online (Sandbox Code Playgroud)

并且,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__
Run Code Online (Sandbox Code Playgroud)

更新:

感谢您的回答。关于And,.comm放入节nondata_int.bss。您能解释一下吗?我没有看到任何对 .bss 的引用,那么这两者有什么关系呢?

当然。当你这样做时,可能有一个更严格但宽松的解释:

int nondata_int;
Run Code Online (Sandbox Code Playgroud)

您正在定义一个“common”节变量[历史起源来自Fortran的common]。

当链接[创建最终的可执行文件]时,如果没有其他.o[或.a]为其声明了值,它将.bss作为B节中。

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

int nondata_int = 43;
Run Code Online (Sandbox Code Playgroud)

在那里,define_it.o将其.data作为D符号

然后,当您链接两者时:

gcc -o executable fix1.o define_it.o
Run Code Online (Sandbox Code Playgroud)

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

因此,.o文件具有/使用.comm[汇编器指令]并且C公共部分。

可执行文件只有.data, 和.bss. 因此,给定文件,如果公共符号从未被初始化并且如果有的话.o,则该公共符号将进入[被提升为].bss.data .o

宽松地说,.comm/C 是一个建议,.data并且.bss一个“承诺”

这是一种美好的感觉。从技术上讲,在 中fix1.c,如果我们事先知道我们将与 链接define_it.o,我们[可能]会想要这样做:

extern char nondata_int;
Run Code Online (Sandbox Code Playgroud)

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

但是,那么,如果fix1.o不是到任何定义该符号的东西,链接器就会抱怨未定义的符号。

通用符号允许我们拥有多个 .o文件,每个文件的作用如下:

int nondata_int;
Run Code Online (Sandbox Code Playgroud)

它们都产生C符号。链接器将所有内容组合起来生成一个

所以,又常见了C符号又是:

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

历史上...

IIRC [我对此可能是错的],common 被添加到 [链接器] 以支持 FortranCOMMON声明/变量。

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

经典/旧的 fortran 只能将变量指定为COMMON(即在 C 中,相当于int val;),但 fortran没有全局初始值设定项(即它没有extern int val;int val = 1;

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

在过去的美好时光(tm),通用链接器类型不存在extern,除了一个文件和一个[且只有一个]声明它的文件外,所有链接器类型都必须有一个显式链接器类型.o。声明.o它可以使用值(例如)int val = 1;不使用值(例如)来定义它int val;,但所有其他.o文件必须使用extern int val;