|
|
X86汇编语言学习手记(3)
作者: Badcoffee
Email: blog.oliver@gmail.com
2004年12月
原文出处: [url]http://blog.csdn.net/yayong[/url]
版权所有: 转载时请务必以超链接形式标明文章原始出处、作者信息及本声明
这是作者在学习X86汇编过程中的学习笔记,难免有错误和疏漏之处,欢迎指正。
严格说来,本篇文档所涉及到的内容并非局限于X86汇编领域,关于ELF文件格式、C语言、编译器及其它相关知识,还需参考相关文档。
作者会将反馈的错误修订后更新在自己的Blog站点上。
在X86汇编语言学习手记(1)(2)中,可以看到栈(Stack)作为进程执行过程中数据的临时存储区域,通常包含如下几类数据:
局部变量
函数调用的返回地址
函数调用的入口参数
STP 栈框架指针 (可以通过编译器优化选项去除)
本章中,将继续通过实验,了解全局变量和静态变量在进程中是如何存储和分配的。
注:不同的Calling Convention对入口参数的规定是有一定差别的,函数调用入口参数也有可能通过寄存器来传递。
例如IBM的Power PC和AMD的Opteron,函数的入口参数全部或部分就是通过寄存器来传递的。
1. 全局变量和全局常量的实验
延续之前的方式,给出一个简单的C程序,其中声明的全局变量分为3种:
初始化过的全局变量
未初始化的全局变量
全局常量
#vi test5.c
int i=1;
int j=2;
int k=3;
int l,m;
int n;
const int o=7;
const int p=8;
const int q=9;
int main()
{
l=4;
m=5;
n=6;
return i+j+k+l+m+n+o+p+q;
}
# gcc test5.c -o test5
# mdb test5
Loading modules: [ libc.so.1 ]
> main::dis
main: pushl %ebp ; main至main+1,创建Stack Frame
main+1: movl %esp,%ebp
main+3: subl $8,%esp
main+6: andl $0xf0,%esp
main+9: movl $0,%eax
main+0xe: subl %eax,%esp ; main+3至main+0xe,为局部变量预留栈空间,并保证栈16字节对齐
main+0x10: movl $4,0x8060948 ; l=4
main+0x1a: movl $5,0x806094c ; m=5
main+0x24: movl $6,0x8060950 ; n=6
main+0x2e: movl 0x8060908,%eax
main+0x33: addl 0x8060904,%eax
main+0x39: addl 0x806090c,%eax
main+0x3f: addl 0x8060948,%eax
main+0x45: addl 0x806094c,%eax
main+0x4b: addl 0x8060950,%eax
main+0x51: addl 0x8050808,%eax
main+0x57: addl 0x805080c,%eax
main+0x5d: addl 0x8050810,%eax ; main+0x2e至main+0x5d,i+j+k+l+m+n+o+p+q
main+0x63: leave ; 撤销Stack Frame
main+0x64: ret ; main函数返回
现在,让我们在全局变量初始化后的地方设置断点,观察一下这几个全局变量的值:
> main+0x2e:b ; 设置断点
> :r ; 运行程序
mdb: stop at main+0x2e
mdb: target stopped at:
main+0x2e: movl 0x8060908,%eax
> 0x8060904,03/nap ; 察看全局变量 i,j,k的值
test5`i:
test5`i:
test5`i: 1
test5`j: 2
test5`k: 3
> 0x8060948,03/nap ; 察看全局变量l,m,n的值
test5`l:
test5`l:
test5`l: 4
test5`m: 5
test5`n: 6
> 0x8050808,03/nap ; 察看全局变量o,p,q的值
o:
o:
o: 7
p: 8
q: 9
>
概念:进程地址空间 Process Address Space
+----------------------+ ----> 0xFFFFFFFF (4GB)
| |
| Kernel Space |
| |
+----------------------+ ----> _kernel_base (0xE0000000)
| |
| Other Library |
: :
: :
| |
+----------------------+
| data section |
| Lib C Library |
| text section |
: :
: :
+----------------------+
| |
| |
: :
: grow up :
: :
| User Heap |
| |
+----------------------+
| bss |
| |
| User Data |
| |
+----------------------+
| |
| User Text |
| |
| |
+----------------------+ ----> 0x08050000
| |
| User Stack |
| |
: grow down :
: :
: :
| |
| |
+----------------------+ ----> 0
图 3-1 Solaris在IA32上的进程地址空间
如图3-1所示,Solaris在IA32上的进程地址空间和Linux是相似的,在用户进程的4GB地址空间内:
Kernel总是映射到用户地址空间的最高端,从宏定义_kernel_base至0xFFFFFFFF的区域
用户进程所依赖的各个共享库紧接着Kernel映射在用户地址空间的高端
最后是用户进程地址空间在地址空间的低端
各共享库的代码段,存放着二进制可执行的机器指令,是由kernel把该库ELF文件的代码段map到虚存空间,属性是read/exec/share
各共享库的数据段,存放着程序执行所需的全局变量,是由kernel把ELF文件的数据段map到虚存空间,属性为read/write/private
用户代码段,存放着二进制形式的可执行的机器指令,是由kernel把ELF文件的代码段map到虚存空间,属性为read/exec
用户代码段之上是数据段,存放着程序执行所需的全局变量,是由kernel把ELF文件的数据段map到虚存空间,属性为 read/write/private
用户代码段之下是栈(stack),作为进程的临时数据区,是由kernel把匿名内存map到虚存空间,属性为read/write/exec
用户数据段之上是堆(heap),当且仅当malloc调用时存在,是由kernel把匿名内存map到虚存空间,属性为read/write/exec
注意Stack和Heap的区别和联系:
相同点:
1. 都是来自于kernel分配的匿名内存,和磁盘上的ELF文件无关
2. 属性均为read/write/exec
不同点:
1.栈的分配在C语言层面一般是通过声明局部变量,调用函数引起的;堆的分配则是通过显式的调用(malloc)引起的
2.栈的释放在C语言层面是对用户透明的,用户不需要关心,由C编译器产生的相应的指令代劳;堆则需显式的调用(free)来释放
3.栈空间的增长方向是从高地址到低地址;堆空间的增长方向是由低地址到高地址
4.栈存在于任何进程的地址空间;堆则在程序中没有调用malloc的情况下不存在
用户地址空间的布局随着CPU和OS的不同,略有差异,以上都是基于X86 CPU在Solaris OS上的情况的讨论。
使用pmap命令,可以观察到系统中的指定进程的地址空间分布情况,下面就是用pmap观察bash进程的一个例子:
# pmap 1030
1030: -bash
08045000 12K rw--- [ stack ] ; bash的栈
08050000 444K r-x-- /usr/bin/bash ; bash文本段
080CE000 72K rwx-- /usr/bin/bash ; bash的数据段
080E0000 156K rwx-- [ heap ] ; bash的堆
DD8C0000 8K r-x-- /usr/lib/locale/zh_CN.GB18030/methods_zh_CN.GB18030.so.2 ; 共享库的文本段
DD8D1000 4K rwx-- /usr/lib/locale/zh_CN.GB18030/methods_zh_CN.GB18030.so.2 ; 共享库的数据段
DD8E0000 324K r-x-- /usr/lib/locale/zh_CN.GB18030/zh_CN.GB18030.so.2
DD940000 8K rwx-- /usr/lib/locale/zh_CN.GB18030/zh_CN.GB18030.so.2
DD950000 4K rwx-- [ anon ] ; 匿名内存, 由映射/dev/zero设备来创建的
DD960000 12K r-x-- /usr/lib/libmp.so.2
DD973000 4K rwx-- /usr/lib/libmp.so.2
DD980000 628K r-x-- /usr/lib/libc.so.1
DDA2D000 24K rwx-- /usr/lib/libc.so.1
DDA33000 4K rwx-- /usr/lib/libc.so.1
DDA50000 4K rwx-- [ anon ]
DDA60000 548K r-x-- /usr/lib/libnsl.so.1
DDAF9000 20K rwx-- /usr/lib/libnsl.so.1
DDAFE000 32K rwx-- /usr/lib/libnsl.so.1
DDB10000 44K r-x-- /usr/lib/libsocket.so.1
DDB2B000 4K rwx-- /usr/lib/libsocket.so.1
DDB30000 152K r-x-- /usr/lib/libcurses.so.1
DDB66000 28K rwx-- /usr/lib/libcurses.so.1
DDB6D000 8K rwx-- /usr/lib/libcurses.so.1
DDB80000 4K r-x-- /usr/lib/libdl.so.1
DDB90000 292K r-x-- /usr/lib/ld.so.1
DDBE9000 16K rwx-- /usr/lib/ld.so.1
DDBED000 8K rwx-- /usr/lib/ld.so.1
total 2864K
问题:全局变量和全局常量在进程地址空间的位置?
显然,根据前面的叙述,全局变量在用户的数据段,那么全局常量呢,是数据段吗?
同样的,可以利用mdb将test5进程挂起,然后用pmap命令求证一下:
# mdb test5
Loading modules: [ libc.so.1 ]
> ::sysbp _exit ; 在系统调用_exit处设置断点
> :r ; 运行程序
mdb: stop on entry to _exit
mdb: target stopped at:
libc.so.1`exit+0x2b: jae +0x15 <libc.so.1`exit+0x40>
>
此时,程序运行后在_exit处挂起,可以利用pmap在另一个终端内查看test5进程的地址空间了:
# ps -ef | grep test5
root 1387 1386 0 02:23:53 pts/1 0:00 test5
root 1399 1390 0 02:25:03 pts/3 0:00 grep test5
root 1386 1338 0 02:23:41 pts/1 0:00 mdb test5
# pmap -F 1387 ; 用pmap强制查看
1387: test5
08044000 16K rwx-- [ stack ] ; test5的stack
08050000 4K r-x-- /export/home/asm/L3/test5 ; test5的代码段,起始地址为0x08050000
08060000 4K rwx-- /export/home/asm/L3/test5 ; test5的数据段,起始地址为0x08060000
DDAC0000 628K r-x-- /usr/lib/libc.so.1
DDB6D000 24K rwx-- /usr/lib/libc.so.1
DDB73000 4K rwx-- /usr/lib/libc.so.1
DDB80000 4K r-x-- /usr/lib/libdl.so.1
DDB90000 292K r-x-- /usr/lib/ld.so.1
DDBE9000 16K rwx-- /usr/lib/ld.so.1
DDBED000 8K rwx-- /usr/lib/ld.so.1
total 1000K
可以看到,由于test5程序没有使用malloc来申请内存,所以没有heap的映射
前面用mdb观察过这些全局变量和常量的初始化值,它们的地址分别是:
全局变量i,j,k:
0x8060904起始的12字节
全局变量l,m,n:
0x8060948起始的12字节
全局常量o,p,q:
0x8050808起始的12字节
显然,根据这些变量的地址,我们可以初步判断出这些变量属于哪个段:
由于test5数据段起始地址为0x08060000,我们得出结论:全局变量i,j,k,l,m,n属于数据段
而test5代码段的起始地址为0x08050000,我们得出结论:全局常量o,p,q属于代码段
得出这个结论的确有点让人意外:全局常量竟然在代码段。
却又似乎在情理之中:数据段内存映射后的属性是r/w/x,而常量要求是只读属性,所以在代码段(r-x)就合情合理了。
问题:为什么这些全局变量地址不是连续的?
很容易注意到,全局变量i,j,k和l,m,n以及全局常量o,p,q是连续声明的,但地址实际上并不连续,而是在3段连续12字节的地址上。
当然,全局常量属于代码段,所以地址和全局变量是分开的;那么,为什么全局变量也并非连续呢?
前面谈到数据段实际上是从ELF格式的二进制文件映射到进程的地址空间的,就通过分析ELF文件格式来寻找答案吧:
# file test5
test5: ELF 32-bit LSB executable 80386 Version 1, dynamically linked, not stripped
# elfdump test5
ELF Header ; ELF头信息共52(0x34)字节,具体意义可以参考ELF format的相关文档
ei_magic: { 0x7f, E, L, F } ; ELF的幻数
ei_class: ELFCLASS32 ei_data: ELFDATA2LSB ; 32位的ELF文件,小端(LSB)编码
e_machine: EM_386 e_version: EV_CURRENT ; Intel 80386, 版本1
e_type: ET_EXEC ; 可执行文件
e_flags: 0
e_entry: 0x8050600 e_ehsize: 52 e_shstrndx: 27 ; 程序入口点_start的地址0x8050600
e_shoff: 0x1584 e_shentsize: 40 e_shnum: 29 ; Section header table的大小是29*40
e_phoff: 0x34 e_phentsize: 32 e_phnum: 5
Program Header[0]: ; 描述Program header table本身在内存中如何映射
p_vaddr: 0x8050034 p_flags: [ PF_X PF_R ]
p_paddr: 0 p_type: [ PT_PHDR ]
p_filesz: 0xa0 p_memsz: 0xa0
p_offset: 0x34 p_align: 0
Program Header[1]: ; 描述程序装载器的路径名(.interp section)存放在文件的位置
p_vaddr: 0 p_flags: [ PF_R ]
p_paddr: 0 p_type: [ PT_INTERP ]
p_filesz: 0x11 p_memsz: 0
p_offset: 0xd4 p_align: 0
Program Header[2]: ; 描述代码段在内存中如何映射,起始地址0x8050000,大小为 0x814
p_vaddr: 0x8050000 p_flags: [ PF_X PF_R ]
p_paddr: 0 p_type: [ PT_LOAD ]
p_filesz: 0x814 p_memsz: 0x814
p_offset: 0 p_align: 0x10000
Program Header[3]: ; 描述数据段在内存中如何映射,起始地址0x8060814,大小为0x144
p_vaddr: 0x8060814 p_flags: [ PF_X PF_W PF_R ]
p_paddr: 0 p_type: [ PT_LOAD ]
p_filesz: 0x118 p_memsz: 0x144
p_offset: 0x814 p_align: 0x10000
Program Header[4]: ; 描述动态链接信息(.dynamic section)在内存中如何映射
p_vaddr: 0x8060848 p_flags: [ PF_X PF_W PF_R ]
p_paddr: 0 p_type: [ PT_DYNAMIC ]
p_filesz: 0xb8 p_memsz: 0
p_offset: 0x848 p_align: 0
Section Header[1]: sh_name: .interp ; 该section保存了程序的解释程序(interpreter)的路径
sh_addr: 0x80500d4 sh_flags: [ SHF_ALLOC ]
sh_size: 0x11 sh_type: [ SHT_PROGBITS ]
sh_offset: 0xd4 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x1
Section Header[2]: sh_name: .hash ; 该section保存着一个符号的哈希表
sh_addr: 0x80500e8 sh_flags: [ SHF_ALLOC ]
sh_size: 0x104 sh_type: [ SHT_HASH ]
sh_offset: 0xe8 sh_entsize: 0x4
sh_link: 3 sh_info: 0
sh_addralign: 0x4
Section Header[3]: sh_name: .dynsym ; 该section保存着动态符号表
sh_addr: 0x80501ec sh_flags: [ SHF_ALLOC ]
sh_size: 0x200 sh_type: [ SHT_DYNSYM ]
sh_offset: 0x1ec sh_entsize: 0x10
sh_link: 4 sh_info: 1
sh_addralign: 0x4
Section Header[4]: sh_name: .dynstr ; 该section保存着动态连接时需要的字符串
sh_addr: 0x80503ec sh_flags: [ SHF_ALLOC SHF_STRINGS ]
sh_size: 0x11a sh_type: [ SHT_STRTAB ]
sh_offset: 0x3ec sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x1
Section Header[5]: sh_name: .SUNW_version ; 该section是SUN扩展的,保存版本信息
sh_addr: 0x8050508 sh_flags: [ SHF_ALLOC ]
sh_size: 0x20 sh_type: [ SHT_SUNW_verneed ]
sh_offset: 0x508 sh_entsize: 0
sh_link: 4 sh_info: 1
sh_addralign: 0x4
Section Header[6]: sh_name: .rel.got ; 该section保存着.got section中部分符号的重定位信息
sh_addr: 0x8050528 sh_flags: [ SHF_ALLOC SHF_INFO_LINK ]
sh_size: 0x18 sh_type: [ SHT_REL ]
sh_offset: 0x528 sh_entsize: 0x8
sh_link: 3 sh_info: 14
sh_addralign: 0x4
Section Header[7]: sh_name: .rel.bss ; 该section保存着.bss section中部分符号的重定位信息
sh_addr: 0x8050540 sh_flags: [ SHF_ALLOC SHF_INFO_LINK ]
sh_size: 0x8 sh_type: [ SHT_REL ]
sh_offset: 0x540 sh_entsize: 0x8
sh_link: 3 sh_info: 22
sh_addralign: 0x4
Section Header[8]: sh_name: .rel.plt ; 该section保存着.plt section中部分符号的重定位信息
sh_addr: 0x8050548 sh_flags: [ SHF_ALLOC SHF_INFO_LINK ]
sh_size: 0x38 sh_type: [ SHT_REL ]
sh_offset: 0x548 sh_entsize: 0x8
sh_link: 3 sh_info: 9
sh_addralign: 0x4
Section Header[9]: sh_name: .plt ; 该section保存着过程连接表(Procedure Linkage Table)
sh_addr: 0x8050580 sh_flags: [ SHF_ALLOC SHF_EXECINSTR ]
sh_size: 0x80 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x580 sh_entsize: 0x10
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[10]: sh_name: .text ; 该section保存着程序的正文部分,即可执行指令
sh_addr: 0x8050600 sh_flags: [ SHF_ALLOC SHF_EXECINSTR ]
sh_size: 0x1ec sh_type: [ SHT_PROGBITS ]
sh_offset: 0x600 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[11]: sh_name: .init ; 该section保存着可执行指令,它构成了进程的初始化代码
sh_addr: 0x80507ec sh_flags: [ SHF_ALLOC SHF_EXECINSTR ]
sh_size: 0xd sh_type: [ SHT_PROGBITS ]
sh_offset: 0x7ec sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x1
Section Header[12]: sh_name: .fini ; 该section保存着可执行指令,它构成了进程的终止代码
sh_addr: 0x80507f9 sh_flags: [ SHF_ALLOC SHF_EXECINSTR ]
sh_size: 0x8 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x7f9 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x1
Section Header[13]: sh_name: .rodata ; 该section保存着只读数据
sh_addr: 0x8050804 sh_flags: [ SHF_ALLOC ]
sh_size: 0x10 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x804 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[14]: sh_name: .got ; 该section保存着全局的偏移量表
sh_addr: 0x8060814 sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0x34 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x814 sh_entsize: 0x4
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[15]: sh_name: .dynamic ; 该section保存着动态连接的信息
sh_addr: 0x8060848 sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0xb8 sh_type: [ SHT_DYNAMIC ]
sh_offset: 0x848 sh_entsize: 0x8
sh_link: 4 sh_info: 0
sh_addralign: 0x4
Section Header[16]: sh_name: .data ; 该sections保存着初始化了的数据
sh_addr: 0x8060900 sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0x10 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x900 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[17]: sh_name: .ctors
sh_addr: 0x8060910 sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0x8 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x910 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[18]: sh_name: .dtors
sh_addr: 0x8060918 sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0x8 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x918 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[19]: sh_name: .eh_frame
sh_addr: 0x8060920 sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0x4 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x920 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[20]: sh_name: .jcr
sh_addr: 0x8060924 sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0x4 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x924 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[21]: sh_name: .data.rel.local
sh_addr: 0x8060928 sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0x4 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x928 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[22]: sh_name: .bss ; 该sectiopn保存着未初始化的数据
sh_addr: 0x806092c sh_flags: [ SHF_WRITE SHF_ALLOC ]
sh_size: 0x2c sh_type: [ SHT_NOBITS ] ; 指示不占据ELF空间sh_size是内存大小
sh_offset: 0x92c sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[23]: sh_name: .symtab ; 该section保存着一个符号表
sh_addr: 0 sh_flags: 0
sh_size: 0x540 sh_type: [ SHT_SYMTAB ]
sh_offset: 0x92c sh_entsize: 0x10
sh_link: 24 sh_info: 53
sh_addralign: 0x4
Section Header[24]: sh_name: .strtab ; 该section保存着字符串表
sh_addr: 0 sh_flags: [ SHF_STRINGS ]
sh_size: 0x20b sh_type: [ SHT_STRTAB ]
sh_offset: 0xe6c sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x1
Section Header[25]: sh_name: .comment ; 该section保存着版本控制信息
sh_addr: 0 sh_flags: 0
sh_size: 0x24d sh_type: [ SHT_PROGBITS ]
sh_offset: 0x1077 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x1
Section Header[26]: sh_name: .stab.index
sh_addr: 0 sh_flags: 0
sh_size: 0x24 sh_type: [ SHT_PROGBITS ]
sh_offset: 0x12c4 sh_entsize: 0xc
sh_link: 0 sh_info: 0
sh_addralign: 0x4
Section Header[27]: sh_name: .shstrtab ; 该section保存着section名称
sh_addr: 0 sh_flags: [ SHF_STRINGS ]
sh_size: 0xdc sh_type: [ SHT_STRTAB ]
sh_offset: 0x12e8 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x1
Section Header[28]: sh_name: .stab.indexstr
sh_addr: 0 sh_flags: 0
sh_size: 0x1c0 sh_type: [ SHT_STRTAB ]
sh_offset: 0x13c4 sh_entsize: 0
sh_link: 0 sh_info: 0
sh_addralign: 0x1
Interpreter:
/usr/lib/ld.so.1
Version Needed Section: .SUNW_version
file version
libc.so.1 SYSVABI_1.3
Symbol Table: .dynsym ; 动态解析和链接所需的符号表
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x080507ec 0x0000000d FUNC GLOB D 0 .init _init
[2] 0x08050804 0x00000004 OBJT GLOB D 0 .rodata _lib_version
[3] 0x08050580 0x00000000 OBJT GLOB D 0 .plt _PROCEDURE_LINKAGE_TABLE_
[4] 0x08050600 0x00000075 FUNC GLOB D 0 .text _start
[5] 0x08060900 0x00000000 OBJT GLOB D 0 .data __dso_handle
[6] 0x08060848 0x00000000 OBJT GLOB D 0 .dynamic _DYNAMIC
[7] 0x00000000 0x00000000 NOTY WEAK D 0 UNDEF __deregister_frame_info_bases
[8] 0x08050814 0x00000000 OBJT GLOB D 0 .rodata _etext
[9] 0x08060958 0x00000000 OBJT GLOB D 0 .bss _end
[10] 0x08050590 0x00000000 FUNC WEAK D 0 UNDEF _cleanup
[11] 0x08050675 0x00000001 FUNC WEAK D 0 .text _mcount
[12] 0x08060904 0x00000004 OBJT GLOB D 0 .data i
[13] 0x08060908 0x00000004 OBJT GLOB D 0 .data j
[14] 0x0806090c 0x00000004 OBJT GLOB D 0 .data k
[15] 0x08060948 0x00000004 OBJT GLOB D 0 .bss l
[16] 0x08060954 0x00000004 OBJT GLOB D 0 .bss _environ
[17] 0x08060814 0x00000000 OBJT GLOB D 0 .got _GLOBAL_OFFSET_TABLE_
[18] 0x0806094c 0x00000004 OBJT GLOB D 0 .bss m
[19] 0x0806092c 0x00000000 OBJT GLOB D 0 .data.rel.l _edata
[20] 0x08060954 0x00000004 OBJT WEAK D 0 .bss environ
[21] 0x080507f9 0x00000008 FUNC GLOB D 0 .fini _fini
[22] 0x080505a0 0x00000000 FUNC GLOB D 0 UNDEF atexit
[23] 0x08060950 0x00000004 OBJT GLOB D 0 .bss n
[24] 0x08050808 0x00000004 OBJT GLOB D 0 .rodata o
[25] 0x0805080c 0x00000004 OBJT GLOB D 0 .rodata p
[26] 0x00000000 0x00000000 NOTY WEAK D 0 UNDEF _Jv_RegisterClasses
[27] 0x08050810 0x00000004 OBJT GLOB D 0 .rodata q
[28] 0x080505b0 0x00000000 FUNC GLOB D 0 UNDEF __fpstart
[29] 0x08050753 0x00000065 FUNC GLOB D 0 .text main
[30] 0x00000000 0x00000000 NOTY WEAK D 0 UNDEF __register_frame_info_bases
[31] 0x080505c0 0x00000000 FUNC GLOB D 0 UNDEF exit
Symbol Table: .symtab ; 程序链接所需的符号表
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS test5
[2] 0x080500d4 0x00000000 SECT LOCL D 0 .interp
[3] 0x080500e8 0x00000000 SECT LOCL D 0 .hash
[4] 0x080501ec 0x00000000 SECT LOCL D 0 .dynsym
[5] 0x080503ec 0x00000000 SECT LOCL D 0 .dynstr
[6] 0x08050508 0x00000000 SECT LOCL D 0 .SUNW_versi
[7] 0x08050528 0x00000000 SECT LOCL D 0 .rel.got
[8] 0x08050540 0x00000000 SECT LOCL D 0 .rel.bss
[9] 0x08050548 0x00000000 SECT LOCL D 0 .rel.plt
[10] 0x08050580 0x00000000 SECT LOCL D 0 .plt
[11] 0x08050600 0x00000000 SECT LOCL D 0 .text
[12] 0x080507ec 0x00000000 SECT LOCL D 0 .init
[13] 0x080507f9 0x00000000 SECT LOCL D 0 .fini
[14] 0x08050804 0x00000000 SECT LOCL D 0 .rodata
[15] 0x08060814 0x00000000 SECT LOCL D 0 .got
[16] 0x08060848 0x00000000 SECT LOCL D 0 .dynamic
[17] 0x08060900 0x00000000 SECT LOCL D 0 .data
[18] 0x08060910 0x00000000 SECT LOCL D 0 .ctors
[19] 0x08060918 0x00000000 SECT LOCL D 0 .dtors
[20] 0x08060920 0x00000000 SECT LOCL D 0 .eh_frame
[21] 0x08060924 0x00000000 SECT LOCL D 0 .jcr
[22] 0x08060928 0x00000000 SECT LOCL D 0 .data.rel.l
[23] 0x0806092c 0x00000000 SECT LOCL D 0 .bss
[24] 0x00000000 0x00000000 SECT LOCL D 0 .symtab
[25] 0x00000000 0x00000000 SECT LOCL D 0 .strtab
[26] 0x00000000 0x00000000 SECT LOCL D 0 .comment
[27] 0x00000000 0x00000000 SECT LOCL D 0 .stab.index
[28] 0x00000000 0x00000000 SECT LOCL D 0 .shstrtab
[29] 0x00000000 0x00000000 SECT LOCL D 0 .stab.index
[30] 0x08050000 0x00000000 OBJT LOCL D 0 .interp _START_
[31] 0x08060958 0x00000000 OBJT LOCL D 0 .bss _END_
[32] 0x00000000 0x00000000 FILE LOCL D 0 ABS crt1.s
[33] 0x00000000 0x00000000 FILE LOCL D 0 ABS crti.s
[34] 0x00000000 0x00000000 FILE LOCL D 0 ABS values-Xa.c
[35] 0x00000000 0x00000000 FILE LOCL D 0 ABS crtstuff.c
[36] 0x08060910 0x00000000 OBJT LOCL D 0 .ctors __CTOR_LIST__
[37] 0x08060918 0x00000000 OBJT LOCL D 0 .dtors __DTOR_LIST__
[38] 0x08060920 0x00000000 OBJT LOCL D 0 .eh_frame __EH_FRAME_BEGIN__
[39] 0x08060924 0x00000000 OBJT LOCL D 0 .jcr __JCR_LIST__
[40] 0x08060928 0x00000000 OBJT LOCL D 0 .data.rel.l p.0
[41] 0x0806092c 0x00000001 OBJT LOCL D 0 .bss completed.1
[42] 0x08050678 0x00000000 FUNC LOCL D 0 .text __do_global_dtors_aux
[43] 0x08060930 0x00000018 OBJT LOCL D 0 .bss object.2
[44] 0x080506e4 0x00000000 FUNC LOCL D 0 .text frame_dummy
[45] 0x00000000 0x00000000 FILE LOCL D 0 ABS test5.c
[46] 0x00000000 0x00000000 FILE LOCL D 0 ABS crtstuff.c
[47] 0x08060914 0x00000000 OBJT LOCL D 0 .ctors __CTOR_END__
[48] 0x0806091c 0x00000000 OBJT LOCL D 0 .dtors __DTOR_END__
[49] 0x08060920 0x00000000 OBJT LOCL D 0 .eh_frame __FRAME_END__
[50] 0x08060924 0x00000000 OBJT LOCL D 0 .jcr __JCR_END__
[51] 0x080507b8 0x00000000 FUNC LOCL D 0 .text __do_global_ctors_aux
[52] 0x00000000 0x00000000 FILE LOCL D 0 ABS crtn.o
[53] 0x080507ec 0x0000000d FUNC GLOB D 0 .init _init
[54] 0x08050804 0x00000004 OBJT GLOB D 0 .rodata _lib_version
[55] 0x08050580 0x00000000 OBJT GLOB D 0 .plt _PROCEDURE_LINKAGE_TABLE_
[56] 0x08050600 0x00000075 FUNC GLOB D 0 .text _start
[57] 0x08060900 0x00000000 OBJT GLOB D 0 .data __dso_handle
[58] 0x08060848 0x00000000 OBJT GLOB D 0 .dynamic _DYNAMIC
[59] 0x00000000 0x00000000 NOTY WEAK D 0 UNDEF __deregister_frame_info_bases
[60] 0x08050814 0x00000000 OBJT GLOB D 0 .rodata _etext
[61] 0x08060958 0x00000000 OBJT GLOB D 0 .bss _end
[62] 0x08050590 0x00000000 FUNC WEAK D 0 UNDEF _cleanup
[63] 0x08050675 0x00000001 FUNC WEAK D 0 .text _mcount
[64] 0x08060904 0x00000004 OBJT GLOB D 0 .data i
[65] 0x08060908 0x00000004 OBJT GLOB D 0 .data j
[66] 0x0806090c 0x00000004 OBJT GLOB D 0 .data k
[67] 0x08060948 0x00000004 OBJT GLOB D 0 .bss l
[68] 0x08060954 0x00000004 OBJT GLOB D 0 .bss _environ
[69] 0x08060814 0x00000000 OBJT GLOB D 0 .got _GLOBAL_OFFSET_TABLE_
[70] 0x0806094c 0x00000004 OBJT GLOB D 0 .bss m
[71] 0x0806092c 0x00000000 OBJT GLOB D 0 .data.rel.l _edata
[72] 0x08060954 0x00000004 OBJT WEAK D 0 .bss environ
[73] 0x080507f9 0x00000008 FUNC GLOB D 0 .fini _fini
[74] 0x080505a0 0x00000000 FUNC GLOB D 0 UNDEF atexit
[75] 0x08060950 0x00000004 OBJT GLOB D 0 .bss n
[76] 0x08050808 0x00000004 OBJT GLOB D 0 .rodata o
[77] 0x0805080c 0x00000004 OBJT GLOB D 0 .rodata p
[78] 0x00000000 0x00000000 NOTY WEAK D 0 UNDEF _Jv_RegisterClasses
[79] 0x08050810 0x00000004 OBJT GLOB D 0 .rodata q
[80] 0x080505b0 0x00000000 FUNC GLOB D 0 UNDEF __fpstart
[81] 0x08050753 0x00000065 FUNC GLOB D 0 .text main
[82] 0x00000000 0x00000000 NOTY WEAK D 0 UNDEF __register_frame_info_bases
[83] 0x080505c0 0x00000000 FUNC GLOB D 0 UNDEF exit
Hash Section: .hash
bucket symndx name
0 [1] _init
1 [2] _lib_version
[3] _PROCEDURE_LINKAGE_TABLE_
2 [4] _start
[5] __dso_handle
4 [6] _DYNAMIC
9 [7] __deregister_frame_info_bases
[8] _etext
10 [9] _end
12 [10] _cleanup
[11] _mcount
[12] i
13 [13] j
14 [14] k
15 [15] l
16 [16] _environ
[17] _GLOBAL_OFFSET_TABLE_
[18] m
[19] _edata
[20] environ
17 [21] _fini
[22] atexit
[23] n
18 [24] o
19 [25] p
20 [26] _Jv_RegisterClasses
[27] q
25 [28] __fpstart
26 [29] main
29 [30] __register_frame_info_bases
[31] exit
13 buckets contain 0 symbols
10 buckets contain 1 symbols
5 buckets contain 2 symbols
2 buckets contain 3 symbols
1 buckets contain 5 symbols
31 buckets 31 symbols (globals)
Global Offset Table: 13 entries
ndx addr value reloc addend symbol
[00000] 08060814 08060848 R_386_NONE 00000000
[00001] 08060818 00000000 R_386_NONE 00000000
[00002] 0806081c 00000000 R_386_NONE 00000000
[00003] 08060820 08050596 R_386_JMP_SLOT 00000000 _cleanup
[00004] 08060824 080505a6 R_386_JMP_SLOT 00000000 atexit
[00005] 08060828 080505b6 R_386_JMP_SLOT 00000000 __fpstart
[00006] 0806082c 080505c6 R_386_JMP_SLOT 00000000 exit
[00007] 08060830 00000000 R_386_GLOB_DAT 00000000 __deregister_frame_info_bases
[00008] 08060834 080505d6 R_386_JMP_SLOT 00000000 __deregister_frame_info_bases
[00009] 08060838 00000000 R_386_GLOB_DAT 00000000 __register_frame_info_bases
[00010] 0806083c 00000000 R_386_GLOB_DAT 00000000 _Jv_RegisterClasses
[00011] 08060840 080505e6 R_386_JMP_SLOT 00000000 _Jv_RegisterClasses
[00012] 08060844 080505f6 R_386_JMP_SLOT 00000000 __register_frame_info_bases
Relocation: .rel.got
type offset section with respect to
R_386_GLOB_DAT 0x8060830 .rel.got __deregister_frame_info_bases
R_386_GLOB_DAT 0x8060838 .rel.got __register_frame_info_bases
R_386_GLOB_DAT 0x806083c .rel.got _Jv_RegisterClasses
Relocation: .rel.bss
type offset section with respect to
R_386_COPY 0x8060954 .rel.bss _environ
Relocation: .rel.plt
type offset section with respect to
R_386_JMP_SLOT 0x8060820 .rel.plt _cleanup
R_386_JMP_SLOT 0x8060824 .rel.plt atexit
R_386_JMP_SLOT 0x8060828 .rel.plt __fpstart
R_386_JMP_SLOT 0x806082c .rel.plt exit
R_386_JMP_SLOT 0x8060834 .rel.plt __deregister_frame_info_bases
R_386_JMP_SLOT 0x8060840 .rel.plt _Jv_RegisterClasses
R_386_JMP_SLOT 0x8060844 .rel.plt __register_frame_info_bases
Dynamic Section: .dynamic
index tag value
[0] NEEDED 0x104 libc.so.1
[1] INIT 0x80507ec
[2] FINI 0x80507f9
[3] HASH 0x80500e8
[4] STRTAB 0x80503ec
[5] STRSZ 0x11a
[6] SYMTAB 0x80501ec
[7] SYMENT 0x10
[8] CHECKSUM 0x6a10
[9] VERNEED 0x8050508
[10] VERNEEDNUM 0x1
[11] PLTRELSZ 0x38
[12] PLTREL 0x11
[13] JMPREL 0x8050548
[14] REL 0x8050528
[15] RELSZ 0x58
[16] RELENT 0x8
[17] DEBUG 0
[18] FEATURE_1 0x1 [ PARINIT ]
[19] FLAGS 0 0
[20] FLAGS_1 0 0
[21] PLTGOT 0x8060814
利用elfdump可以查看ELF文件格式的详细信息,可以在符号表.dynsym和.symtab中找到程序中定义的全局变量和全局常量:
i,j,k在.data section中
l,m,n在.bss section中
o,p,q在.rodata section中
概念:ELF(Executable and Linking Format) 可执行连接格式
ELF格式是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的。
目前,ELF格式是Unix/Linux平台上应用最广泛的二进制工业标准之一
下图从不同视角给出了ELF文件的一般格式:
Linking 视角 Execution 视角
============ ==============
ELF header ELF header
Program header table (optional) Program header table
Section 1 Segment 1
... Segment 2
Section n ...
Section header table Section header table (optional)
图 3-2 ELF文件格式 摘自 EXECUTABLE AND LINKABLE FORMAT (ELF)
可以根据test5 ELF文件的Program header table和Section header table中文件偏移量的信息描绘出test5的内容:
entry name 起始文件偏移+实际大小=下个entry起始偏移
-------------------------------------------------------
ELF header 0x0+0x34=0x34
Program header 0x34+0xa0=0xd4
Section 1 .interp 0xd4+0x11=0xe5
000 0xe5+0x3=0xe8
Section 2 .hash 0xe8+0x104=0x1ec
Section 3 .dynsym 0x1ec+0x200=0x3ec
Section 4 .dynstr 0x3ec+0x11a=0x506
00 0x506+0x2=0x508
Section 5 .SUNW_version 0x508+0x20=0x528
Section 6 .rel.got 0x528+0x18=0x540
Section 7 .rel.bss 0x540+0x8=0x548
Section 8 .rel.plt 0x548+0x38=0x580
Section 9 .plt 0x580+0x80=0x600
Section 10 .text 0x600+0x1ec=0x7ec
Section 11 .init 0x7ec+0xd=0x7f9
Section 12 .fini 0x7f9+0x8=0x801
000 0x801+0x3=0x804
Section 13 .rodata 0x804+0x10=0x814
Section 14 .got 0x814+0x34=0x848
Section 15 .dynamic 0x848+0xb8=900
Section 16 .data 0x900+0x10=0x910
Section 17 .ctors 0x910+0x8=0x918
Section 18 .dtors 0x918+0x8=0x920
Section 19 .eh_frame 0x920+0x4=0x924
Section 20 .jcr 0x924+0x4=0x928
Section 21 .data.rel.local 0x928+0x4=0x92c
Section 22 .bss 0x92c+0x0=0x958
Section 23 .symtab 0x92c+0x540=0xe6c
Section 24 .strtab 0xe6c+0x20b=1077
Section 25 .comment 0x1077+0x24d=0x12c4
Section 26 .stab.index 0x12c4+0x24=0x12e8
Section 27 .shstrtab 0x12e8+0xdc=0x13c4
Section 28 .stab.indexstr 0x13c4+0x1c0=0x1584
Section header table 0x1584+0x488=0x1a0c ; 29x40=1160=0x488 这是根据Elf header的信息算得的
------------------------------------------------------
图 3-3 test5的ELF文件格式
# ls -al test5
-rwxr-xr-x 1 root other 6668 2004-12-19 06:56 test5
可以看到test5的大小是0x1a0c字节,即6688字节。
可以看到,.bss section用于保存未初始化的全局变量,因此不占据ELF文件空间;
ELF文件装入时,会按照Section header table 22中的.bss的相关属性,为.bss映射相应大小的内存空间,并初始化为0
ELF文件的Program header table描述了如何将ELF装入内存:
Program Header 2 描述了用户代码段的起始地址和大小: 0x8050000+0x814=0x805 | |