一、首先看一个简单的程序:
#include<stdlib.h> int main() { while(1) { sleep(1000); } return 0; }
gcc -static SectionMapping.c -o SectionMapping.elf
/usr/bin/ld: cannot find
-lc
yum install glibc-static
查看一下静态链接之后,SectionMapping.elf的段分布情况
readelf -S SectionMapping.elf
There are 34 section headers, starting at offset 0xd0aa0: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .note.ABI-tag NOTE 0000000000400190 00000190 0000000000000020 0000000000000000 A 0 0 4 [ 2] .note.gnu.build-i NOTE 00000000004001b0 000001b0 0000000000000024 0000000000000000 A 0 0 4 [ 3] .rela.plt RELA 00000000004001d8 000001d8 0000000000000108 0000000000000018 AI 0 25 8 [ 4] .init PROGBITS 00000000004002e0 000002e0 000000000000001a 0000000000000000 AX 0 0 4 [ 5] .plt PROGBITS 0000000000400300 00000300 00000000000000b0 0000000000000000 AX 0 0 16 [ 6] .text PROGBITS 00000000004003b0 000003b0 0000000000091916 0000000000000000 AX 0 0 16 [ 7] __libc_thread_fre PROGBITS 0000000000491cd0 00091cd0 00000000000000b2 0000000000000000 AX 0 0 16 [ 8] __libc_freeres_fn PROGBITS 0000000000491d90 00091d90 0000000000001aef 0000000000000000 AX 0 0 16 [ 9] .fini PROGBITS 0000000000493880 00093880 0000000000000009 0000000000000000 AX 0 0 4 [10] .rodata PROGBITS 00000000004938a0 000938a0 00000000000196f8 0000000000000000 A 0 0 32 [11] .stapsdt.base PROGBITS 00000000004acf98 000acf98 0000000000000001 0000000000000000 A 0 0 1 [12] __libc_thread_sub PROGBITS 00000000004acfa0 000acfa0 0000000000000008 0000000000000000 A 0 0 8 [13] __libc_subfreeres PROGBITS 00000000004acfa8 000acfa8 0000000000000050 0000000000000000 A 0 0 8 [14] __libc_IO_vtables PROGBITS 00000000004ad000 000ad000 00000000000006a8 0000000000000000 A 0 0 32 [15] __libc_atexit PROGBITS 00000000004ad6a8 000ad6a8 0000000000000008 0000000000000000 A 0 0 8 [16] .eh_frame PROGBITS 00000000004ad6b0 000ad6b0 000000000000e234 0000000000000000 A 0 0 8 [17] .gcc_except_table PROGBITS 00000000004bb8e4 000bb8e4 0000000000000105 0000000000000000 A 0 0 1 [18] .tdata PROGBITS 00000000006bbeb0 000bbeb0 0000000000000020 0000000000000000 WAT 0 0 16 [19] .tbss NOBITS 00000000006bbed0 000bbed0 0000000000000038 0000000000000000 WAT 0 0 16 [20] .init_array INIT_ARRAY 00000000006bbed0 000bbed0 0000000000000010 0000000000000008 WA 0 0 8 [21] .fini_array FINI_ARRAY 00000000006bbee0 000bbee0 0000000000000010 0000000000000008 WA 0 0 8 [22] .jcr PROGBITS 00000000006bbef0 000bbef0 0000000000000008 0000000000000000 WA 0 0 8 [23] .data.rel.ro PROGBITS 00000000006bbf00 000bbf00 00000000000000e4 0000000000000000 WA 0 0 32 [24] .got PROGBITS 00000000006bbfe8 000bbfe8 0000000000000008 0000000000000008 WA 0 0 8 [25] .got.plt PROGBITS 00000000006bc000 000bc000 0000000000000070 0000000000000008 WA 0 0 8 [26] .data PROGBITS 00000000006bc080 000bc080 0000000000001690 0000000000000000 WA 0 0 32 [27] .bss NOBITS 00000000006bd720 000bd710 0000000000002158 0000000000000000 WA 0 0 32 [28] __libc_freeres_pt NOBITS 00000000006bf878 000bd710 0000000000000030 0000000000000000 WA 0 0 8 [29] .comment PROGBITS 0000000000000000 000bd710 000000000000002d 0000000000000001 MS 0 0 1 [30] .note.stapsdt NOTE 0000000000000000 000bd740 0000000000000f88 0000000000000000 0 0 4 [31] .symtab SYMTAB 0000000000000000 000be6c8 000000000000b9e8 0000000000000018 32 815 8 [32] .strtab STRTAB 0000000000000000 000ca0b0 0000000000006870 0000000000000000 0 0 1 [33] .shstrtab STRTAB 0000000000000000 000d0920 000000000000017b 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), l (large), p (processor specific)
可执行文件*有33个段。
readelf -l SectionMapping.elf: 查看elf文件的Segment 即程序头。它描述ELF文件该如何被映射到进程的虚拟地址空间
Elf file type is EXEC (Executable file) Entry point 0x400ecd There are 6 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000bb9e9 0x00000000000bb9e9 R E 200000 LOAD 0x00000000000bbeb0 0x00000000006bbeb0 0x00000000006bbeb0 0x0000000000001860 0x00000000000039f8 RW 200000 NOTE 0x0000000000000190 0x0000000000400190 0x0000000000400190 0x0000000000000044 0x0000000000000044 R 4 TLS 0x00000000000bbeb0 0x00000000006bbeb0 0x00000000006bbeb0 0x0000000000000020 0x0000000000000058 R 10 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 10 GNU_RELRO 0x00000000000bbeb0 0x00000000006bbeb0 0x00000000006bbeb0 0x0000000000000150 0x0000000000000150 R 1 Section to Segment mapping: Segment Sections... 00 .note.ABI-tag .note.gnu.build-id .rela.plt .init .plt .text __libc_thread_freeres_fn __libc_freeres_fn .fini .rodata .stapsdt.base __libc_thread_subfreeres __libc_subfreeres __libc_IO_vtables __libc_atexit .eh_frame .gcc_except_table 01 .tdata .init_array .fini_array .jcr .data.rel.ro .got .got.plt .data .bss __libc_freeres_ptrs 02 .note.ABI-tag .note.gnu.build-id 03 .tdata .tbss 04 05 .tdata .init_array .fini_array .jcr .data.rel.ro .got
二、堆和栈
在操作系统中,VMA除了被用来映射可执行文件中的各个"segment"以外,他还可以有其他作用。操作系统通过使用VMA来管理进程的地址空间。程序执行时,要用到栈和堆,事实上,它们也是以VMA的形式存在。一般情况,一个进程中的栈和堆分别都有一个对应的VMA。在Linux中可以由/proc查看:
./SectionMapping.elf &
[1] 59117
[root@tlinux /]# cat /proc/59117/maps
00400000-004bc000 r-xp 00000000 08:05 4456 /SectionMapping.elf 006bb000-006be000 rw-p 000bb000 08:05 4456 /SectionMapping.elf 006be000-006c0000 rw-p 00000000 00:00 0 017e1000-01804000 rw-p 00000000 00:00 0 [heap] 7ffc12ce6000-7ffc12d07000 rw-p 00000000 00:00 0 [stack] 7ffc12db7000-7ffc12db9000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
输出中:第一列是VMA地址范围,第二列是VMA权限,“p”表示私有,“s”表示共享。第三列是偏移。表示VMA对应的Segment在映像文件中的偏移。第四列是映像文件的主设备号与次设备号。第五列表示文件结点。