如何在C++中查找ELF二进制文件所需的动态库?

11

如何使用C ++在Linux中获取elf二进制文件所需的所有动态库列表?

一旦我从二进制文件中提取了信息(文件名?),我可以通过搜索PATH找到实际文件,但我还没有找到有关从ELF二进制文件中提取未改变信息的任何信息。

有什么想法吗?


如果你使用基于RHEL的发行版(具有包管理基础的rpm),你可以尝试使用此脚本来解决二进制文件的依赖关系,不仅限于*.so文件,还包括提供它们的软件包。 - Vitaly Isaev
1
你为什么问呢?你关心间接依赖关系吗(例如可执行文件 foo 动态链接到 libbar.so,而 libbar.so 本身又动态链接到 libgee.so,因此 ldd foo 将同时显示 libbar.solibgee.so)? - Basile Starynkevitch
我之所以问这个问题,是因为我正在使用静态分析工具,并且需要从目标二进制文件以及它依赖的任何动态库中提取CFG。 - John Smith
使用 readelf -d 直接查找 Linux ELF 二进制文件的共享对象依赖关系:https://dev59.com/6m025IYBdhLWcg3wEhZb,使用 ldd 间接查找:http://unix.stackexchange.com/questions/120015/how-to-find-out-the-dynamic-libraries-executables-loads-when-run - Ciro Santilli OurBigBook.com
2
可能是在Linux上显示可执行文件使用的所有库的重复问题。 - Ciro Santilli OurBigBook.com
显示剩余3条评论
3个回答

13
你可以调用 "readelf -d" 程序并解析输出:
readelf -d /usr/bin/readelf | grep NEEDED
 0x0000000000000001 (NEEDED)             Shared library: [libz.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

12
所需共享对象列表存储在可执行文件的所谓动态部分中。获取必要信息的大致算法如下:
  1. 解析ELF头,检查文件是否为动态可执行文件(ET_EXECET_DYN)。
  2. 获取程序头的偏移量和计数(e_phoff/e_phnum/e_phentsize),检查它们是否非零且有效。
  3. 解析程序头,查找PT_DYNAMIC。同时记住PT_LOAD段的虚拟地址 -> 文件偏移映射。
  4. 一旦找到,解析动态部分。查找DT_NEEDEDDT_STRTAB条目。

DT_NEEDED条目中的d_val字段是指向DT_STRTAB字符串表的偏移量,该字符串表将是所需库的SONAME。请注意,由于DT_STRTAB条目是运行时地址而不是字符串表的偏移量,因此您需要使用存储在第3步中的信息将其映射回文件偏移量。


谢谢,我认为这应该是一个相当普遍的问题,即访问二进制文件中的信息。是否有任何库、开源项目或类似的东西可以提供此功能,而无需自己实现和维护它? - John Smith
4
据我所知,大多数人在编写程序时不需要使用它,因此他们依赖于“readelf”、“objdump”或“ldd”进行脚本编写。如果需要进行编程访问,可以使用“libelf”,但它并不提供一个专门用于这个特定任务的现成 API - 你仍然需要手动解析动态部分。 - Igor Skochinsky

0

您可以使用 libelf 来实现此操作。请注意,libelf 具有 C API。

从他们的教程 这里,查看第4.2节中的示例(或这里)以获取程序头表。找到 DT_DYNAMIC 部分,并像第5.4节中的示例(或这里)中的方式从字符串表中读取依赖项。

-- 编辑 --

实际上我有机会编写代码。以下是我的代码:

#include <assert.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdio.h>
#include <unistd.h>

void print_dt_needed(const char *elf_path) {
  assert(elf_version(EV_CURRENT) != EV_NONE);

  int fd = open(elf_path, O_RDWR, 0);
  assert(fd >= 0);

  Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
  assert(elf != NULL);
  assert(elf_kind(elf) == ELF_K_ELF);

  Elf_Scn *scn = NULL;
  while ((scn = elf_nextscn(elf, scn)) != NULL) {
    GElf_Shdr shdr = {};
    assert(gelf_getshdr(scn, &shdr) == &shdr);

    if (shdr.sh_type == SHT_DYNAMIC) {
      Elf_Data *data = NULL;
      data = elf_getdata(scn, data);
      assert(data != NULL);

      size_t sh_entsize = gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT);

      for (size_t i = 0; i < shdr.sh_size / sh_entsize; i++) {
        GElf_Dyn dyn = {};
        assert(gelf_getdyn(data, i, &dyn) == &dyn);

        if (dyn.d_tag == DT_NEEDED) {
          printf("DT_NEEDED detected: %s\n",
                 elf_strptr(elf, shdr.sh_link, dyn.d_un.d_val));
        }
      }
    }
  }
  assert(elf_end(elf) == 0);
  assert(close(fd) == 0);
}

int main(int argc, char const *argv[]) {
  print_dt_needed(argv[1]);
  return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接