1. 程式人生 > >ARM ELF函數重定位

ARM ELF函數重定位

lose 沒有 成了 strong idea 指令 include 重定向 技術分享

ARM ELF的函數重定位與x86是一致的,但由於匯編指令不同,再鼓搗一遍。

示例代碼:

#include <stdio.h>
#include <stdlib.h>

int main () {
        puts ("Hello world");
        sleep (1);
        FILE *fp = fopen ("1.c", "r");
        fclose (fp);
        exit (0);
}

通過 readelf -r 可以查看ELF中所有需要重定位的函數,我們以fopen()函數為例,分析其重定位過程。

$ arm-linux-androideabi-readelf -r elf_2

Relocation section ‘.rel.plt‘ at offset 0x278 contains 7 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00009ff4  00000516 R_ARM_JUMP_SLOT   00000000   fopen

首先main()函數中,通過 bl 82f4調用fopen(),82f4是一個16進制表示的地址,位於.plt節。

$ arm-linux-androideabi-objdump -d elf_2
000083c8 <main>:
...
    8404:       ebffffba        bl      82f4 <fopen@plt>
 Disassembly of section .plt:
 
 000082f4 <fopen@plt>:
    82f4:       e28fc600        add     ip, pc, #0, 12 @由於ARM三級流水,PC = 0x82f4 + 0x8
    82f8:       e28cca01        add     ip, ip, #4096
    82fc:       e5bcfcf8        ldr     pc, [ip, #3320]! @ip + 0xcf8 = 0x9ff4

以上三條指令執行完,從0x9ff4位置取值給pc,完成間接尋址的跳轉。看一下0x9ff4處內容:

(gdb) p/x *0x9ff4
$1 = 0x82b0

程序跳轉到0x82b0位置:

Disassembly of section .plt:

000082b0 <__libc_init@plt-0x14>:
    82b0:       e52de004        push    {lr}            ; (str lr, [sp, #-4]!)
    82b4:       e59fe004        ldr     lr, [pc, #4]    ; 82c0 <__libc_init@plt-0x4>
    82b8:       e08fe00e        add     lr, pc, lr
    82bc:       e5bef008        ldr     pc, [lr, #8]!
    82c0:       00001d18        andeq   r1, r0, r8, lsl sp

可以看到,這是.plt節的開始位置,IDA幫助我們做了一些顯示的優化,所以其匯編結果與objdump看到的不同,它假裝替我們完成了GOT的重定位過程,實際並非如此:

@ida的顯示結果
.got:00009FF4 fopen_ptr       DCD __imp_fopen

下面解析一下.plt節開頭的這幾條指令:

@ 1. stack <- lr

@ 2. lr <- 0x1d18

@ 3. lr <- 0x82c0 + 0x1d18 = 0x9fd8

@ 4. pc <- [0x9fd8 + 0x8], lr <- 0x9fd8 + 0x8 = 0x9fe0

發現程序最終從0x9fe0地址處取值,並間接尋址將其作為地址跳轉過去執行。使用gdb發現,此處靜態值為0x0。顯然這塊地址內容,要由程序運行時動態補充的,否則這條指令將產生0地址訪問異常。

(gdb) p/x *0x82c0
$1 = 0x1d18
(gdb) p/x *0x9fe0
$2 = 0x0
(gdb) 

技術分享圖片

原來GOT的前3項,是為系統預留的(GOT[0][1][2]),其中GOT[1]中是ELF中所有動態庫構成的鏈表的指針,GOT[2]是_dl_runtime_resolve函數指針。這個函數將具體完成函數的重定向過程,並將結果反饋到GOT表中。

因此上面靜態分析時,GOT這3個表項是沒有值的,它們由加載器動態填充。

以前畫的x86的圖,同樣適應 ARM:

技術分享圖片

ARM ELF函數重定位