变量及函数在内存中的位置

从一个CPP文件编译成ELF可执行文件过程中会把不同的变量和函数映射到不同的内存区域。这些不同的区域具有不同的访问权限,有的是只读的,有的是读写的,有的是可执行的。让我们举个简单的例子来了解一下。

示例代码

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>

template<typename dst_type,typename src_type>
dst_type pointer_cast(src_type src)
{
return *static_cast<dst_type*>(static_cast<void*>(&src));
}

int global_uninit_var;
int global_init_var = 255;
const int const_global_int = 255;

void func() {
printf("Just a function\n");
}
static void static_func() {
}
inline void inline_func() {
}

typedef void (*pfunc_t)();
typedef int (*main_func_t)();
typedef void (*static_func_t)();

class Simple {
public:
static void Show() {
printf("I am Show...\n");
}
void localshow() {
printf("I am localshow...\n");
}
};

int main() {
static int static_var = 255;

int local_uninit_var;
int local_init_var = -1;

const int const_local_int = 127;

int* heap_int = new int();

pfunc_t pf = func;
main_func_t mf = main;
static_func_t sf = static_func;
static_func_t csf = Simple::Show;
void* cpf = pointer_cast<void*>(&Simple::localshow);
pfunc_t ipf = inline_func;

printf("global_uninit_var: 0x%x\n", &global_uninit_var);
printf("global_init_var: 0x%x\n", &global_init_var);
printf("static_var: 0x%x\n", &static_var);
printf("const_global_int: 0x%x\n", &const_global_int);
printf("local_uninit_var: 0x%x\n", &local_uninit_var);
printf("local_init_var: 0x%x\n", &local_init_var);
printf("const_local_int: 0x%x\n", &const_local_int);
printf("heap_int: 0x%x, 0x%x\n", &heap_int, heap_int);
printf("point_func: 0x%x, 0x%x\n", &pf, pf);
printf("point_main_func: 0x%x, 0x%x\n", &mf, mf);
printf("static_func: 0x%x, 0x%x\n", &sf, sf);
printf("class_static_func: 0x%x, 0x%x\n", &csf, csf);
printf("class_local_func: 0x%x, 0x%x\n", &cpf, cpf);
printf("inline_func: 0x%x, 0x%x\n", &ipf, ipf);

return 0;
}

由于ELF格式中有很多信息,我们只取readelf --sections相关信息
ELF结构如下:

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
[Nr] Name              Type             Address           Offset       Size              EntSize          Flags  Link  Info  Align
[ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000004002a8 000002a8 000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.bu[...] NOTE 00000000004002c4 000002c4 0000000000000024 0000000000000000 A 0 0 4
[ 3] .note.ABI-tag NOTE 00000000004002e8 000002e8 0000000000000020 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400308 00000308 000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000400328 00000328 0000000000000090 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 00000000004003b8 000003b8 000000000000007d 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400436 00000436 000000000000000c 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400448 00000448 0000000000000040 0000000000000000 A 6 2 8
[ 9] .rela.dyn RELA 0000000000400488 00000488 0000000000000018 0000000000000018 A 5 0 8
[10] .rela.plt RELA 00000000004004a0 000004a0 0000000000000078 0000000000000018 AI 5 22 8
[11] .init PROGBITS 0000000000401000 00001000 000000000000001a 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000401020 00001020 0000000000000060 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000401080 00001080 0000000000000392 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 0000000000401414 00001414 0000000000000009 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 0000000000402000 00002000 000000000000019e 0000000000000000 A 0 0 8
[16] .eh_frame_hdr PROGBITS 00000000004021a0 000021a0 0000000000000064 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 0000000000402208 00002208 00000000000001b8 0000000000000000 A 0 0 8
[18] .init_array INIT_ARRAY 0000000000403de8 00002de8 0000000000000008 0000000000000008 WA 0 0 8
[19] .fini_array FINI_ARRAY 0000000000403df0 00002df0 0000000000000008 0000000000000008 WA 0 0 8
[20] .dynamic DYNAMIC 0000000000403df8 00002df8 0000000000000200 0000000000000010 WA 6 0 8
[21] .got PROGBITS 0000000000403ff8 00002ff8 0000000000000008 0000000000000008 WA 0 0 8
[22] .got.plt PROGBITS 0000000000404000 00003000 0000000000000040 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000404040 00003040 000000000000000c 0000000000000000 WA 0 0 4
[24] .bss NOBITS 000000000040404c 0000304c 000000000000000c 0000000000000000 WA 0 0 4
[25] .comment PROGBITS 0000000000000000 0000304c 000000000000005c 0000000000000001 MS 0 0 1
[26] .debug_aranges PROGBITS 0000000000000000 000030a8 0000000000000070 0000000000000000 0 0 1
[27] .debug_info PROGBITS 0000000000000000 00003118 000000000000031a 0000000000000000 0 0 1
[28] .debug_abbrev PROGBITS 0000000000000000 00003432 00000000000001d9 0000000000000000 0 0 1
[29] .debug_line PROGBITS 0000000000000000 0000360b 0000000000000114 0000000000000000 0 0 1
[30] .debug_str PROGBITS 0000000000000000 0000371f 0000000000000235 0000000000000001 MS 0 0 1
[31] .debug_ranges PROGBITS 0000000000000000 00003954 0000000000000060 0000000000000000 0 0 1
[32] .symtab SYMTAB 0000000000000000 000039b8 0000000000000750 0000000000000018 33 52 8
[33] .strtab STRTAB 0000000000000000 00004108 0000000000000298 0000000000000000 0 0 1
[34] .shstrtab STRTAB 0000000000000000 000043a0 0000000000000151 0000000000000000 0 0 1

将上述代码编译运行打印出的结果对应到elf格式中相应的区域如下:

variable

  • 所有代码无聊作何标记全部存储在.text段,换句话说从汇编的角度可以调用任何函数都可以被调用
  • 全局的const常量存放在.rodata
  • 局部的const常量则存储在栈空间内,而常量的右值127则存放在.text
  • 已经初始化的全局变量或static变量存放在.data
  • 未初始化的全局变量存放在bss