简体   繁体   English

静态局部变量在哪里

[英]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. 13i移动到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 05mov部分
  • 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 AAdded-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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM