出于各种目的,我试图在不解析的情况下获取主可执行文件的ELF头的地址/proc/self/maps.我尝试解析/ functions link_list给出的链,但它们不包含指向主可执行文件基址的条目.有没有办法做到这一点(标准与否)没有解析?dlopendlinfol_addr/proc/self/maps
我正在尝试做的一个例子:
#include <stdio.h>
#include <elf.h>
int main()
{
Elf32_Ehdr* header = /* Somehow obtain the address of the ELF header of this program */;
printf("%p\n", header);
/* Read the header and do stuff, etc */
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Emp*_*ian 18
void *返回的指针dlopen(0, RTLD_LAZY)给你一个struct link_map *,对应于主可执行文件.
调用dl_iterate_phdr还会在第一次执行回调时返回主可执行文件的条目.
您可能会对.l_addr == 0链接映射中的事实以及dlpi_addr == 0使用时的事实感到困惑dl_iterate_phdr.
这种情况正在发生,因为l_addr(和dlpi_addr)实际上并没有记录ELF图像的加载地址.相反,它们记录已应用于该图像的重定位.
通常,主可执行文件构建为加载0x400000(对于x86_64 Linux)或0x08048000(对于ix86 Linux),并加载到同一地址(即它们不重定位).
但是,如果您将可执行文件与-pieflag 链接起来,那么它将被链接到0x0,并且它将被重定位到其他地址.
那你怎么得到ELF标题?简单:
#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;
static int once = 0;
if (once) return 0;
once = 1;
printf("relocation: 0x%lx\n", (long)info->dlpi_addr);
for (j = 0; j < info->dlpi_phnum; j++) {
if (info->dlpi_phdr[j].p_type == PT_LOAD) {
printf("a.out loaded at %p\n",
(void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr));
break;
}
}
return 0;
}
int
main(int argc, char *argv[])
{
dl_iterate_phdr(callback, NULL);
exit(EXIT_SUCCESS);
}
$ gcc -m32 t.c && ./a.out
relocation: 0x0
a.out loaded at 0x8048000
$ gcc -m64 t.c && ./a.out
relocation: 0x0
a.out loaded at 0x400000
$ gcc -m32 -pie -fPIC t.c && ./a.out
relocation: 0xf7789000
a.out loaded at 0xf7789000
$ gcc -m64 -pie -fPIC t.c && ./a.out
relocation: 0x7f3824964000
a.out loaded at 0x7f3824964000
Run Code Online (Sandbox Code Playgroud)
更新:
为什么手册页会说"基地址"而不是重定位?
这是一个错误;-)
我猜测,这名男子页的很久以前写的prelink和pie,和ASLR存在.如果没有预链接,共享库始终链接在地址加载0x0,然后relocation和base address成为一个和相同的.
当info引用主可执行文件时,dlpi_name如何指向空字符串?
这是实施的意外.
它的工作方式是内核open(2)是可执行文件,并将打开的文件描述符传递给加载器(在auxv[]向量中,如AT_EXECFD).一切装载机知道它会通过读取文件描述符的可执行文件.
在UNIX上没有简单的方法将文件描述符映射回它打开的名称.首先,UNIX支持硬链接,并且可能有多个引用同一文件的文件名.
较新的Linux内核也传递用于execve(2)可执行文件的名称(也在auxv[]as中AT_EXECFN).但这是可选的,即使传入它,glibc也不会将其放入.l_name/ dlpi_name以便不破坏依赖于名称为空的现有程序.
相反,glibc将该名称保存在__progname和中__progname_full.
装载机coud readlink(2)从名称/proc/self/exe上未使用的系统AT_EXECFN,但该/proc文件系统,但不保证任何安装,这样仍然有一个空的名字离开它的时候.