C++问题剖析:C++的static关键字的作用(我从elf结构,链接过程来回答)
内容目录

 
 

0x01 C++的static关键字主要在以下场景中用到

 

1)static修饰全局变量和局部变量

全局变量属于数据,放在.data或.bss段,如果是常量字符串的话,那就放在.rodata段,接下来看一下一个普通全局变量和static全局变量有什么区别?
 

int gdata1 = 10;
static int gdata2 = 10;

int main()
{
    return 0;
}

 

Linux下创建a.c文件,查看符号表:

delphi@delphi-vm:~$ g++ -c a.cpp -o a.o
delphi@delphi-vm:~$ objdump -t a.o

a.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 a.cpp
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000004 l     O .data  00000004 _ZL6gdata2
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .eh_frame  00000000 .eh_frame
00000000 l    d  .comment   00000000 .comment
00000000 g     O .data  00000004 gdata1
00000000 g     F .text  0000000a main
00000000         *UND*  00000000 __gxx_personality_v0

delphi@delphi-vm:~$ 

 

从上图的符号表可以看出,全局变量gdata1是“g”代表global符号,在链接阶段其他的obj文件是可以看到的,这样的符号是要参与链接过程符号解析的;gdata2是“l”代表一个local本地符号,这样的符号只能当前obj文件可见,是不参与链接符号解析过程的,因此多个源文件可以定义同名的static全局变量,不会产生重复定义的错误。

 

函数中的普通局部变量是属于指令,是存放在.text代码段上的,运行时系统给函数在栈上分配空间,函数的局部变量此时表示栈上的一段内存;但是static静态局部变量就成为数据了,放在.data或.bss段上了,程序一起来它就有内存,第一次运行到它的时候进行初始化,整个进程结束,它的内存才释放。

 

2)static修饰普通函数

static修饰普通函数和上面static修饰全局变量的意思是一样的,函数经过编译,产生一个函数符号,被static修饰后,就成为了一个“l”的local符号了,只能当前obj文件可见,不参与链接的符号解析,因此其它文件不能链接这个文件中的static函数。
 

3)static修饰成员方法和成员变量成为静态成员方法和静态成员变量

static修饰成员方法就变成静态成员方法了,形参不会再生成this指针,因此调用的时候不再依赖于对象,用类的作用域来调用就可以了;static修饰成员变量,那么此时这样的成员变量就不属于对象的,而是属于类的,它的内存已经放在.data或者.bss段上了,同样访问不需要依赖对象了,通过类作用域就能够访问,相当于是落在类作用域下的全局变量。

===============================
 
 

0x02 补充知识:查看符号表的工具&手法

2.1. 使用nm工具:

  • nm是一个常用的工具,用于查看目标文件的符号表。
  • 默认情况下,nm显示符号的地址、类型和名称。
  • 运行以下命令查看符号表:
  • nm a.o

delphi@delphi-vm:~$ g++ -c a.cpp -o a.o
delphi@delphi-vm:~$ nm a.o
00000004 d _ZL6gdata2
         U __gxx_personality_v0
00000000 D gdata1
00000000 T main
delphi@delphi-vm:~$ 

 

符号类型标识:

  • 在nm工具的输出中,符号类型用单个字母表示,常见的有:
  • T或t: 代码段(text section),通常是函数。
  • D或d: 已初始化的数据段(data section)。
  • B或b: 未初始化的数据段(bss section)。
  • U: 未定义的符号。
  • 符**号可见性**:
  • 符号的可见性(全局或本地)通常通过符号的大小写来表示:
  • 大写字母(如D、B、T)表示全局符号。
  • 小写字母(如d、b、t)表示本地符号。

 
具体来分析这里:

gdata1

  • 符号类型: D
  • 在符号表输出中,gdata1的符号类型是D,表示它是一个全局符号,位于已初始化的数据段(.data段)。
  • 符号可见性:
  • gdata1是一个全局符号,全局符号意味着它可以被其他目标文件访问和链接。
  • 在符号表中,它显示为gdata1,没有前缀或修饰符。

 

gdata2

  • 符号类型: d
  • gdata2的符号类型是d,表示它是一个本地符号,位于已初始化的数据段(.data段)。
  • gdata2是一个static全局变量,意味着它在当前编译单元内可见,不会被导出到其他目标文件。
  • 符号修饰: _ZL6gdata2
  • _ZL是GCC编译器对static变量的修饰,表示这是一个本地符号(local)。
  • 6gdata2是符号名的修饰,6表示符号名的长度。

总结一下:

  • gdata1: 是一个全局变量,存储在.data段,初始化为10,可以被其他目标文件访问。
  • gdata2: 是一个static全局变量,存储在.data段,初始化为10,仅在当前编译单元内可见,不会被导出。
  • 在nm工具的输出中,通过符号类型的大小写可以判断符号的可见性。大写字母表示全局符号,小写字母表示本地符号。因此,gdata1是全局符号的结论是从其符号类型D得出的。

通过这些符号信息呢,可以了解变量的存储位置和可见性,这对于理解程序的链接和内存布局蛮有用。

 
 

2.2. 使用readelf工具:

  • readelf可以提供更详细的符号信息,包括符号的绑定(Binding)属性。
  • 运行以下命令查看符号表:
  • readelf -s a.o

– 注意:在输出中,Binding列会显示GLOBAL或LOCAL,表示符号的可见性。

  • GLOBAL: 符号是全局的,可以被其他目标文件访问。
  • LOCAL: 符号是本地的,仅在当前目标文件可见。

 

delphi@delphi-vm:~$ readelf -s a.o

Symbol table '.symtab' contains 12 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS a.cpp
     2: 00000000     0 SECTION LOCAL  DEFAULT    1 
     3: 00000000     0 SECTION LOCAL  DEFAULT    2 
     4: 00000000     0 SECTION LOCAL  DEFAULT    3 
     5: 00000004     4 OBJECT  LOCAL  DEFAULT    2 _ZL6gdata2
     6: 00000000     0 SECTION LOCAL  DEFAULT    5 
     7: 00000000     0 SECTION LOCAL  DEFAULT    6 
     8: 00000000     0 SECTION LOCAL  DEFAULT    4 
     9: 00000000     4 OBJECT  GLOBAL DEFAULT    2 gdata1
    10: 00000000    10 FUNC    GLOBAL DEFAULT    1 main
    11: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND __gxx_personality_v0
delphi@delphi-vm:~$ 

 
 

2.3.使用objdump工具:

  • objdump也可以用于查看符号表,提供详细的符号信息。
  • 运行以下命令查看符号表:
  • objdump -t a.o

 

delphi@delphi-vm:~$ objdump -t a.o

a.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 a.cpp
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000004 l     O .data  00000004 _ZL6gdata2
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .eh_frame  00000000 .eh_frame
00000000 l    d  .comment   00000000 .comment
00000000 g     O .data  00000004 gdata1
00000000 g     F .text  0000000a main
00000000         *UND*  00000000 __gxx_personality_v0

delphi@delphi-vm:~$ 
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇