一.ELF的头:
ELF 的头可以通过readelf -h /bin/false获得:
program header:(程序执行的时候使用)
section:(编译链接的时候使用)
section header table:(用来索引section信息)
segment:(程序运行的时候使用)
2.通过objdump -h /bin/false | egrep -A 1 "Name|\.text|\.plt|\.rodata" 可以查看elf各种段信息
3.通过readelf -l /bin/false 查看elf中的program header信息
4.查看指定段中的内容(核心技术):
objdump -S -j .text /bin/false(以汇编的方式) 或者 objdump -s -j .text /bin/false(以文本的方式查看)
二.动态链接:
1.动态链接的程序,首先开始执行的时候会先执行程序所依赖的动态链接器:
通过 ldd /bin/false 可以查看到此程序所使用的动态链接器,或者通过objdump -s -j .interp /bin/false 以字符串的形式查看 .interp中的信息.
2.got表
程序刚开始运行的时候会动态链接器会初始化程序的got表
got[0] = .dynamic段的地址
got[1] = 本模块的ID
got[2] = 动态链接器,比如ld.so中_dl_runtime_resolve()的地址
当程序调用外模块的函数时候(例如调用动态库中的函数),会从got表中找到此函数的地址开始调用
3.plt:(延迟绑定) 这是一种调用got表中函数地址的lazy方式:
比如有些函数从来就不会被调用,为了优化性能,所以刚开始就不初始化got表中的函数地址,当调用哪个函数的时候。链接器就把函数地址加载在got表中提供调用,当第一次从got表中加载此函数地址的时候 比如调用 call print$plt,肯定是加载不到(因为还没有填入),此时填入的是 [email protected]的下面一条地址0x400008(随便写的), 此时程序开始执行的时候还是调回了[email protected]中的0x400008处。
例如:
PLT0:
push *(GOT + 4)
push *(GOT +8)
0x400004 jmp *([email protected])
0x400008 jump PLT0
此时会调用到链接器中的_dl_runtime_resolve(),此函数会把真实的printf函数地址填入到[email protected]中,然后调用printf函数地址
接着返回到调用call [email protected]中的下一条地址开始执行。
当然,当下次再调用[email protected]时,就直接能从got表中找到printf函数的地址了,直接调用完事。