全局变量和 .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
注意 .data
将 global_int
变量放入数据部分。并且,.comm
将 nondata_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;
存储在.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
注意 .data
将 global_int
变量放入数据部分。并且,.comm
将 nondata_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 putnondata_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;