备注

C库的未来版本可能会在dl_phdr_info结构中添加更多字段;在这种情况下,size参数为回调函数提供了一种机制,以发现它是否在具有添加字段的系统上运行。

回调访问的第一个对象是主程序。对于主程序,dlpi_name字段将为空字符串。

DL_ITERATE_PHDR - Linux手册页

Linux程序员手册 第3部分
更新日期: 2020-06-09

返回值

dl_iterate_phdr()函数返回上次对回调的调用返回的值。

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/

说明

dl_iterate_phdr()函数允许应用程序在运行时进行查询,以了解其已加载了哪些共享库以及它们的加载顺序。

dl_iterate_phdr()函数遍历应用程序共享对象的列表,并为每个对象调用一次函数回调,直到所有共享对象都已处理或回调返回非零值为止。

每个对回调的调用都接收三个参数:info,它是指向包含有关共享库信息的结构的指针;大小,即info指向的结构的大小;和数据,它是调用程序作为对dl_iterate_phdr()的调用中的第二个参数(也称为数据)传递的任何值的副本。

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 dlpi_phdr */

    /* The following fields were added in glibc 2.4, after the first
       version of this structure was available.  Check the size
       argument passed to the dl_iterate_phdr callback to determine
       whether or not each later member is available.  */

    unsigned long long int dlpi_adds;
                    /* Incremented when a new object may
                       have been added */
    unsigned long long int 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's instance
                       of this module's 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字段是一个以空字符结尾的字符串,提供了从中加载共享库的路径名。

为了理解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 */

示例

下面的程序显示它已加载的共享库的路径名列表。对于每个共享对象,程序为ELF段的每个对象列出一些信息(虚拟地址,大小,标志和类型)。

以下shell会话演示了该程序在x86-64系统上产生的输出。显示输出的第一个共享库(名称为空字符串)是主程序。

$ ./a.out
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>

static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
    char *type;
    int p_type, j;

    printf("Name: \"%s\" (%d segments)\n", info->dlpi_name,
               info->dlpi_phnum);

    for (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:%7lx] flags: 0x%x; ", j,
                (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr),
                info->dlpi_phdr[j].p_memsz,
                info->dlpi_phdr[j].p_flags);
        if (type != NULL)
            printf("%s\n", type);
        else
            printf("[other (0x%x)]\n", p_type);
    }

    return 0;
}

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

    exit(EXIT_SUCCESS);
}

属性

有关本节中使用的术语的说明,请参见attribute(7)。

InterfaceAttributeValue
dl_iterate_phdr()Thread safetyMT-Safe

版本

从2.2.4版开始,glibc已支持dl_iterate_phdr()。

另外参见

ldd(1),objdump(1),readelf(1),dladdr(3),dlopen(3),elf(5),ld.so(8)

可执行和链接格式规范,可在线获得在各个位置。

语法

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <link.h>

int dl_iterate_phdr(
          int (*callback) (struct dl_phdr_info *info,
                           size_t size, void *data),
          void *data);

遵循规范

在任何标准中均未指定dl_iterate_phdr()函数。尽管返回的dl_phdr_info结构的详细信息有所不同,但其他各种系统也提供了此功能的版本。在BSD和Solaris上,该结构除其他特定于实现的字段外,还包括字段dlpi_addrdlpi_name,dlpi_phdr和dlpi_phnum。

名称

dl_iterate_phdr-遍历共享对象列表

日期:2019-08-20 18:00:07 来源:oir作者:oir