模块中绝对地址四种类型引用方式的总结

1.类型一 模块内部调用或跳转--相对地址调用/跳转  偏移量=目标地址 - 下一条指令的地址

8048344 <bar>:

8048344:     55                push %ebp

....

8048349 <foo>:

8048357:    e8 e8 ff ff ff  call 8048344<bar>
804835C: ...

 

2.类型二 模块内部数据访问--当前指令地址加上固定的偏移量

0000044c    <bar>:

.....

44f:    e8 40 00 00 00           call 494<__i686.get_pc_thunk.cx>//把下一条指令地址压栈,跳转

454:    81 c1 8c 11 00 00        add $0x118c,%ecx      //%ecx=0x454 + 0x118C,GOT表地址

45a:    c7 81 28 00 00 00 01     movl %0x1,0x28(%ecx)  //a=1

.....

494:    <__i686.get_pc_thunk.cx>:

494:    8b 0c 24                 mov (%esp),%ecx

497:    c3                       ret

a的实际地址是0x10000000+0x454 + 0x118c + 0x28 = 0x10001608

 

3.类型三 模块间数据访问--当前指令地址加上固定的偏移,得到GOT表地址,然后根据变量地址在GOT中的偏移就可以得到变量的地址  

0000044c    <bar>:

.....

44f:    e8 40 00 00 00           call 494 <__i686.get_pc_thunk.cx>

454:    81 c1 8c 11 00 00        add $0x118c,%ecx      //%ecx=0x454 + 0x118C,GOT表地址

45a:    c7 81 28 00 00 00 01     movl $0x1,0x28(%ecx)  //a = 1
461:    00 00 00 
464:    8b 81 f8 ff ff ff        mov 0xfffffff8(%ecx),%eax
46a:    c7 00 02 00 00 00        movl $0x2,(%eax)      //b = 2

.....

494:    <__i686.get_pc_thunk.cx>:

494:    8b 0c 24                 mov (%esp),%ecx

497:    c3                       ret

程序首先计算出变量b的地址在GOT中的位置:即0x10000000 + 0x454 + 0x118c +(-8) = 0x100015d8,然后使用寄存器ji

间接寻址方式给变量b赋值2.

注意:

共享模块中定义的全局变量也采用此类型来访问,GOT表指向可执行文件的.bss。

 

类型四 模块间调用, 跳转--当前指令地址加上固定的偏移,得到GOT表地址,然后根据函数地址在GOT中的偏移就可以得到函数 的地址

call    494 <__i686.get_pc_thunk.cx>

add     $0x118c,%ecx                 //%ecx=0x454 + 0x118C,GOT表地址

mov     0xfffffffc(%ecx),%eax

call    *(%eax)

.....

494:    <__i686.get_pc_thunk.cx>:

494:    8b 0c 24                 mov (%esp),%ecx

497:    c3                       ret

注意:

PLT(Procedure Linkage Table)为了实现延迟绑定,在这个过程中间又增加了一层间接跳转。调用函数并不直接通过GOT跳转,而是通过一个叫做PLT项的结构来进行跳转。每个外部函数在PLT中都有一个相应的项,比如bar()函数在PLT中的项的地址我们称之为bar@plt.看看bar@plt的实现

示例的PLT结构:

bar@plt:

jmp *(bar@GOT)

push n

push moduleID

jump _dl_runtime_resolve

为了实现延迟绑定,链接器在初始化的阶段并没有将bar()的实际地址填入到bar@GOT,而是将上面代码中的第二条指令push n 的地址填入到bar@GOT中。数字 n是bar这个符号引用在重定位表".rel.plt"中的下标。_dl_runtime_resolve()函数来完成符号解析和重定位工作。_dl_rutime_resolve()在进行一系列工作以后将bar()的真正地址填入到bar@GOT中。

ELF将GOT拆分成了两个表叫做".got"和".got.plt".其中".got"用来保存全局变量引用的地址,"got.plt"用来保存函数引用的地址。".got.plt"前三项有特殊意义:

第一项保存的是".dynamic"段的地址,这个段描述了本模块动态链接相关的信息。

第二项保存的是本模块的ID.

第三项保存的是_dl_runtime_resolve()的地址。

为了减少代码的重复,ELF把上面示例中的最后两条指令放到PLT中的第一项。并且归定每一项的长度是16个字节,刚好用来存放三条指令。

PLT0:

push    *(GOT + 4) //本模块的ID

jmp     *(GOT+8)   //跳转到_dl_runtime_resolve(),完成符号解析和重定位工作,bar@GOT填入真正地址

...

bar@plt:

jmp     *(bar@GOT)

push    n         //数字 n是bar这个符号引用在重定位表".rel.plt"中的下标

jump    PLT0

PLT在ELF文件中以独立的节存放,节名通常为".plt".因为他本身是一些地址无关的代码,所以可以跟.text节等一起合并一个可读可执行的Segment被装载入内存。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值