linux动态库的重定位机制

linux的动态库可以被共享,这个符号会怎么被链接进去呢,让我们来看一下过程。下面以常见的printf来举例。

函数入口

首先,我们编码过程中遇到的printf函数,并不是直接调用的,首先会进入如下的代码位置。

Dump of assembler code for function printf@plt:
   0x00015454 <+0>:     bx      pc
   0x00015456 <+2>:     nop                     ; (mov r8, r8)
->0x00015458 <+4>:     add     r12, pc, #6291456       ; 0x600000
   0x0001545c <+8>:     add     r12, r12, #872448       ; 0xd5000
   0x00015460 <+12>:    ldr     pc, [r12, #3084]!       ; 0xc0c
End of assembler dump.

 外部调用printf入口是0x15458,我们根据汇编来分析下,

 15460位置计算, pc = *(0x15460 + 0x6d5c0c) = *(0x6EB06C) (*标识地址引用,类似指针的意义),同时,这条指令还会让r12寄存器保存0x6EB06C(<printf@got.plt>)

其中,地址0x6EB06C的数据为真实printf加载前的代码桩位置,指向PLT的一段函数:

0x6eb06c <printf@got.plt>:      0x00015310

最后一行,pc会被设置成0x00015310,进入下面一个环节

函数重定位流程

我们看一下0x00015310为PLT代码段的头,也就是函数重定位流程入口

00015310 <.plt>:

   15310:      e52de004      push       {lr}          ; (str lr, [sp, #-4]!)

   15314:      e59fe004      ldr   lr, [pc, #4]      ; 15320 <.plt+0x10>

   15318:      e08fe00e       add lr, pc, lr

   1531c:       e5bef008      ldr   pc, [lr, #8]!

   15320:      006d5ce0      .word      0x006d5ce0

 我们计算一下下面几行的数据值:

15314计算,lr=*(0x1531c+4)= 0x006d5ce0

15318计算,lr= 0x15320 + *(0x15320) = 0x6EB000

1531c计算,pc=*(lr+8)= *0x6EB008 = 0xb6f22320,同时lr也被更新为0x6EB008

0x6eb008:       0xb6f22320

在1531c行,pc被载入0xb6f22320这个函数入口,看一下原来是动态链接符号解析函数

 Dump of assembler code for function _dl_linux_resolve:

   0xb6f22320 <+0>:     push    {r0, r1, r2, r3, r4}

   0xb6f22324 <+4>:     ldr     r0, [lr, #-4]

   0xb6f22328 <+8>:     sub     r1, lr, r12

   0xb6f2232c <+12>:    mvn     r1, r1, asr #2

   0xb6f22330 <+16>:    blx     0xb6f1f9a8 <_dl_linux_resolver>

   0xb6f22334 <+20>:    mov     r12, r0

   0xb6f22338 <+24>:    pop     {r0, r1, r2, r3, r4, lr}

   0xb6f2233c <+28>:    bx      r12

End of assembler dump.

 这个流程完毕之后,我们再来看下加载后的printf@got.plt位置函数指针变成了:

0x6eb06c <printf@got.plt>:      0xb6ead0e9

反汇编看一下,原来是真的printf了

(gdb) disassemble 0xb6ead0e9

Dump of assembler code for function printf:

   0xb6ead0e8 <+0>:     push    {r0, r1, r2, r3}

   0xb6ead0ea <+2>:     push    {r0, r1, r2, lr}

这个过程中为什么printf@got.plt的值能被知道呢?

还记得开始入口时,r12寄存器也会被记录,正对应printf@got.plt,同时根据这个地址相对GOT的位置,可以隐式确认其为printf的GOT。总地来说,只需知道其相对地址即可。

动态库的共享

 动态库在物理内存上是只有一份的,但由于不同的进程具有不同的虚拟地址空间,因此其会产生一份进程内虚拟空间到物理内存的页表映射。

动态库的内存分两部分,一部分为进程所有的,另一部分是动态库所有的。定位动态库在进程地址的部分编入了进程内,其他的在动态库加载后动态定位。

通过cat /proc/800/smaps观察代码段,找到libc的代码段,得到如下信息,其中Rss为动态库内存,Pss为分摊内存,这个值会被所有使用libc的进程分摊。

b6f28000-b6f29000 r--p 00004000 1f:04 241        /lib/ld-uClibc-1.0.31.so
Size:                  4 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:         0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd mr mw me dw ac

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值