侧边栏壁纸
博主头像
noerror

虚灵不寐,众理具而万事出。

  • 累计撰写 239 篇文章
  • 累计创建 9 个标签
  • 累计收到 2 条评论
标签搜索

目 录CONTENT

文章目录

dl_iterate_phdr函数用法详解

noerror
2022-10-19 / 0 评论 / 3 点赞 / 256 阅读 / 1,731 字 / 正在检测是否收录...

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 。关于这些类型的进一步信息可以在 " and " 头文件中找到。)
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);
}
3

评论区