为什么动态链接器要*减去虚拟地址来找出加载的共享库可执行文件在内存中的位置?

问题描述 投票:1回答:1

根据ld的源码 此处dl_main. 如果不知道什么情况下 phdr 传入 dl_main我有点不明白为什么main_map的加载地址是通过减去虚拟地址来推导的。

我追踪过的代码。

1124 static void
1125 dl_main (const ElfW(Phdr) *phdr,
1126          ElfW(Word) phnum,
1127          ElfW(Addr) *user_entry,
1128          ElfW(auxv_t) *auxv)
1129 {
1130   const ElfW(Phdr) *ph;
1131   enum mode mode;
1132   struct link_map *main_map;
1133   size_t file_size;
1134   char *file;
1135   bool has_interp = false;
1136   unsigned int i;
1137   bool prelinked = false;
1138   bool rtld_is_main = false;
1139   void *tcbp = NULL;
...    // Before this else, it thinks you're calling `ld.so.<version>` directly. This is not usually the case.
1366   else
1367     {
1368       /* Create a link_map for the executable itself.
1369          This will be what dlopen on "" returns.  */
1370       main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
1371                                  __RTLD_OPENEXEC, LM_ID_BASE);
1372       assert (main_map != NULL);
1373       main_map->l_phdr = phdr;
1374       main_map->l_phnum = phnum;
1375       main_map->l_entry = *user_entry;
1376 
1377       /* Even though the link map is not yet fully initialized we can add
1378          it to the map list since there are no possible users running yet.  */
1379       _dl_add_to_namespace_list (main_map, LM_ID_BASE);
1380       assert (main_map == GL(dl_ns)[LM_ID_BASE]._ns_loaded);
1399     }
...    // Loops through program headers loaded in sequence from the ELF header.
1409   for (ph = phdr; ph < &phdr[phnum]; ++ph)
1410     switch (ph->p_type)
1411       {
1412       case PT_PHDR:
1413         /* Find out the load address.  */
1414         main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr;
1415         break;

那为什么这里要减去加载地址呢?

c++ ld elf
1个回答
2
投票

特别是在第1414行,我们看到了以下内容 main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr;. 从文件中 link.h 的类型,它定义了 link_map 对于 main_map我明白了 link_map 是一个 结构 描述一个加载的共享对象。该 l_addr 字段是用来描述 .so加载到内存中的位置与它说的在 VirtAddr 领域 当你运行 readelf:

❯ readelf -l main

Elf file type is EXEC (Executable file)
Entry point 0x401020
There are 11 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x0000000000000268 0x0000000000000268  R      0x8
  INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000004c0 0x00000000000004c0  R      0x1000
...

这意味着 l_addr 不是 负荷 地址。其实是 偏移 你需要从当前的内存中添加以访问共享对象的内容。

© www.soinside.com 2019 - 2024. All rights reserved.