pthread_detach()在64位Linux上导致SIGSEGV

这是我的情况的描述:我必须照顾我们的产品中的错误。 线程被创build为joinable ,它必须完成它的工作,终止,没有人会为它调用pthread_join() 。 所以线程是使用JOINABLE属性创build的(默认情况下),在终止之前调用下一个代码:

 { pthread_detach(pthread_self()); pthread_exit(NULL); } 

它在我遇到的所有32位Linux发行版中都像一个魅力,但它导致了64位发行版(Ubuntu 13.04 x86_64和Debian)上的SIGSEGV 。 我没有尝试与Slackware。 这是一个核心:

 Core was generated by `IsaVM -s=1 -PrjPath="/home/taf/Linux_Fov_540148/Cmds" -stgMode=1 -PR -Failover'. Program terminated with signal 11, Segmentation fault. #0 0x00007f5911a7c009 in pthread_detach () from /lib/x86_64-linux-gnu/libpthread.so.0 (gdb) bt #0 0x00007f5911a7c009 in pthread_detach () from /lib/x86_64-linux-gnu/libpthread.so.0 #1 0x000000000041310d in _kerCltDownloadThr (StartParams=0x6bfce0 <RESFOV>) at ./dker0clt.c:1258 #2 0x00007f5911a7ae9a in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0 #3 0x00007f591159f3fd in clone () from /lib/x86_64-linux-gnu/libc.so.6 #4 0x0000000000000000 in ?? () 

我想出了如何解决这个错误 – 我创buildCREATE_DETACHABLE属性(与pthread_attr_setdetachstate() )为线程创build之前,它按预期方式工作。

但是我的问题 – 称这个代码是犯罪吗?

 { pthread_detach(pthread_self()); pthread_exit(NULL); } 

pthread_detach()是否会在调用之后asynchronous执行某些操作,导致pthread_exit()带来问题? 但是崩溃点是pthread_detach()而不是pthread_exit() ! 我完全不明白这个崩溃的原因! 为什么它在32位上工作? 这是pthread实现中的某处竞争条件吗?

pthread_join()不会调用这个线程。

提前感谢您的任何想法。

脱离线程本身并不正确。 这通常是调用pthread_create()的线程的责任,如果需要,可以创建一个分离的线程。

这可能是该线程已被分离。 因为尝试分离已经分离的线程会导致未指定的行为。

我最大的猜想是:

  1. 线程被分离不止一次。 作为一个快速检查,我会尝试在gdb pthread_detach上设置一个断点,以查看重复的线程ID是否在此函数中传递。 如果在gdb下运行你的应用程序很困难,另一个选择是覆盖pthread_createpthread_detach并跟踪线程id来检测双重分离。 见http://hackerboss.com/overriding-system-functions-for-fun-and-profit/

  2. 内存损坏。 valgrind可以帮助你检测内存损坏,如果它可以运行你的应用程序。 或者,如果您使用gcc ,请尝试使用-fstack-protector-all-fsanitize=address-fsanitize=thread编译运行时错误检查。 clang编译器也有一些选项来检测这些错误,请参阅http://clang.llvm.org/docs/index.html上&#x7684; sanitizers。

我用一个可敬的@MaximYegorushkin提供的方法完成了我的研究。 AddressSanitizer在我们的产品中向我显示了一个缓冲区流,但它与我的问题没有关系(我以后肯定会修复这个问题,拥有这样一个聪明的工具来寻找bug是很好的)。 于是决定用LD_PRELOAD方法覆盖所有必要的pthread_xxx函数。 我运行一个简单的测试,以确保我的图书馆按预期工作:

 [HACK] Loading pthread hack. Starting thread...! [HACK] pthread_create: thread=7FAC6C86D700 Waiting for 2 seconds... [HACK] pthread_self: thread=7FAC6C86D700 thread_func: thread id = 7FAC6C86D700 Thread: sin(3.26) = -0.121109 [HACK] pthread_self: thread=7FAC6C86D700 [HACK] pthread_detach: thread=7FAC6C86D700 Terminating... 

[HACK]开始的所有字符串都是由我的threadhack.so库生成的。 然后我用这个库运行我的项目,它指出了问题的确切位置:

代码执行: { pthread_detach(pthread_self()); pthread_exit(NULL); } { pthread_detach(pthread_self()); pthread_exit(NULL); }

调试跟踪:

 [HACK] pthread_create: thread=7F403251CB00 ..... [HACK] pthread_self: thread=7F403251CB00 [HACK] pthread_detach: thread=3251CB00 

所以我们看到pthread_self返回一个好的线程ID,但是pthread_detach收到它已经被损坏(切换到32位)。 这怎么可能? 我为我的简单工作测试应用程序作为参考和我的项目生成汇编代码:

参考应用:

 call pthread_self movq %rax, %rdi call pthread_detach movl $0, %edi call pthread_exit 

所以我们在这里看到, movq指令用于复制64位线程ID( movq %rax, %rdi )。 好的,检查GCC 为我的项目生成的内容

 movl $0, %eax call pthread_self movl %eax, %edi movl $0, %eax call pthread_detach movl $0, %edi movl $0, %eax call pthread_exit 

WOA! 我们有两个movl指令(32位),一个复制最不重要的32位( movl %eax, %edi ),而不是最重要的部分,它总是把零! ( movl $0, %eax )。 所以这是造成伤亡的原因。 我不知道为什么代码是如此不同 – 编译标志是相同的。 我在GCC 4.7看到了这个bug。我在GCC 4.8Ubuntu 13.10 x86_64最新软件包)中看到了这个bug。

所以至少现在我看到了什么发生。 感谢@Maxim和辉煌的工具。 我又学到了一件新事物。

PS我不知道如何向GCC团队提交错误报告。 我不能在一个小的简单的应用程序上重现问题,我不能把它们交给我的项目,因为它是一个专有软件,我是NDA编辑的,不能分发它。

我的猜测是,在调用pthread_detach(pthread_self())的代码中没有pthread_detach或pthread_self的原型。 如果没有原型,编译器将假定参数是int(pthread_detach),或者函数返回一个int(pthread_self)。

虽然进一步思考,但我更怀疑pthread_self是罪魁祸首,要么是未定义(返回一个int),或不正确地定义为返回一个int。 然后编译器通过添加前32位零来正确地将其扩展为64位整数。