在多线程情况下修改堆栈

时间:2016-10-24 07:14:33

标签: c linux multithreading shared-libraries glibc

我们在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()返回的句柄。

更新:我感谢所有评论这个问题的人,尽管缺乏几乎任何可行的信息,但我还是试着回答这个问题。我找到了这个问题的解决方案。正如评论者所预见的那样,它与我提供的堆栈跟踪和其他信息完全无关。因此,我认为这个问题是封闭的,并将其标记为删除。 这么久,感谢所有鱼

1 个答案:

答案 0 :(得分:1)

  

第8帧和第7帧之间库句柄差异的可能来源是什么?

最可能的原因是ld-linux.solibdl.so之间不匹配。正如this answer中所述,ld-linuxlibdl 必须来自同一版本的GLIBC,否则会发生不好的事情。

不匹配可能来自(A)尝试通过libc指向不同的LD_LIBRARY_PATH构建,或(B)通过将libdl.a静态链接到程序中。

(gdb) info shared应显示当前加载的库。如果您看到已安装系统ld-linuxlibdl以外的内容,则(A)可能是您的问题。

对于(B),您可能得到(并忽略)一个链接器警告,表明您的程序在运行时将需要用于链接它的相同的 libc版本。与流行的看法相反,全静态二进制文件在Linux上 可移植,而不是更多。