我们在GNU / Linux下通过dlsym()
从共享库加载一个符号,显然会遇到某种竞争条件导致分段错误。回溯看起来像这样:
(gdb) backtrace
#0 do_lookup_x at dl-lookup.c:366
#1 _dl_lookup_symbol_x at dl-lookup.c:829
#2 do_sym at dl-sym.c:168
#3 _dl_sym at dl-sym.c:273
#4 dlsym_doit at dlsym.c:50
#5 _dl_catch_error at dl-error.c:187
#6 _dlerror_run at dlerror.c:163
#7 __dlsym at dlsym.c:70
#8 ... (our code)
我的本地机器使用glibc-2.23。
我发现,第7帧中__dlsym()
的库句柄与传递给_dlerror_run()
的句柄不同。它在dlsym.c
:
void *
__dlsym (void *handle, const char *name DL_CALLER_DECL)
{
# ifdef SHARED
if (__glibc_unlikely (_dlfcn_hook != NULL))
return _dlfcn_hook->dlsym (handle, name, DL_CALLER);
# endif
struct dlsym_args args;
args.who = DL_CALLER;
args.handle = handle; /* <------------------ this isn't my handle! */
args.name = name;
/* Protect against concurrent loads and unloads. */
__rtld_lock_lock_recursive (GL(dl_load_lock));
void *result = (_dlerror_run (dlsym_doit, &args) ? NULL : args.sym);
__rtld_lock_unlock_recursive (GL(dl_load_lock));
return result;
}
GDB说
(gdb) frame 7
#7 __dlsym at dlsym.c:70
(gdb) p *(struct link_map *)args.handle
$36 = {l_addr= 140736951484536, l_name = 0x7fffe0000078 "\300\215\r\340\377\177", ...}
所以这显然是垃圾。在更高的帧中也会发生相同的情况,例如在框架#2中:
(gdb) frame 2
#2 do_sym at dl-sym.c:168
(gdb) p handle
$38 = {l_addr= 140736951484536, l_name = 0x7fffe0000078 "\300\215\r\340\377\177", ...}
不幸的是,第7帧中的参数handle
无法显示:
(gdb) p handle
$37 = <optimized out>
但令人惊讶的是在第8帧和我们的代码中,句柄是正确的:
(gdb) frame 8
#8 ...
(gdb) p *(struct link_map *)libHandle
$38 = {l_addr = 140737160646656, l_name = 0x7fffd8005b60 "/path/to/libfoo.so", ...}
现在我的结论是,在args
内执行期间必须修改变量__dlsym()
,但我看不到在哪里以及为什么。
我必须承认,这个问题还有第二个方面:它只发生在多线程环境中,有时只发生。但正如您所看到的,在__dlsym()
的实现中存在一些针对竞争条件的对策,因为它们调用__rtld_lock_(un)lock_recursive()
并且不在线程之间共享局部变量args
。奇怪的是,如果我在我的线程中使#8帧相互排斥,问题仍然存在。
问题:第8帧和第7帧之间库句柄差异的可能来源是什么?
问题2: dlopen()
是否会为不同的线程产生不同的值?或者换一种说法:是否可以在不同的线程之间共享dlopen()
返回的句柄。
更新:我感谢所有评论这个问题的人,尽管缺乏几乎任何可行的信息,但我还是试着回答这个问题。我找到了这个问题的解决方案。正如评论者所预见的那样,它与我提供的堆栈跟踪和其他信息完全无关。因此,我认为这个问题是封闭的,并将其标记为删除。 这么久,感谢所有鱼
答案 0 :(得分:1)
第8帧和第7帧之间库句柄差异的可能来源是什么?
最可能的原因是ld-linux.so
和libdl.so
之间不匹配。正如this answer中所述,ld-linux
和libdl
必须来自同一版本的GLIBC,否则会发生不好的事情。
不匹配可能来自(A)尝试通过libc
指向不同的LD_LIBRARY_PATH
构建,或(B)通过将libdl.a
静态链接到程序中。
(gdb) info shared
应显示当前加载的库。如果您看到已安装系统ld-linux
和libdl
以外的内容,则(A)可能是您的问题。
对于(B),您可能得到(并忽略)一个链接器警告,表明您的程序在运行时将需要用于链接它的相同的 libc版本。与流行的看法相反,全静态二进制文件在Linux上 可移植,而不是更多。