最近我尝试构建一个复杂的 C++ 应用程序。应用程序已构建。但是当我尝试启动它时,它因非法内存访问而崩溃。实际上它是带有偏移量的空指针。 我开始调查原因。使用 dlopen 动态加载库 (libXcursor.so) 时发生崩溃。所以原因是空指针
link_map->l_versions
,在elf_machine_rela
函数内部重定位的过程中没有检查(特别是在RESOLVE_MAP
宏中)。这发生在这里:map->l_versions[ndx]
ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)],
&map->l_versions[ndx],
(void *) (l_addr + r->r_offset), skip_ifunc);
前面提到的
link_map
是库在加载过程中的依赖描述。正如我所看到的,这种依赖关系尚未完全初始化。但更奇怪的是,这实际上并不是依赖项:它是一个 libgthread
库 DSO。而 libXcursor.so 确实不依赖于它。
提到的
link_map
的libgthread
从dl_open_worker进行搬迁:
_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
正在收集到家属名单这样:
l = new;
do
{
if (! l->l_real->l_relocated)
maps[nmaps++] = l;
l = l->l_next;
}
while (l != NULL);
这里
new
是libXcursor.so本身找到的link_map
。
这段代码很奇怪恕我直言。
因为同时其他部门实际上是用这段代码初始化的:
/* Load that object's dependencies. */
_dl_map_object_deps (new, NULL, 0, 0,
mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
/* So far, so good. Now check the versions. */
for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
if (new->l_searchlist.r_list[i]->l_real->l_versions == NULL)
(void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real,
0, 0);
所以问题是为什么其他依赖项列表被传递,并且在如此重要的地方(系统加载器)没有进行额外的检查。以及可能在哪里修复崩溃。
使用的libdl版本是来自glibc-2.27-38.fc28.x86_64的/lib64/libdl-2.27.so