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?
如果静态全局变量和静态局部变量的名称相同,编译器如何区分它们?
3 个解决方案
#1
11
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段。
#2
2
Static variable is almost similar to global variable and hence the uninitialized static variable is in BSS and the initialized static variable is in data segment.
静态变量几乎与全局变量相似,因此未初始化的静态变量在BSS中,初始化的静态变量在数据段中。
#3
0
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 = 1
goes on.data
static int i = 0继续.bss
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:
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 theeax
to prepare for the increment -
d
moves the incremented value back to memory -
13
movesi
to theeax
for the return value. It is obviously unnecessary sinceeax
already contains it, and-O3
is able to remove that.
4移动到eax以准备增量
d将递增的值移回内存
13将i移动到eax以获得返回值。显然没必要,因为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:
偏移量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 themov
part -
00 00 00 00
is the address part, which starts at byte6
8b 05是mov部分
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
-
A
: theAdded
:-4
-
P
: the address of byte 6 when loaded
S:段指向:.data
答:增加:-4
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段的第一个字节。
#1
11
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段。
#2
2
Static variable is almost similar to global variable and hence the uninitialized static variable is in BSS and the initialized static variable is in data segment.
静态变量几乎与全局变量相似,因此未初始化的静态变量在BSS中,初始化的静态变量在数据段中。
#3
0
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 = 1
goes on.data
static int i = 0继续.bss
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:
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 theeax
to prepare for the increment -
d
moves the incremented value back to memory -
13
movesi
to theeax
for the return value. It is obviously unnecessary sinceeax
already contains it, and-O3
is able to remove that.
4移动到eax以准备增量
d将递增的值移回内存
13将i移动到eax以获得返回值。显然没必要,因为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:
偏移量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 themov
part -
00 00 00 00
is the address part, which starts at byte6
8b 05是mov部分
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
-
A
: theAdded
:-4
-
P
: the address of byte 6 when loaded
S:段指向:.data
答:增加:-4
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段的第一个字节。