You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取link_map结构中的l_info、l_libname等扩展字段?

如何获取LinkMap结构体的扩展字段(如l_info)

问题背景

用dladdr()或dl_info获取的link_map结构体仅包含公开的标准字段(l_addr、l_name、l_ld、l_next、l_prev),但实际在glibc内部,这个结构体存在扩展字段(l_real、l_ns、l_libname、l_info、l_phdr等),这些字段会在libc初始化(比如setup_vdso阶段)后被填充,如何通过编程方式访问这些字段?

解决方法

1. 手动定义内部扩展版LinkMap结构体

glibc内部的link_map结构体定义在elf/link.h中,但这个是内部头文件,不对外暴露。你可以手动复制对应glibc版本的结构体定义到代码中(注意不同版本的字段偏移可能有变化,需匹配目标系统的glibc版本)。

以glibc 2.35为例的结构体定义:

#include <stdint.h>

struct link_map_extended {
  uintptr_t l_addr;                /* 共享对象加载的基地址 */
  char *l_name;                    /* 共享对象加载时的绝对路径 */
  void *l_ld;                      /* 共享对象的动态段 */
  struct link_map_extended *l_next, *l_prev; /* 已加载对象的链表 */

  /* 以下为扩展字段 */
  struct link_map_extended *l_real; /* 符号所属的实际对象 */
  struct namespace *l_ns;          /* 该link_map所属的命名空间 */
  const char *l_libname;           /* 库名称(来自DT_SONAME) */
  struct r_debug *l_r_debug;       /* 指向r_debug结构的指针 */
  struct link_map_extended *l_loader; /* 加载器的link_map */
  ElfW(Phdr) *l_phdr;              /* 程序头表指针 */
  ElfW(Addr) l_phaddr;             /* 程序头表的地址 */
  size_t l_phnum;                  /* 程序头数量 */
  uint32_t l_flags;                /* 标志位 */
  uint32_t l_nbuckets;             /* 哈希桶数量 */
  uint32_t l_symbias;              /* 符号偏移量 */
  const ElfW(Sym) *l_symtab;       /* 符号表 */
  const char *l_strtab;            /* 字符串表 */
  uint32_t l_chain;                /* 哈希表链 */
  uint32_t l_version;              /* 版本号 */
  struct dl_phdr_info *l_phdr_info; /* 指向dl_phdr_info结构的指针 */
  struct link_map_extended *l_next_run; /* 运行顺序中的下一个对象 */
  struct link_map_extended *l_prev_run; /* 运行顺序中的上一个对象 */
  void *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM]; /* 动态段条目 */
};

之后将dladdr()获取的link_map指针强制转换为这个扩展结构体指针,即可直接访问l_info字段:

#include <dlfcn.h>
#include <stdio.h>

// 上面的struct link_map_extended定义放在此处

int main() {
  void *addr = &main;
  Dl_info info;
  if (dladdr(addr, &info)) {
    struct link_map_extended *lm_ext = (struct link_map_extended *)info.dli_fbase;
    // 示例:获取DT_SONAME条目
    ElfW(Dyn) *soname_dyn = lm_ext->l_info[DT_SONAME];
    if (soname_dyn) {
      const char *soname = lm_ext->l_strtab + soname_dyn->d_un.d_val;
      printf("SONAME: %s\n", soname);
    }
  }
  return 0;
}

2. 用dl_iterate_phdr间接获取信息

如果不想依赖内部结构体定义,可以使用dl_iterate_phdr()遍历加载的共享库,通过程序头找到动态段,间接获取l_info对应的动态条目。

示例代码:

#include <link.h>
#include <stdio.h>

static int phdr_callback(struct dl_phdr_info *info, size_t size, void *data) {
  // 遍历程序头找到动态段
  for (int i = 0; i < info->dlpi_phnum; i++) {
    if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
      ElfW(Dyn) *dyn = (ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
      // 遍历动态段条目
      while (dyn->d_tag != DT_NULL) {
        if (dyn->d_tag == DT_SONAME) {
          const char *soname = (const char *)(info->dlpi_addr + dyn->d_un.d_val);
          printf("库路径: %s, SONAME: %s\n", info->dlpi_name, soname);
        }
        dyn++;
      }
      break;
    }
  }
  return 0;
}

int main() {
  dl_iterate_phdr(phdr_callback, NULL);
  return 0;
}

3. 注意事项

  • 结构体偏移依赖glibc版本:不同glibc版本的link_map扩展字段可能有增减或偏移变化,直接强制转换可能引发内存访问错误,建议在目标系统上查看elf/link.h的实际定义。
  • 兼容性有限:这种方法依赖glibc内部实现,不保证跨版本、跨发行版的兼容性,仅适合调试或特定场景使用。
  • 编译需链接动态库:编译时需添加-ldl选项,例如gcc -o test test.c -ldl。

内容的提问来源于stack exchange,提问作者Baba Rocks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 02:30:50