之前分析了x86平台ELF文件PLT和GOT,这里来看一些Android的ELF文件。因为Android智能手机使用的是ARM架构的CPU,所以本文关注的ELF文件也可以认为是ARM平台的ELF。
测试代码依然是经典的hello world
1 2 3 4 5 | #include <stdio.h> int main( int argc, char *argv[]) { puts ( "Hello, world" ); return 0; } |
在Android源码环境下编译,需要Android.mk文件:
LOCAL_PATH:=$(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES := \ hello.cLOCAL_MODULE_TAGS := debugLOCAL_MODULE := helloLOCAL_SHARED_LIBRARIES := libcLOCAL_MODULE_PATH := ./custom_outinclude $(BUILD_EXECUTABLE)
Android源码中提供了几个和x86平台类似的ELF文件查看工具,比如objdump和readelf,位置分别是:
./prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-objdump./prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-readelf
然后使用arm-eabi-objdump看看plt表中的内容,如下
Disassembly of section .plt:00008304 <.plt>: 8304: e52de004 push {lr} ; (str lr, [sp, #-4]!) 8308: e59fe004 ldr lr, [pc, #4] ; 8314 <__exidx_start-0x81> 830c: e08fe00e add lr, pc, lr 8310: e5bef008 ldr pc, [lr, #8]! 8314: 00000dd4 ldrdeq r0, [r0], -r4 8318: e28fc600 add ip, pc, #0 ; 0x0 831c: e28cca00 add ip, ip, #0 ; 0x0 8320: e5bcfdd4 ldr pc, [ip, #3540]! 8324: e28fc600 add ip, pc, #0 ; 0x0 8328: e28cca00 add ip, ip, #0 ; 0x0 832c: e5bcfdcc ldr pc, [ip, #3532]!
根据指令的重复特征可以看到,从0×8318开始的六条指令,是PLT中的两个表项,每个表项有三条指令。
使用arm-eabi-readelf看.rel.plt节区的内容如下,确实只有两个符号需要使用PLT进行重定位:
Relocation section '.rel.plt' at offset 0x2f4 contains 2 entries: Offset Info Type Sym.Value Sym. Name000090f4 00000416 R_ARM_JUMP_SLOT 00008318 puts000090f8 00000716 R_ARM_JUMP_SLOT 00008324 __libc_init
按照顺序,从0×8318到0×8320的三条指令,是puts函数对应的PLT表项:
8318: e28fc600 add ip, pc, #0 ; 0x0831c: e28cca00 add ip, ip, #0 ; 0x08320: e5bcfdd4 ldr pc, [ip, #3540]!
这三条指令到底做了什么,还需要进一步分析。
1. 0×8318: e28fc600 add ip, pc, #0 ; 0×0
ARM采用三级流水线,PC寄存器中取到的指令地址是当前执行指令的地址加8。当前指令的地址为0×8318,因此PC = 0×8318 + 0×8 = 0×8320。IP = PC + 0 = 0×83202. 0x831c: e28cca00 add ip, ip, #0 ; 0×0
IP = IP + 0 = 0×8320 + 0 = 0×83203. 0×8320: e5bcfdd4 ldr pc, [ip, #3540]!
PC = IP + 3540(dec) = 0×8320 + 0xdd4 = 0x90f4 这是一个间接寻址后跳转的语句,跳转的目的地址是0x90f4中存储的数值。0x90f4这个地址附近的内容如下:
Disassembly of section .got:000090e8 <.got>: 90e8: 00009020 andeq r9, r0, r0, lsr #32 ... 90f4: 00008304 andeq r8, r0, r4, lsl #6 90f8: 00008304 andeq r8, r0, r4, lsl #6
已经到了GOT中,0x90f4就是puts对应的GOT表项的地址,而上一步跳转的目的地,就是0x90f4中存储的数值,即0×8304。再看一下之间的PLT,发现0×8304正是PLT第0项的地址。至此,完成了和x86平台类似的功能,然后下一步就是具体的符号解析过程。
当找到puts的真正地址时,修改对应的GOt表项,再次调用puts就不再需要重新解析符号了