PLT Hook的死锁递归bug排查
这篇博客记录一下开发内存泄露和死锁检测 SDK 过程中遇到的一个 bug。
问题背景
SDK 使用的是 PLT Hook,也就是替换要检测模块的 GOT,使其指向 SDK 的函数。
按理来说每个模块的 PLT 都是隔离的,我去 hook 其他模块的 GOT,SDK 自己的模块不会受到任何影响,也就是说不会出现 hook 中比较常见的递归 hook 问题。
在内存泄露模块开发时,没有遇到任何问题。但在开发死锁检测模块时,出现了 SDK 自己的函数被自己 hook 了,导致递归栈溢出的问题。
问题分析
这个问题的解决方法也不难,就是加一个防重入标志。但是这个问题是怎么造成的?为什么 malloc 等函数的 hook 就没有出现这个问题?
经过一段时间的调试和拷打ai,得到了以下结论:
根本原因
导致无限递归的是 mutex::lock。
被检测模块导出了 mutex::lock 符号,如下:
1 | test@test-VMware-Virtual-Platform:~/projects/nv_detector/build/examples$ nm -D /home/test/projects/nv_detector/build/examples/deadlock_test | grep -E "mutex.*lock|_ZNSt5mutex4lockEv" |
为什么会导出 mutex::lock 符号呢?是因为在 mutex 头文件中,包含了 <bits/std_mutex.h>,而在 std_mutex.h 中,包含了 mutex::lock 的实现。如下:
1 | class mutex : private __mutex_base |
在 SDK 中 hook 的是 pthread_mutex_lock,__gthread_mutex_lock 展开后就是 pthread_mutex_lock。
同时在当前的编译选项下,没有将它内联进去,如下图所示:

这两个因素导致了主模块会导出 mutex::lock 的符号。
然后又因为符号抢占,SDK 的 so 绑定到了目标模块的 mutex::lock 上,mutex::lock 会调用 pthread_mutex_lock,这个函数又被自身给 hook 了,导致了递归的 hook。
为什么 malloc 没有问题?
那 malloc 等函数为什么没有出现这个问题呢?因为它们的实现都在源文件中,动态链接下不会包含到我们写的代码的二进制文件中,也就不会导出这个函数的符号,也就没有后面的问题了。
可以用命令行来看一下,libnv_detector.so 绑定到了 deadlock_test 的 _ZNSt5mutex4lockEv:
1 | test@test-VMware-Virtual-Platform:~/projects/nv_detector/build$ LD_DEBUG=bindings timeout 5 ./examples/deadlock_test 2>&1 | grep "libnv_detector.*to.*deadlock_test" |
解决方案
解决的方法就是修改编译参数,加上 -fvisibility-inlines-hidden,让符号不导出,如下:
1 | test@test-VMware-Virtual-Platform:~/projects/nv_detector/build$ cd "/home/test/projects/nv_detector/build" && nm -D ./examples/deadlock_test 2>&1 | grep -E "mutex|lock" |
可以看到没有 mutex::lock 的符号了。





