dl_iterate_phdr函数用法详解
dl_iterate_phdr函数简介
- 头文件包含
#include <link.h>
- 函数定义
int dl_iterate_phdr(
int (* callback ) (struct dl_phdr_info * info ,
size_t size , void * data ),
void * data );
dl_iterate_phdr函数常见使用错误
- 编译错误
warning: implicit declaration of function ‘dl_iterate_phdr’ [-Wimplicit-function-declaration]
解决办法:包含头文件
#include <link.h>
dl_iterate_phdr函数详细描述
dl_iterate_phdr ()函数允许应用程序在运行时进行查询,以找出它加载了哪些共享对象,以及它们加载的顺序。
dl_iterate_phdr ()函数遍历应用程序的共享对象列表,并为每个对象调用函数callback一次,直到所有共享对象都已处理完毕,或者callback返回一个非零值。
每个对callback的调用都接收三个参数:info ,它是指向包含共享对象信息的结构的指针;size 是info ;指向的结构的大小,data 是调用程序作为第二个参数传递的值的副本(在对dl_iterate_phdr ()的调用中也称为data )
info参数是以下类型的结构:
struct dl_phdr_info {
ElfW(Addr) dlpi_addr; /* Base address of object */
const char *dlpi_name; /* (Null-terminated) name of
object */
const ElfW(Phdr) *dlpi_phdr; /* Pointer to array of
ELF program headers
for this object */
ElfW(Half) dlpi_phnum; /* # of items in \fIdlpi_phdr\fP */
/* The following fields were added in glibc 2.4, after the first
version of this structure was available. Check the \fIsize\fP
argument passed to the dl_iterate_phdr callback to determine
whether or not each later member is available. */
unsigned long long dlpi_adds;
/* Incremented when a new object may
have been added */
unsigned long long dlpi_subs;
/* Incremented when an object may
have been removed */
size_t dlpi_tls_modid;
/* If there is a PT_TLS segment, its module
ID as used in TLS relocations, else zero */
void *dlpi_tls_data;
/* The address of the calling thread\(aqs instance
of this module\(aqs PT_TLS segment, if it has
one and it has been allocated in the calling
thread, otherwise a null pointer */
};
(ElfW ()宏定义将其参数转换为适用于硬件体系结构的ELF数据类型的名称。例如,在32位平台上,ElfW(Addr)产生数据类型名称Elf32_Addr 。关于这些类型的进一步信息可以在
dlpi_addr字段指示共享对象的基地址(即,共享对象的虚拟内存地址与该对象在加载该对象的文件中的偏移量之间的差值)。dlpi_name字段是一个以NULL结尾的字符串,提供了从其加载共享对象的路径名。
为了理解dlpi_phdr和dlpi_phnum字段的含义,我们需要知道一个ELF共享对象由许多段组成,每个段都有一个描述该段的相应程序头。dlpi_phdr字段是指向此共享对象的程序头数组的指针。dlpi_phnum字段指示这个数组的大小。
这些程序头是以下形式的结构:
typedef struct {
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;
请注意,我们可以使用以下公式计算特定程序头x 在虚拟内存中的位置:
addr == info\->dlpi_addr + info\->dlpi_phdr[x].p_vaddr;
p_type的可能值包括以下内容(更多细节请参见
#define PT_LOAD 1 /* Loadable program segment */
#define PT_DYNAMIC 2 /* Dynamic linking information */
#define PT_INTERP 3 /* Program interpreter */
#define PT_NOTE 4 /* Auxiliary information */
#define PT_SHLIB 5 /* Reserved */
#define PT_PHDR 6 /* Entry for header table itself */
#define PT_TLS 7 /* Thread-local storage segment */
#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */
#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
dl_iterate_phdr函数返回值
dl_iterate_phdr ()函数返回上次调用callback 所返回的值
dl_iterate_phdr函数其他说明
C库的未来版本可能会在dl_phdr_info结构中添加更多的字段;在这种情况下,size参数为回调函数提供了一种机制,以发现它是否运行在具有添加字段的系统上。
callback访问的第一个对象是主程序。对于主程序,dlpi_name字段将是一个空字符串。
dl_iterate_phdr函数使用举例
下面的程序显示它加载的共享对象的路径名列表。对于每个共享对象,程序为每个对象ELF段列出一些信息(虚拟地址、大小、标志和类型)。
下面的shell会话演示程序在x86-64系统上产生的输出。显示输出的第一个共享对象(其中名称为空字符串)是主程序。
$ \fB./a.out\fP
Name: "" (9 segments)
0: [ 0x400040; memsz: 1f8] flags: 0x5; PT_PHDR
1: [ 0x400238; memsz: 1c] flags: 0x4; PT_INTERP
2: [ 0x400000; memsz: ac4] flags: 0x5; PT_LOAD
3: [ 0x600e10; memsz: 240] flags: 0x6; PT_LOAD
4: [ 0x600e28; memsz: 1d0] flags: 0x6; PT_DYNAMIC
5: [ 0x400254; memsz: 44] flags: 0x4; PT_NOTE
6: [ 0x400970; memsz: 3c] flags: 0x4; PT_GNU_EH_FRAME
7: [ (nil); memsz: 0] flags: 0x6; PT_GNU_STACK
8: [ 0x600e10; memsz: 1f0] flags: 0x4; PT_GNU_RELRO
Name: "linux-vdso.so.1" (4 segments)
0: [0x7ffc6edd1000; memsz: e89] flags: 0x5; PT_LOAD
1: [0x7ffc6edd1360; memsz: 110] flags: 0x4; PT_DYNAMIC
2: [0x7ffc6edd17b0; memsz: 3c] flags: 0x4; PT_NOTE
3: [0x7ffc6edd17ec; memsz: 3c] flags: 0x4; PT_GNU_EH_FRAME
Name: "/lib64/libc.so.6" (10 segments)
0: [0x7f55712ce040; memsz: 230] flags: 0x5; PT_PHDR
1: [0x7f557145b980; memsz: 1c] flags: 0x4; PT_INTERP
2: [0x7f55712ce000; memsz: 1b6a5c] flags: 0x5; PT_LOAD
3: [0x7f55716857a0; memsz: 9240] flags: 0x6; PT_LOAD
4: [0x7f5571688b80; memsz: 1f0] flags: 0x6; PT_DYNAMIC
5: [0x7f55712ce270; memsz: 44] flags: 0x4; PT_NOTE
6: [0x7f55716857a0; memsz: 78] flags: 0x4; PT_TLS
7: [0x7f557145b99c; memsz: 544c] flags: 0x4; PT_GNU_EH_FRAME
8: [0x7f55712ce000; memsz: 0] flags: 0x6; PT_GNU_STACK
9: [0x7f55716857a0; memsz: 3860] flags: 0x4; PT_GNU_RELRO
Name: "/lib64/ld-linux-x86-64.so.2" (7 segments)
0: [0x7f557168f000; memsz: 20828] flags: 0x5; PT_LOAD
1: [0x7f55718afba0; memsz: 15a8] flags: 0x6; PT_LOAD
2: [0x7f55718afe10; memsz: 190] flags: 0x6; PT_DYNAMIC
3: [0x7f557168f1c8; memsz: 24] flags: 0x4; PT_NOTE
4: [0x7f55716acec4; memsz: 604] flags: 0x4; PT_GNU_EH_FRAME
5: [0x7f557168f000; memsz: 0] flags: 0x6; PT_GNU_STACK
6: [0x7f55718afba0; memsz: 460] flags: 0x4; PT_GNU_RELRO
Program source&
#define _GNU_SOURCE
#include <link.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
char *type;
int p_type;
printf("Name: \e"%s\e" (%d segments)\en", info\->dlpi_name,
info\->dlpi_phnum);
for (int j = 0; j < info\->dlpi_phnum; j++) {
p_type = info\->dlpi_phdr[j].p_type;
type = (p_type == PT_LOAD) ? "PT_LOAD" :
(p_type == PT_DYNAMIC) ? "PT_DYNAMIC" :
(p_type == PT_INTERP) ? "PT_INTERP" :
(p_type == PT_NOTE) ? "PT_NOTE" :
(p_type == PT_INTERP) ? "PT_INTERP" :
(p_type == PT_PHDR) ? "PT_PHDR" :
(p_type == PT_TLS) ? "PT_TLS" :
(p_type == PT_GNU_EH_FRAME) ? "PT_GNU_EH_FRAME" :
(p_type == PT_GNU_STACK) ? "PT_GNU_STACK" :
(p_type == PT_GNU_RELRO) ? "PT_GNU_RELRO" : NULL;
printf(" %2d: [%14p; memsz:%7jx] flags: %#jx; ", j,
(void *) (info\->dlpi_addr + info\->dlpi_phdr[j].p_vaddr),
(uintmax_t) info\->dlpi_phdr[j].p_memsz,
(uintmax_t) info\->dlpi_phdr[j].p_flags);
if (type != NULL)
printf("%s\en", type);
else
printf("[other (%#x)]\en", p_type);
}
return 0;
}
int
main(int argc, char *argv[])
{
dl_iterate_phdr(callback, NULL);
exit(EXIT_SUCCESS);
}
评论区