代碼使用std::thread,使用-static -lpthread靜態編譯後,運行段錯誤的問題?
詳細過程就是在Linux下使用gcc靜態編譯程序,編譯沒有任何警告和錯誤。編譯完成後,一運行就報段錯誤(Segmentation fault)。gdb調試將問題定位在std::thread,只要調用std::thread::join()或者std::thread::detach(),馬上出問題。
劃重點:
1.常規動態編譯,編譯和運行都沒有任何問題。
2.只有靜態編譯的程序會出現這個問題。
3.這個問題只出現在Linux下,Windows下使用MinGW並不會出現問題。
4.目前使用boost::thread代替std::thread,暫時解決了問題,但治標不治本。
5.gcc版本7.2.0,經朋友測試4.x和5.x都會出現這個問題。
6.不要問我為什麼非要靜態編譯。
代碼補充:(main.cpp)
#include &
#include &
using namespace std;
void Test(){
for (int i=0; i&<100; ++i){
cout&<&
}
}
auto main()-&>int{
thread t(Test);
t.join();
return 1;
}
編譯參數:
g++ -std=c++1z -O3 -Wall -static main.cpp -o test -lpthread
加上這個鏈接選項即可:
-Wl,--whole-archive -lpthread -Wl,--no-whole-archive
而且這個問題目前只在ubuntu上有,在Redhat系平台上不需要這麼做(謝謝 @不鋒利的鋒 的復現,因為有這樣的討論可以讓我們把問題看的更清晰:參看這個答案以及下面的評論 不鋒利的鋒:代碼使用std::thread,使用-static -lpthread靜態編譯後,運行段錯誤的問題?)。
原因在 @rsgAh 裡面提到了 知乎用戶:代碼使用std::thread,使用-static -lpthread靜態編譯後,運行段錯誤的問題?pthread_join是weak symbol,默認靜態鏈接找到第一個符號就不找了,而whole archive會把每一個object file包含進來,那麼當把strong symbol的pthread_join加進來以後,這個時候weak symbol的pthread_join就會被strong symbol的「覆蓋」掉。但是在Redhat上,首先找到的就不是weak symbol,於是就不需要額外的處理方式了。Ubuntu:
Redhat:
無論是這裡的鏈接選項還是 @陳碩 的方法,都是把pthread_join的weak symbol變為了strong symbol.
而按照不鋒利的鋒:代碼使用std::thread,使用-static -lpthread靜態編譯後,運行段錯誤的問題?下面的討論,這裡面要正常執行,不僅僅有pthread_join weak symbol,還有其它函數,所以比較好的方式是我這種,保證是strong symbol。代碼呢?
顯式引用一下 pthread_join 即可。
void foo() int main()#include &
{
printf("hello
");
}
{
auto* f = pthread_join; // ******!!!!!******
std::thread t(foo);
t.join();
}
找到了:https://stackoverflow.com/questions/35116327/when-g-static-link-pthread-cause-segmentation-fault-why
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x0000000000425a27 in std::thread::join() ()
#2 0x0000000000400f0f in main () at main.cc:14
Dump of assembler code for function _ZNSt6thread4joinEv:
0x0000000000425a00 &<+0&>: push %rbx
0x0000000000425a01 &<+1&>: mov %rdi,%rbx
0x0000000000425a04 &<+4&>: mov (%rdi),%rdi
0x0000000000425a07 &<+7&>: mov $0x16,%eax
0x0000000000425a0c &<+12&>: test %rdi,%rdi
0x0000000000425a0f &<+15&>: jne 0x425a20 &<_ZNSt6thread4joinEv+32&>
0x0000000000425a11 &<+17&>: mov %eax,%edi
0x0000000000425a13 &<+19&>: callq 0x412eb0 &<_ZSt20__throw_system_errori&>
0x0000000000425a18 &<+24&>: nopl 0x0(%rax,%rax,1)
0x0000000000425a20 &<+32&>: xor %esi,%esi
0x0000000000425a22 &<+34&>: callq 0x0
0x0000000000425a27 &<+39&>: test %eax,%eax
0x0000000000425a29 &<+41&>: jne 0x425a11 &<_ZNSt6thread4joinEv+17&>
0x0000000000425a2b &<+43&>: movq $0x0,(%rbx)
0x0000000000425a32 &<+50&>: pop %rbx
0x0000000000425a33 &<+51&>: retq
nm -u & 無腦猜測std::thread::join調用了pthread_join(就是那個callq 0x0),然而pthread_join是個weak symbol(0x0)。所以剩下的工作就是把它變成一個strong symbol就好了.
w __call_tls_dtors
w _dl_rtld_map
w _dl_starting_up
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
w _pthread_cleanup_pop_restore
w _pthread_cleanup_push_defer
w __pthread_cleanup_upto
w pthread_cond_broadcast
w pthread_cond_destroy
w pthread_cond_signal
w pthread_cond_wait
w pthread_detach
w pthread_equal
w pthread_join
w __pthread_rwlock_destroy
w __pthread_rwlock_init
w __pthread_rwlock_rdlock
w __pthread_rwlock_unlock
w __pthread_rwlock_wrlock
w __pthread_setcancelstate
U __tls_get_addr
在 centos7 上靜態編譯,執行沒有錯誤,求教 @陳碩 @rsgAh @藍色 幾位大大。代碼就是題主的代碼
編譯相關來自官方的 glibc和glibc-static
補充一下,nm a.out | grep join
centos上 gcc 4.8 5.5 7.2 輸出類似如下
0000000000405bd0 T pthread_join
0000000000405da0 T pthread_timedjoin_np0000000000405d10 T pthread_tryjoin_np00000000004022ca W _ZNKSt6thread8joinableEv000000000040f980 T _ZNSt6thread4joinEvdebian8 gcc 4.9.2上輸出如下
0000000000460af0 T pthread_join
0000000000401df4 W _ZNKSt6thread8joinableEv000000000042e3e0 T _ZNSt6thread4joinEv最高贊 藍色 大大的方案兩種系統下都可以正常工作,點個贊問題在於這幾點?
- 你的問題我沒有關注!為什麼推送?
- 我不會這個語言!為什麼推送?
- 這是什麼?為什麼都收到了推送?
答主臆想:
- 不關注被推送,可能是全體推送.
- 沒使用過這個語言標籤還推送,可能是演算法問題.
- 這種問題被推送可能題主背景略大.
終上所述:題主可能是在北京市海淀區學院路甲5號上班,並且寫了一個BUG無法解決.
又推送一遍,知乎推送傻了嗎
這個問題我們在過去也遇到過,也是用boost:: thread解決的,沒有更深入到std::thread代碼中探尋,檢測到bug的代碼中使用了boost::asio和boost庫的TLS線程局部內存boost:: thread_specific_ptr,猜測有可能是boost::thread和std::thread在一些線程局部內存的處理上存在差異,因為TLS內存在線程析構時需要自動釋放,所以使用boost thread正常後直接bug close。
推薦閱讀:
※C語言中在一個函數中定義另一個函數是否可編譯並運行?
※如何選購用於ArchLinux的筆記本?
※初學 Linux,應該看什麼書,從哪裡下手?有哪些可以自學以及交流的網站?
※同樣的代碼在windows平台和在Linux平台上運行結果不同?
※linux真的比windows更適合web開發者作為開發環境嗎?