进程的虚拟地址空间内存划分和布局

数据和指令在运行时放在内存的哪些地方?

1
2
3
4
5
6
7
8
9
// 从低地址到高地址
//.text代码段
//.rodata只读数据段
//.data 初始化的且不为0
//.bss 未初始化或为0
// .heap堆
// 共享库 *.dll *.so
// stack 栈
// 命令行参数和环境变量

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

int gdata1 = 5; // 下面输出符号表可以看到在 .data段
int gdata2 = 0; // 下面输出符号表可以看到在 .bss段
int gdata3; // 下面输出符号表可以看到在 .bss段

static int gdata4 = 6; // 下面输出符号表可以看到在 .data段
static int gdata5 = 0; // 下面输出符号表可以看到在 .bss段
static int gdata6; // 下面输出符号表可以看到在 .bss段

int main() {
int a = 7;
int b = 0;
int c;

static int d = 8;
static int e = 0;
static int f;

return 0;

}

nm命令输出可执行文件符号表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
ubuntu@ubuntu-ThinkCentre-M800t-1N000:~/learn$ g++ neicun.cpp 
ubuntu@ubuntu-ThinkCentre-M800t-1N000:~/learn$ g++ -g neicun.cpp
ubuntu@ubuntu-ThinkCentre-M800t-1N000:~/learn$
ubuntu@ubuntu-ThinkCentre-M800t-1N000:~/learn$
ubuntu@ubuntu-ThinkCentre-M800t-1N000:~/learn$
ubuntu@ubuntu-ThinkCentre-M800t-1N000:~/learn$ nm a.out
000000000000401c B __bss_start
000000000000401c b completed.8061
U __cxa_atexit@@GLIBC_2.2.5
w __cxa_finalize@@GLIBC_2.2.5
0000000000004000 D __data_start
0000000000004000 W data_start
00000000000010b0 t deregister_tm_clones
0000000000001120 t __do_global_dtors_aux
0000000000003da0 d __do_global_dtors_aux_fini_array_entry
0000000000004008 D __dso_handle
0000000000003da8 d _DYNAMIC
000000000000401c D _edata
0000000000004040 B _end
0000000000001268 T _fini
0000000000001160 t frame_dummy
0000000000003d90 d __frame_dummy_init_array_entry
00000000000021a4 r __FRAME_END__
0000000000004010 D gdata1
0000000000004020 B gdata2
0000000000004024 B gdata3
0000000000003fa8 d _GLOBAL_OFFSET_TABLE_
00000000000011d3 t _GLOBAL__sub_I_gdata1
w __gmon_start__
0000000000002008 r __GNU_EH_FRAME_HDR
0000000000001000 t _init
0000000000003da0 d __init_array_end
0000000000003d90 d __init_array_start
0000000000002000 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000001260 T __libc_csu_fini
00000000000011f0 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
0000000000001169 T main
00000000000010e0 t register_tm_clones
0000000000001080 T _start
0000000000004020 D __TMC_END__
0000000000001186 t _Z41__static_initialization_and_destruction_0ii
0000000000004014 d _ZL6gdata4
000000000000402c b _ZL6gdata5
0000000000004030 b _ZL6gdata6
U _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4
0000000000002004 r _ZStL19piecewise_construct
0000000000004028 b _ZStL8__ioinit
0000000000004018 d _ZZ4mainE1d
0000000000004034 b _ZZ4mainE1e
0000000000004038 b _ZZ4mainE1f

C++ 在其早期版本中就已经禁止了 char* 指向字符串常量。这是因为字符串常量是存储在只读内存**(.rodata只读数据段)**中的,而 char* 是可修改的,所以不允许 char* 指向它。

nm命令详情

在Linux系统中,你可以使用 nm 命令来查看C++编译后的符号表。下面是如何使用 nm 命令的步骤:

\1. 首先,你需要在终端中编译你的C++程序。例如,如果你的C++文件名为main.cpp,你可以使用以下g++命令进行编译:

1
g++ -g -o main main.cpp   

-g选项是为了在编译时包含调试信息,-o main是指定输出的可执行文件的名称。

\2. 编译成功后,你可以使用nm命令来查看符号表。例如:

1
nm main 

这将会输出你的C++程序的符号表。

请注意,nm命令的输出可能会非常复杂,因为它会列出程序中所有的符号。每一行都会包含一个符号的地址、符号类型以及符号名称。

符号类型可以是以下之一:

- “T” 或 “t” 表示该符号在文本(代码)段中。

- “D” 或 “d” 表示该符号在初始化的数据段中。

- “B” 或 “b” 表示该符号在未初始化的数据段中。

- “U” 表示该符号在其他地方定义(未定义)。

- “C” 表示该符号为未初始化的公共变量。

- “W” 表示弱符号。

其中大写字母表示全局符号,小写字母表示局部符号。

作者:张泽中