未解决的符号只有dlmopen而不是dlopen

时间:2015-11-03 11:17:36

标签: multithreading dll linker dynamic-linking dlopen

我使用的共享库有很多全局变量, 几乎用过 所有导出的函数都使库函数不是线程安全的。 我的应用程序创建多个线程,每个线程动态打开它 库并避免在并行调用之间使用任何同步 出口 函数,我用磁盘上的不同名称多次复制了库 每个线程都打开自己的副本。为了避免这种情况,现在我正在寻找使用dlmopen,但我遇到了一个问题。

当我在我的应用程序中使用dlopen打开库时,应用程序正常运行

libHandle = dlopen(ip->pathname, (RTLD_LAZY |RTLD_LOCAL|RTLD_DEEPBIND|RTLD_NODELETE));

当我在应用程序中使用dlmopen时,我收到错误:

ip->libHandle = dlmopen(LM_ID_NEWLM, ip->pathname,
                (RTLD_LAZY |RTLD_LOCAL|RTLD_DEEPBIND|RTLD_NODELETE));

dlerror是:

error(libfoo.so.0: undefined symbol: _ZTIN6google8protobuf11MessageLiteE)

执行nm确实会显示未定义的符号 U _ZTIN6google8protobuf11MessageLiteE

问题1:我想知道如何解决此问题,以便我可以使用dlmopen。

原因是因为当使用LM_ID_NEWLM时,在libc中创建一个新的空命名空间而没有任何符号。因此,库应该是自包含的,或者与任何依赖项重新链接。

问题2:我的主应用程序导出一些libfoo将使用的符号。由于在新命名空间中打开libfoo,主应用程序的符号对libfoo不可见,因此无法解析它们。 有没有办法告诉链接器创建一个新的命名空间NEWLM,通过制作现有的基本命名空间的副本,而不是使用新创建的命名空间的dlmopen + lmid打开libfoo,其中所有其他必需的符号已经存在?

问题3:我自己可以对libfoo的不同部分进行mmap,并提供指向libc的mmaped部分的指针。意味着打开文件并将其从libc中删除并让它完成符号解析工作?这样我根本不需要调用dlopen,并且可以解决多文本部分问题。

2 个答案:

答案 0 :(得分:3)

  

有没有办法告诉链接器创建一个新的命名空间NEWLM,通过制作现有的基本命名空间的副本,而不是使用新创建的命名空间的dlmopen + lmid打开libfoo,其中所有其他必需的符号已经存在?

以下是我解决类似问题的方法:

  1. 将protobuf动态加载到新的命名空间中:

    void* pb_handle = dlmopen(LM_ID_NEWLM, "libprotobuf.so", RTLD_LAZY);
    
  2. 抓住名称空间ID:

    Lmid_t lmid;
    dlinfo(dl_handle, RTLD_DI_LMID, &lmid);
    
  3. 将foo打开到新的protobuf命名空间中:

    void* foo_handle = dlmopen(lmid, “libfoo.so.0”, RTLD_LAZY);
    

答案 1 :(得分:2)

  

如何解决此问题

使用dlopen时,新加载的库可以使用所有已加载的库来解析其符号。我猜测libprotobuf.so是已经加载的库之一。

当您使用dlmopen(LM_ID_NEWLM, ...)时,新加载的库必须完全自包含。

dlmopen失败的事实告诉你它不是。您应该将libfoo.so.0重新链接到libprotobuf.so(以及它需要的任何其他库)。

使用ldd -r libfoo.so.0验证其中的所有符号都已解析。在链接-Wl,--no-undefined时使用libfoo.so.0也是一个好主意。

<强>更新

  

我的主应用程序导出一些libfoo将使用的符号。由于在新命名空间中打开libfoo,主应用程序的符号对libfoo不可见,因此无法解析它们。

这是预期的行为。如果这些符号的数量相当少,您可以使用libfoo

明确注册它们
void *h = dlmopen(...);
void (*init)(void *, void *) = dlsym(h, 'init');
(*init)(&main_fn1, &main_fn2);
  

有没有办法告诉链接器创建一个新的命名空间NEWLM,通过制作现有的基本命名空间的副本,而不是使用新创建的命名空间的dlmopen + lmid打开libfoo,其中所有其他必需的符号已经存在?

我不相信。这是一个有趣的想法。您可以在glibc bugzilla中打开功能请求。

  

使用dlmopen似乎是合理的(尽管最大限制为16)

在我看来,虽然libfoo的16个实例优于1个,但在这条路径上你仍然受到严重限制,重写libfoo以便不使用全局变量会好得多。第一名。

更新2:

  

我可以自己mmap libfoo的不同部分并提供指向libc的mmaped部分的指针

如果实施了GLIBC bug 11767,您可以。但事实并非如此。