Multithreading 只有dlmopen而没有dlopen的未解析符号

Multithreading 只有dlmopen而没有dlopen的未解析符号,multithreading,dll,linker,dynamic-linking,dlopen,Multithreading,Dll,Linker,Dynamic Linking,Dlopen,我使用的是一个共享库,它有很多全局变量, 用在几乎 所有导出的函数,因此库函数不是线程安全的。 我的应用程序创建多个线程,每个线程动态地打开这个 库,以避免在并行调用之间使用任何同步 出口 函数,我在磁盘上用不同的名称多次复制了库 每个线程打开自己的副本。为了避免这种情况,现在我希望改用dlmopen,但我面临一个问题 当我在应用程序中使用dlopen打开库时,应用程序运行良好 libHandle = dlopen(ip->pathname, (RTLD_LAZY |RTLD_LOCAL|

我使用的是一个共享库,它有很多全局变量, 用在几乎 所有导出的函数,因此库函数不是线程安全的。 我的应用程序创建多个线程,每个线程动态地打开这个 库,以避免在并行调用之间使用任何同步 出口 函数,我在磁盘上用不同的名称多次复制了库 每个线程打开自己的副本。为了避免这种情况,现在我希望改用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));
错误是:

error(libfoo.so.0: undefined symbol: _ZTIN6google8protobuf11MessageLiteE)
执行nm时,显示符号未定义 U_ztin6Google8Protobuf11消息源

问题1:我想知道如何解决这个问题,以便使用dlmopen

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

问题2:我的主应用程序导出了libfoo将使用的一些符号。由于在新名称空间中打开libfoo,libfoo看不到主应用程序的符号,因此无法解析它们。 有没有办法告诉链接器创建一个新的名称空间NEWLM,方法是复制现有的基本名称空间,然后使用新创建的名称空间的dlmopen+lmid打开libfoo,同时所有其他必需的符号都已经存在

问题3:我可以自己映射libfoo的不同部分并提供指向libc的mmaped部分的指针吗。意味着打开文件并将其从libc中删除,然后让它执行符号解析的任务?这样,我根本不需要调用dlopen,多文本部分的问题就会得到解决

我如何解决这个问题

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

使用
dlmopen(LM\u ID\u NEWLM,…)
时,新加载的库必须完全独立

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

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

更新:

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

这是预期的行为。如果此类符号的数量相当少,则可以将其显式注册到
libfoo

void *h = dlmopen(...);
void (*init)(void *, void *) = dlsym(h, 'init');
(*init)(&main_fn1, &main_fn2);
有没有办法告诉链接器创建一个新的名称空间NEWLM,方法是复制现有的基本名称空间,然后使用新创建的名称空间的dlmopen+lmid打开libfoo,同时所有其他必需的符号都已经存在

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

对于dlmopen,这似乎是合理的(尽管最大限制为16)

在我看来,
libfoo
的16个实例比一个好,但您仍然在这条道路上受到严重限制,重写
libfoo
一开始就不使用globals会更好

更新2:

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

如果实现了GLIBC,您可以。但事实并非如此

有没有办法告诉链接器创建一个新的名称空间NEWLM,方法是复制现有的基本名称空间,然后使用新创建的名称空间的dlmopen+lmid打开libfoo,同时所有其他必需的符号都已经存在

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

  • 将protobuf动态加载到新命名空间中:

    void* pb_handle = dlmopen(LM_ID_NEWLM, "libprotobuf.so", RTLD_LAZY);
    
    void* foo_handle = dlmopen(lmid, “libfoo.so.0”, RTLD_LAZY);
    
  • 获取名称空间id:

    Lmid_t lmid;
    dlinfo(dl_handle, RTLD_DI_LMID, &lmid);
    
  • 在新protobuf命名空间中打开foo:

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

  • 谢谢你的回复。可以将libfoo与libproto和所有其他必需的库重新链接。但我有一个更大的问题。我的主应用程序导出libfoo将使用的一些符号。由于在新名称空间中打开libfoo,libfoo看不到主应用程序的符号,因此无法解析它们。有没有办法告诉链接器创建一个新的名称空间NEWLM,通过复制现有的基本命名空间,然后使用新创建的命名空间的dlmopen+lmid打开libfoo,并且所有其他必需的符号都已存在?此活动的关键是避免多次复制同一库而导致文本节的多个副本,因为每个副本实例都有不同的inode号码。将其与inode相同的情况进行比较。在这种情况下,文本部分将在每个dlopen实例之间共享,而每个实例都有自己的私有数据副本。不幸的是,dlopen不允许多重打开,因为libc代码检查inode是否已在当前命名空间中打开。对于dlmopen,这似乎是合理的(尽管最大限制为16),但空名称空间给我带来了问题。感谢您的回答。我同意,走德尔莫本之路也不是一个完整的解决方案,但有点更好。我也同意,改变libfoo将是最合适的途径。不过我有一个(希望是最后一个)问题。这只是一个海军问题,对不起,我没有足够的链接/加载知识。我可以自己mmap libfoo的不同部分并向libc提供指向mmaed部分的指针吗。意味着从libc中取出打开文件和mmaping的任务,并让它完成