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

问题描述:

我正在使用一个共享库,其中有几个全局变量, 几乎用于所有导出的函数 ,因此库函数不是线程安全的。 我的应用程序创建多个线程,每个线程动态地打开这个 库,并避免使用并行调用 到导出 功能之间的同步,我的图书馆多次以不同的名称复制磁盘与 每个线程打开自己的副本。为了避免这种情况,现在我正在使用dlmopen来代替,但是我正面临一个问题。未解决的符号只有dlmopen而不是dlopen

当我使用的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显示符号undefined U _ZTIN6google8protobuf11MessageLiteE

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

原因是因为当使用LM_ID_NEWLM时,在libc中没有任何符号的情况下会创建一个新的空名称空间。所以这个库应该是自包含的或者与任何依赖关系重新链接。

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

问题3:我可以自己mmap libfoo的不同部分,并提供一个指向libc的mmaped部分的指针。意味着需要打开文件并将其从libc中取出并让其执行符号解析的工作?这样我就不需要调用dlopen,多个文本部分的问题就可以解决。

how can I resolve this issue

当您使用dlopen,新加载的库可以使用所有已经加载库,以解决其符号。我猜测libprotobuf.so是这种已经加载的库之一。

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

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

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

更新:

My main application exports some symbols which libfoo will use. Due to opening libfoo in new namespace, the symbols of main application are not visible to libfoo and hence it is not able to resolve them.

这是预期的行为。如果有这种符号的相当小的数字,你可以明确地libfoo注册它们:

void *h = dlmopen(...); 
void (*init)(void *, void *) = dlsym(h, 'init'); 
(*init)(&main_fn1, &main_fn2); 

Is there any way to tell linker to create a new namespace NEWLM, by making replica of existing base namespace and than use dlmopen + lmid of newly created namespace to open libfoo with all other required symbols being already present?

我不这么认为。这是一个有趣的想法。请随时打开glibc bugzilla中的功能请求。

With dlmopen it seems plausible (although max limit of 16)

在我看来,虽然中libfoo 16个实例是比一个好,你仍然严重这条道路上的限制,这将是更好的重写libfoo在一开始就不使用全局变量。

更新2:

Can I myself mmap the different section of libfoo and provide a pointer to the mmaped sections to libc

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

+0

感谢您的答复。将libfoo与libproto以及所有其他所需的库重新链接是可能的。但是我有更大的问题。我的主应用程序导出libfoo将使用的一些符号。由于在新命名空间中打开libfoo,主应用程序的符号对libfoo不可见,因此无法解析它们。有没有什么办法告诉链接器创建一个新的命名空间NEWLM,通过复制现有的基本命名空间并使用新创建的命名空间的dlmopen + lmid来打开libfoo,其中所有其他必需的符号已经存在? – ashish

+0

此活动的关键是避免由于每个副本实例具有不同的inode号而多次复制同一个库所导致的文本部分的多个副本。将这与inode相同的情况进行比较。在这种情况下,文本部分将在每个dlopen实例之间共享,而每个都有自己的私人数据副本。不幸的是,dlopen不允许多重打开,因为libc代码检查inode是否已经在当前命名空间中打开。使用dlmopen似乎是合理的(虽然最大限制为16),但空的命名空间给我的问题。 – ashish

+0

@ user2270067我已经更新了答案。 –

Is there any way to tell linker to create a new namespace NEWLM, by making replica of existing base namespace and than use dlmopen + lmid of newly created namespace to open libfoo with all other required symbols being already present?

这是我解决了类似的问题的方法:

  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. 开富到您的新protobuf的命名空间:

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