Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
434 views
in Technique[技术] by (71.8m points)

c - Get loaded address of a ELF binary, dlopen is not working as expected

I'm trying to get the loaded address of an ELF binary, but dlopen doesn't work as expected:

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

It prints 0xb772d918, but from what /proc/1510/maps tells, it doesn't point to the loaded address of the dlfn binary, but the 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]

So, other than parsing /proc/pid/maps, is there a way to retrieve the loaded address of an ELF binary? (0x0848000 in this case)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

On Linux, dlopen doesn't return the address where the ELF binary was loaded. It returns struct link_map instead, which has .l_addr member. So you'll want something like:

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

However, despite what comment in /usr/include/link.h says, .l_addr is actually not a load address either. Instead, it's the difference between where ELF image was linked to load, and where it was actually loaded.

For non-PIE main executable, that difference is always 0. For non-prelinked shared library, that difference is always the load address (because non-prelinked ELF shared libraries are linked to load at address 0).

So how do you find the base address of the main executable? The easiest method is to use this code (linked into main executable):

#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;
  const char *cb = (const char *)&callback;
  const char *base = (const char *)info->dlpi_addr;
  const ElfW(Phdr) *first_load = NULL;

  for (j = 0; j < info->dlpi_phnum; j++) {
    const ElfW(Phdr) *phdr = &info->dlpi_phdr[j];

    if (phdr->p_type == PT_LOAD) {
      const char *beg = base + phdr->p_vaddr;
      const char *end = beg + phdr->p_memsz;

      if (first_load == NULL) first_load = phdr;
      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
",
               base + first_load->p_vaddr, first_load->p_vaddr, info->dlpi_addr);
        return 1;
      }
    }
  }
  return 0;
}

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

Here is what you should see on 32-bit system:

$ 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

(The last address: 0xf779a000 will vary from run to run if you have address randomization enabled (as you should)).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...