代碼使用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 即可。

#include &

void foo()
{
printf("hello
");
}

int main()
{
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 &
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

無腦猜測std::thread::join調用了pthread_join(就是那個callq 0x0),然而pthread_join是個weak symbol(0x0)。所以剩下的工作就是把它變成一個strong symbol就好了.


在 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_np

0000000000405d10 T pthread_tryjoin_np

00000000004022ca W _ZNKSt6thread8joinableEv

000000000040f980 T _ZNSt6thread4joinEv

debian8 gcc 4.9.2上輸出如下

0000000000460af0 T pthread_join

0000000000401df4 W _ZNKSt6thread8joinableEv

000000000042e3e0 T _ZNSt6thread4joinEv

最高贊 藍色 大大的方案兩種系統下都可以正常工作,點個贊


問題在於這幾點?

  1. 你的問題我沒有關注!為什麼推送?
  2. 我不會這個語言!為什麼推送?
  3. 這是什麼?為什麼都收到了推送?

答主臆想:

  • 不關注被推送,可能是全體推送.
  • 沒使用過這個語言標籤還推送,可能是演算法問題.
  • 這種問題被推送可能題主背景略大.

終上所述:題主可能是在北京市海淀區學院路甲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開發者作為開發環境嗎?

TAG:Linux | CC | GCC | 編譯器 |