http://photonwen.i.sohu.com/blog/view/201923753.htm
参考资料
看了几天elf,找到不少文章,不过大多写的很难看明白,上面的链接里最后一个还算比较清晰。
不过我关心的是arm下的,而找到的资料大多是x86下的,还是有些不一样。
写了个小程序,里面定义了myfunc函数,这个函数里调用了dlopen。在android ndk环境下编译通过。
1 #include <stdio.h>
2 #include <stdlib.h>
3 void myfunc(){
4 dlopen("sdfasdfa",2);
5 }
6 main(){
7 myfunc();
8 }
下面我用objdump和readelf来分析下dlopen这个外部函数是如何被调用的。
不废话,直接看看
00008570 <myfunc>:
8570: 4803 ldr r0, [pc, #12] ; (8580 <myfunc+0x10>)
8572: 2102 movs r1, #2
8574: b510 push {r4, lr}
8576: 4478 add r0, pc //r0,r1保存调用参数
8578: f7ff efd0 blx 851c <_start-0x24> //原来dlopen的地址在0x851c
857c: bd10 pop {r4, pc}
857e: bf00 nop
8580: 0000150e andeq r1, r0, lr, lsl #10
...
接着在.plt里找到了0x851c
000084d8 <.plt>:
84d8: e52de004 push {lr} ; (str lr, [sp, #-4]!)
84dc: e59fe004 ldr lr, [pc, #4] ; 84e8 <_start-0x58> //lr = 0x9790
84e0: e08fe00e add lr, pc, lr //lr = 0x9790+0x84e8
84e4: e5bef008 ldr pc, [lr, #8]! //pc = [ 0x9790+0x84e8+8] = [0x11C80]
84e8: 00009790 muleq r0, r0, r7
84ec: e28fc600 add ip, pc, #0
84f0: e28cca09 add ip, ip, #36864 ; 0x9000
84f4: e5bcf790 ldr pc, [ip, #1936]! ; 0x790
84f8: e28fc600 add ip, pc, #0
84fc: e28cca09 add ip, ip, #36864 ; 0x9000
8500: e5bcf788 ldr pc, [ip, #1928]! ; 0x788
8504: e28fc600 add ip, pc, #0
8508: e28cca09 add ip, ip, #36864 ; 0x9000
850c: e5bcf780 ldr pc, [ip, #1920]! ; 0x780
8510: e28fc600 add ip, pc, #0
8514: e28cca09 add ip, ip, #36864 ; 0x9000
8518: e5bcf778 ldr pc, [ip, #1912]! ; 0x778
851c: e28fc600 add ip, pc, #0 //注意:pc总是指向当前地址+8
8520: e28cca09 add ip, ip, #36864 ; 0x9000
8524: e5bcf770 ldr pc, [ip, #1904]! ; 0x770 //pc=[0x8524+0x9000+0x770]=[0x11c94]
8528: e28fc600 add ip, pc, #0
852c: e28cca09 add ip, ip, #36864 ; 0x9000
8530: e5bcf768 ldr pc, [ip, #1896]! ; 0x768
8534: e28fc600 add ip, pc, #0
8538: e28cca09 add ip, ip, #36864 ; 0x9000
853c: e5bcf760 ldr pc, [ip, #1888]! ; 0x760
0x11c94是.got节中的一项
Disassembly of section .got:
00011c78 <_GLOBAL_OFFSET_TABLE_>:
11c78: 00011b90 muleq r1, r0, fp
11c7c
11c80
...
11c84: 000084d8 ldrdeq r8, [r0], -r8
11c88: 000084d8 ldrdeq r8, [r0], -r8
11c8c: 000084d8 ldrdeq r8, [r0], -r8
11c90: 000084d8 ldrdeq r8, [r0], -r8
11c94: 000084d8 ldrdeq r8, [r0], -r8
11c98: 000084d8 ldrdeq r8, [r0], -r8
11c9c: 000084d8 ldrdeq r8, [r0], -r8
...
所以pc现在指向0x84d8,看最前面的.plt节,就知道这就是plt0
plt0会跳转到[0x11C80],注意这个0x11c80也在.got节里。
从上面的分析得到一个疑惑,无论调用哪个外部函数都会去调用plt0,可plt0怎么知道我们到底在调用哪个外部函数? x86平台下是有传参数(got中的地址,和函数对应的符号表项)。在arm下我是没看到什么参数,只有ip比较可疑(见 绿色行)。
不过Android下现在是不支持lazy binding的,所以就算上面的plt0只是个摆设也没关系。可执行文件加载时其调用的外部函数对应的.got项就被修改了。这只是猜测,为了验证现在去看看linker的实现。
转载于:https://blog.51cto.com/laokaddk/1158117