一.ELF的头:

ELF 的头可以通过readelf -h  /bin/false获得:

ELF简单说

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中的信息.

ELF简单说

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)

 

[email protected]:

0x400004 jmp *([email protected])

0x400008 jump PLT0

 

此时会调用到链接器中的_dl_runtime_resolve(),此函数会把真实的printf函数地址填入到[email protected]中,然后调用printf函数地址

接着返回到调用call [email protected]中的下一条地址开始执行。

当然,当下次再调用[email protected]时,就直接能从got表中找到printf函数的地址了,直接调用完事。

 

 

 

相关文章: