2013-10-18 40 views
8

我試圖得到一個ELF二進制文件的加載地址,but dlopen doesn't按預期方式工作:得到一個ELF二進制文件的加載地址,dlopen的是沒有按預期工作

void *elf = (char *)dlopen (0, RTLD_NOW); 
printf ("%p\n", elf); 
sleep (100); 

它打印0xb772d918,但是從/proc/1510/maps告訴,它並不指向dlfn二進制文件的加載地址,但ld-2.15.so

08048000-08049000 r-xp 00000000 fc:00 1379  /root/dlfn 
08049000-0804a000 r--p 00000000 fc:00 1379  /root/dlfn 
0804a000-0804b000 rw-p 00001000 fc:00 1379  /root/dlfn 
b7550000-b7552000 rw-p 00000000 00:00 0 
b7552000-b76f5000 r-xp 00000000 fc:00 9275  /lib/i386-linux-gnu/libc-2.15.so 
b76f5000-b76f7000 r--p 001a3000 fc:00 9275  /lib/i386-linux-gnu/libc-2.15.so 
b76f7000-b76f8000 rw-p 001a5000 fc:00 9275  /lib/i386-linux-gnu/libc-2.15.so 
b76f8000-b76fb000 rw-p 00000000 00:00 0 
b76fb000-b76fe000 r-xp 00000000 fc:00 9305  /lib/i386-linux-gnu/libdl-2.15.so 
b76fe000-b76ff000 r--p 00002000 fc:00 9305  /lib/i386-linux-gnu/libdl-2.15.so 
b76ff000-b7700000 rw-p 00003000 fc:00 9305  /lib/i386-linux-gnu/libdl-2.15.so 
b7708000-b770b000 rw-p 00000000 00:00 0 
b770b000-b770c000 r-xp 00000000 00:00 0   [vdso] 
b770c000-b772c000 r-xp 00000000 fc:00 9299  /lib/i386-linux-gnu/ld-2.15.so 
b772c000-b772d000 r--p 0001f000 fc:00 9299  /lib/i386-linux-gnu/ld-2.15.so 
b772d000-b772e000 rw-p 00020000 fc:00 9299  /lib/i386-linux-gnu/ld-2.15.so 
bfc34000-bfc55000 rw-p 00000000 00:00 0   [stack] 

所以,比分析/proc/pid/maps等,有沒有辦法來檢索ELF二進制文件的加載地址? (在這種情況下爲0x0848000)

回答

11

在Linux上,dlopen未返回裝載ELF二進制文件的地址。它返回struct link_map,而其中有.l_addr成員。所以,你會希望是這樣的:

struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW); 
printf("%p\n", lm->l_addr); 

然而,儘管什麼/usr/include/link.h評論說,.l_addr其實並不是一個加載地址要麼。相反,這是區別之間和ELF圖像鏈接到加載,以及它實際加載。對於非預先鏈接的共享庫,該差異始終是加載地址(因爲非預先鏈接的ELF共享庫鏈接到地址0加載)。對於非PIE主要可執行文件,該差異始終爲0.

那麼你如何找到主要可執行文件的基址?最簡單的方法是使用這個代碼(鏈接到主可執行文件):

#ifndef _GNU_SOURCE 
#define _GNU_SOURCE 
#endif 

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

static int 
callback(struct dl_phdr_info *info, size_t size, void *data) 
{ 
    int j; 
    for (j = 0; j < info->dlpi_phnum; j++) { 
    if (info->dlpi_phdr[j].p_type == PT_LOAD) { 
     const char *beg = (const char*) info->dlpi_addr + info->dlpi_phdr[j].p_vaddr; 
     const char *end = beg + info->dlpi_phdr[j].p_memsz; 
     const char *cb = (const char *)&callback; 
     if (beg < cb && cb < end) { 
     // Found PT_LOAD that "covers" callback(). 
     printf("ELF header is at %p, image linked at 0x%zx, relocation: 0x%zx\n", 
       beg, info->dlpi_phdr[j].p_vaddr, info->dlpi_addr); 
     return 1; 
     } 
     return 0; 
    } 
    } 
    return 0; 
} 

int 
main(int argc, char *argv[]) 
{ 
    dl_iterate_phdr(callback, NULL); 
    exit(EXIT_SUCCESS); 
} 

這裏是你應該在32位系統上看到的內容:

$ gcc -g t.c -ldl -m32 && ./a.out 
ELF header is at 0x8048000, image linked at 0x8048000, relocation: 0x0 
$ gcc -g t.c -ldl -m32 -pie -fPIE && ./a.out 
ELF header is at 0xf779a000, image linked at 0x0, relocation: 0xf779a000 

(最後地址:0xf779a000將改變如果您啓用了地址隨機化(如您應該那樣),則運行以運行)。

+0

有趣......甚至沒有在'dlopen(3)'中提及過。謝謝! – daisy

+1

非常有用。如果您想在Android上執行此操作,則dl_iterate_phdr可從平臺版本21開始提供。 – sheltond