测试代码为:
1 #include<stdio.h>
2 #include<stdlib.h>
3 int global_init_val=100;//初始化后的全局变量
4 int global_noninit_val;//未初始化的全局变量
5 extern char **environ;//定义全局的指针变量接收系统环境变量
6 int main(int argc,char *argv[],char* envp[])
7 {
8 static int localstaticval=10;//初始化了的静态局部变量
int i=23;//初始化的局部动态变量
9 char * localval;//局部动态变量
10 localval = malloc(10);//申请分配内存,将首指针赋给局部变量
11 printf("address of text is:%p\n",main);
12 printf("address of data is:%p,%p\n",&global_init_val,&localstaticval);
13 printf("address of bss is:%p\n",&global_noninit_val);
14 printf("address of heap is:%p\n",localval);
15 printf("address of stack is:%p\n",&localval);
16 free(localval);//释放动态分配的内存空间,如不释放,会造成内存泄露
17 printf("&environ =%p,environ = %p\n",&envp,envp);
18 printf("&argv=%p,argv=%p\n",&argv,argv);
19 return 0;
20 }
编辑makefie如下
memlayout:memlayout.o
gcc memlayout.o -o memlayout
memlayout.o:memlayout.c
gcc -c memlayout.c
输入make命令
gcc -c memlayout.c
gcc memlayout.o -o memlayout
生成的目标文件和可执行文件可进行readelf操作,有利于进一步弄清楚程序的内存空间布局。
接着执行./memlayout命令执行程序,输出结果如下:
address of text is:0x8048454
address of data is:0x804a01c,0x804a020
address of bss is:0x804a02c
address of i is:0xbff4af3c
address of data is:0x804a01c,0x804a020
address of heap is:0x9a5d008
address of stack is:0xbff4af38
&environ =0xbff4af58,environ = 0xbff4affc
&argv=0xbff4af54,argv=0xbff4aff4
按照已有的知识,从低地址到高地址依次为.text段、.data段、.bss段、.heap段、.stack段,最高地址处为命令行参数和环境变量。
各段的相对位置是确定的,但是各段的绝对起始位置是否就是上述输出结果呢?
为了搞清楚这个,我对目标文件进行readelf操作,执行命令:readelf -a memlayout.o
接着执行readelf -a memlayout,截取我们关心的部分:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048148 000148 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048168 000168 000024 00 A 0 0 4
[ 4] .hash HASH 0804818c 00018c 000030 04 A 6 0 4
[ 5] .gnu.hash GNU_HASH 080481bc 0001bc 000020 04 A 6 0 4
[ 6] .dynsym DYNSYM 080481dc 0001dc 000070 10 A 7 1 4
[ 7] .dynstr STRTAB 0804824c 00024c 000058 00 A 0 0 1
[ 8] .gnu.version VERSYM 080482a4 0002a4 00000e 02 A 6 0 2
[ 9] .gnu.version_r VERNEED 080482b4 0002b4 000020 00 A 7 1 4
[10] .rel.dyn REL 080482d4 0002d4 000008 08 A 6 0 4
[11] .rel.plt REL 080482dc 0002dc 000028 08 A 6 13 4
[12] .init PROGBITS 08048304 000304 000030 00 AX 0 0 4
[13] .plt PROGBITS 08048334 000334 000060 04 AX 0 0 4
[14] .text PROGBITS 080483a0 0003a0 00026c 00 AX 0 0 16
[15] .fini PROGBITS 0804860c 00060c 00001c 00 AX 0 0 4
[16] .rodata PROGBITS 08048628 000628 0000c8 00 A 0 0 4
[17] .eh_frame PROGBITS 080486f0 0006f0 000004 00 A 0 0 4
[18] .ctors PROGBITS 08049f0c 000f0c 000008 00 WA 0 0 4
[19] .dtors PROGBITS 08049f14 000f14 000008 00 WA 0 0 4
[20] .jcr PROGBITS 08049f1c 000f1c 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049f20 000f20 0000d0 08 WA 7 0 4
[22] .got PROGBITS 08049ff0 000ff0 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 08049ff4 000ff4 000020 04 WA 0 0 4
[24] .data PROGBITS 0804a014 001014 000010 00 WA 0 0 4
[25] .bss NOBITS 0804a024 001024 00000c 00 WA 0 0 4
[26] .comment PROGBITS 00000000 001024 000048 01 MS 0 0 1
[27] .shstrtab STRTAB 00000000 00106c 0000ee 00 0 0 1
[28] .symtab SYMTAB 00000000 00160c 000460 10 29 46 4
[29] .strtab STRTAB 00000000 001a6c 00025a 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
果然发现了问题,在14行红色标记行,.text段的起始地址赤裸裸的是0x080483a0,而不是上面输出结果显示的0x8048454(输出结果红色行)。
为什么呢?再把符号表截取出来:
Symbol table '.symtab' contains 70 entries:
Num: Value Size Type Bind Vis Ndx Name
27: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
28: 08049f0c 0 OBJECT LOCAL DEFAULT 18 __CTOR_LIST__
29: 08049f14 0 OBJECT LOCAL DEFAULT 19 __DTOR_LIST__
30: 08049f1c 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__
31: 080483d0 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
32: 0804a024 1 OBJECT LOCAL DEFAULT 25 completed.7021
33: 0804a028 4 OBJECT LOCAL DEFAULT 25 dtor_idx.7023
34: 08048430 0 FUNC LOCAL DEFAULT 14 frame_dummy
35: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
36: 08049f10 0 OBJECT LOCAL DEFAULT 18 __CTOR_END__
37: 080486f0 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
38: 08049f1c 0 OBJECT LOCAL DEFAULT 20 __JCR_END__
39: 080485e0 0 FUNC LOCAL DEFAULT 14 __do_global_ctors_aux
40: 00000000 0 FILE LOCAL DEFAULT ABS memlayout.c
41: 0804a020 4 OBJECT LOCAL DEFAULT 24 localstaticval.2184
42: 08049ff4 0 OBJECT LOCAL HIDDEN 23 _GLOBAL_OFFSET_TABLE_
43: 08049f0c 0 NOTYPE LOCAL HIDDEN 18 __init_array_end
44: 08049f0c 0 NOTYPE LOCAL HIDDEN 18 __init_array_start
45: 08049f20 0 OBJECT LOCAL HIDDEN 21 _DYNAMIC
46: 0804a014 0 NOTYPE WEAK DEFAULT 24 data_start
47: 0804a02c 4 OBJECT GLOBAL DEFAULT 25 global_noninit_val
48: 08048570 5 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
49: 080483a0 0 FUNC GLOBAL DEFAULT 14 _start
50: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
51: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
52: 08048628 4 OBJECT GLOBAL DEFAULT 16 _fp_hw
53: 0804860c 0 FUNC GLOBAL DEFAULT 15 _fini
54: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
55: 0804862c 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
56: 00000000 0 FUNC GLOBAL DEFAULT UND free@@GLIBC_2.0
57: 0804a014 0 NOTYPE GLOBAL DEFAULT 24 __data_start
58: 0804a018 0 OBJECT GLOBAL HIDDEN 24 __dso_handle
59: 0804a01c 4 OBJECT GLOBAL DEFAULT 24 global_init_val
60: 08049f18 0 OBJECT GLOBAL HIDDEN 19 __DTOR_END__
61: 08048580 90 FUNC GLOBAL DEFAULT 14 __libc_csu_init
62: 00000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.0
63: 0804a024 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
64: 00000000 0 FUNC GLOBAL DEFAULT UND malloc@@GLIBC_2.0
65: 0804a030 0 NOTYPE GLOBAL DEFAULT ABS _end
66: 0804a024 0 NOTYPE GLOBAL DEFAULT ABS _edata
67: 080485da 0 FUNC GLOBAL HIDDEN 14 __i686.get_pc_thunk.bx
68: 08048454 269 FUNC GLOBAL DEFAULT 14 main
69: 08048304 0 FUNC GLOBAL DEFAULT 12 _init
看68行(红色标记),发现0x080483a0地址对应的段是_start,08048454对应的是main段,所以验证程序的 printf("address of text is:%p\n",main)并不能真正的打印出.text段
的起始地址,我认为事实上在编译成汇编代码的时候,main函数前面加入了一段代码,应该是编译器做的。
这个验证程序是一本书上看到的,当时看的时候就觉得不太准确,所以做了个实验来验证一下。当然这段程序估计还有其他地方不够严谨,下次看有没有机会再深入研究一下。
我发现当深入到编译器或系统层面理解代码之后以前的很多疑问顺其自然的解决了。
比如说请看41、47和59行全局变量和局部变量的属性区别就显现出来了,当然这是根据分页数据保护实现的。