[英]Where do static local variables go
Where are static local variables stored in memory? 静态局部变量存储在内存中的哪个位置? Local variables can be accessed only inside the function in which they are declared.
只能在声明它们的函数内访问局部变量。
Global static variables go into the .data segment. 全局静态变量进入.data段。
If both the name of the static global and static local variable are same, how does the compiler distinguish them? 如果静态全局变量和静态局部变量的名称相同,编译器如何区分它们?
Static variables go into the same segment as global variables. 静态变量与全局变量进入同一段。 The only thing that's different between the two is that the compiler "hides" all static variables from the linker: only the names of extern (global) variables get exposed.
两者之间唯一不同的是编译器“隐藏”链接器中的所有静态变量:只暴露外部(全局)变量的名称。 That is how compilers allow static variables with the same name to exist in different translation units.
这就是编译器允许具有相同名称的静态变量存在于不同翻译单元中的方式。 Names of static variables remain known during the compilation phase, but then their data is placed into the
.data
segment anonymously. 静态变量的名称在编译阶段仍然是已知的,但随后它们的数据将匿名放入
.data
段。
静态变量几乎与全局变量相似,因此未初始化的静态变量在BSS中,初始化的静态变量在数据段中。
As mentioned by dasblinken, GCC 4.8 puts local statics on the same place as globals. 正如dasblinken所提到的,GCC 4.8将局部静态与全局变量放在同一个地方。
More precisely: 更确切地说:
static int i = 0
goes on .bss
static int i = 0
继续.bss
static int i = 1
goes on .data
static int i = 1
继续.data
Let's analyze one Linux x86-64 ELF example to see it ourselves: 让我们分析一个Linux x86-64 ELF示例来自己查看:
#include <stdio.h>
int f() {
static int i = 1;
i++;
return i;
}
int main() {
printf("%d\n", f());
printf("%d\n", f());
return 0;
}
To reach conclusions, we need to understand the relocation information. 为了得出结论,我们需要了解搬迁信息。 If you've never touched that, consider reading this post first .
如果你从未接触到这一点,请考虑先阅读这篇文章 。
Compile it: 编译它:
gcc -ggdb -c main.c
Decompile the code with: 用以下代码反编译代码:
objdump -S main.o
f
contains: f
包含:
int f() {
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
static int i = 1;
i++;
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <f+0xa>
a: 83 c0 01 add $0x1,%eax
d: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 13 <f+0x13>
return i;
13: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 19 <f+0x19>
}
19: 5d pop %rbp
1a: c3 retq
Which does 3 accesses to i
: 哪3次访问
i
:
4
moves to the eax
to prepare for the increment 4
移动到eax
以准备增量 d
moves the incremented value back to memory d
将递增的值移回内存 13
moves i
to the eax
for the return value. 13
将i
移动到eax
以获得返回值。 It is obviously unnecessary since eax
already contains it, and -O3
is able to remove that. eax
已经包含它, -O3
能够删除它。 So let's focus just on 4
: 所以我们只关注
4
:
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <f+0xa>
Let's look at the relocation data: 我们来看看重定位数据:
readelf -r main.o
which says how the text section addresses will be modified by the linker when it is making the executable. 其中说明了链接器在创建可执行文件时如何修改文本部分地址。
It contains: 它包含:
Relocation section '.rela.text' at offset 0x660 contains 9 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000006 000300000002 R_X86_64_PC32 0000000000000000 .data - 4
We look at .rela.text
and not the others because we are interested in relocations of .text
. 我们查看
.rela.text
而不是其他文件,因为我们对.text
重定位感兴趣。
Offset 6
falls right into the instruction that starts at byte 4: Offset 6
落在从字节4开始的指令中:
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <f+0xa>
^^
This is offset 6
From our knowledge of x86-64 instruction encoding: 根据我们对x86-64指令编码的了解:
8b 05
is the mov
part 8b 05
是mov
部分 00 00 00 00
is the address part, which starts at byte 6
00 00 00 00
是地址部分,从字节6
开始 AMD64 System V ABI Update tells us that R_X86_64_PC32
acts on 4 bytes ( 00 00 00 00
) and calculates the address as: AMD64 System V ABI更新告诉我们
R_X86_64_PC32
作用于4个字节( 00 00 00 00
)并计算地址为:
S + A - P
which means: 意思是:
S
: the segment pointed to: .data
S
:段指向: .data
A
: the Added
: -4
A
: Added
: -4
P
: the address of byte 6 when loaded P
:加载时字节6的地址 -P
is needed because GCC used RIP
relative addressing, so we must discount the position in .text
-P
是必需的,因为GCC使用RIP
相对寻址,因此我们必须在.text
折扣位置
-4
is needed because RIP
points to the following instruction at byte 0xA
but P
is byte 0x6
, so we need to discount 4. 需要
-4
,因为RIP
指向字节0xA
处的以下指令但P
是字节0x6
,因此我们需要折扣4。
Conclusion: after linking it will point to the first byte of the .data
segment. 结论:链接后,它将指向
.data
段的第一个字节。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.