我使用的是一个共享库,它拥有相当多的全局变量,用于几乎所有导出的函数,因此库函数不是线程安全的。我的应用程序创建多个线程,每个线程动态地打开这个库,为了避免使用对导出函数的并行调用之间的任何同步,我多次以不同的名称在磁盘上复制该库,每个线程都打开自己的副本。为了避免这种情况,现在我希望使用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 _ZTIN6google8protobuf11MessageLiteE符号。
Question1:我想知道如何解决这个问题,这样我才能使用dlmopen。
原因是当您使用LM_ID_NEWLM时,将在libc中创建一个新的空命名空间,没有任何符号。因此,库应该是独立包含的或与任何依赖项重新链接的。
Question2:我的主要应用程序导出了libfoo将要使用的一些符号。由于在新的命名空间中打开libfoo,主应用程序的符号对libfoo不可见,因此无法解析它们。有什么方法可以告诉链接器创建一个新的命名空间NEWLM,方法是复制现有的基本命名空间,而不是使用新创建的名称空间的dlmopen + lmid来打开libfoo,并且所有其他必需的符号都已经存在了?
Question3:我自己可以映射libfoo的不同部分,并提供指向mmaped节到libc的指针。意思是把打开文件的工作从libc中取出来,让它做符号解析的工作?这样,我根本不需要调用dlopen,就可以解决多个文本部分的问题。
发布于 2015-11-09 15:29:34
我如何解决这个问题?
使用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,并且所有其他必需的符号都已经存在了?
我想不行。这是个有趣的主意。可以在格列布奇拉中自由地打开一个特性请求。
对于dlmopen来说,这似乎是合理的(尽管最大限制为16)。
在我看来,虽然16个libfoo实例优于一个实例,但在这条路径上仍然受到严重限制,最好首先重写libfoo以避免使用全局。
更新2:
我自己可以映射libfoo的不同部分并提供指向mmaped部分的libc指针吗?
如果实现了GLIBC bug 11767,您可以这样做。但事实并非如此。
发布于 2017-06-26 20:02:36
有什么方法可以告诉链接器创建一个新的命名空间NEWLM,方法是复制现有的基本命名空间,而不是使用新创建的名称空间的dlmopen + lmid来打开libfoo,并且所有其他必需的符号都已经存在了?
下面是我解决类似问题的方法:
发布于 2021-11-06 15:46:16
在dlerror()之后执行dlmopen()检查是否加载了这个dll。(检查dlmopen()的返回值是nullptr?)
您可能会注意到,类似于地址清除器之类的东西会在.so库中放置一些未定义的符号,并在entry ELF文件( int main()放置的)中实现。
要检查这一点,请使用:
readelf -s exe_main |grep __asan_init
readelf -s libfoo.so |grep __asan_init在我的系统中,我可以看到以下情况:
[vrqq@rhel]$ readelf -s ./out/libfoo.so |grep __asan_init
26: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND __asan_init
226: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND __asan_init
[vrqq@rhel]$ readelf -s ./out/exe_main |grep __asan_init
1279: 00000000002fe430 96 FUNC GLOBAL DEFAULT 16 __asan_init
5017: 00000000002fe430 0 NOTYPE LOCAL DEFAULT 16 .annobin___asan_init.star
5018: 00000000002fe490 0 NOTYPE LOCAL DEFAULT 16 .annobin___asan_init.end
9044: 00000000002fe430 96 FUNC GLOBAL DEFAULT 16 __asan_inithttps://stackoverflow.com/questions/33497784
复制相似问题