Dev 日誌 | 一次 Segmentation Fault 和 GCC Illegal Instruction 編譯問題排查
摘要
筆者最近在重新整理和編譯 Nebula Graph 的第三方依賴,選出兩個比較有意思的問題給大家分享一下。
Flex Segmentation Fault——Segmentation fault (core dumped)
在編譯 Flex 過程中,遇到了 Segmentation fault:
make[2]: Entering directory '/home/dutor/flex-2.6.4/src'
./stage1flex -o stage1scan.c ./scan.l
make[2]: *** [Makefile:1696: stage1scan.c] Segmentation fault (core dumped)
使用 gdb 檢視 coredump:
Core was generated by `./stage1flex -o stage1scan.c ./scan.l'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 flexinit (argc=4, argv=0x7ffd25bea718) at main.c:976
976 action_array[0] = '\0';
(gdb) disas
Dump of assembler code for function flexinit:
0x0000556c1b1ae040 : push %r15
0x0000556c1b1ae042 : lea 0x140fd(%rip),%rax # 0x556c1b1c2146
...
0x0000556c1b1ae20f : callq 0x556c1b1af460 #這裡申請了buffer
...
=> 0x0000556c1b1ae24f : movb $0x0,(%rax) # 這裡向buffer[0]寫入一個位元組,地址非法,掛掉了
...
(gdb) disas allocate_array
Dump of assembler code for function allocate_array:
0x0000556c1b1af460 : sub $0x8,%rsp
0x0000556c1b1af464 : mov %rsi,%rdx
0x0000556c1b1af467 : xor %eax,%eax
0x0000556c1b1af469 : movslq %edi,%rsi
0x0000556c1b1af46c : xor %edi,%edi
0x0000556c1b1af46e : callq 0x556c1b19a100 # 呼叫庫函式申請記憶體
0x0000556c1b1af473 : test %eax,%eax # 判斷是否為 NULL
0x0000556c1b1af475 : je 0x556c1b1af47e # 跳轉至NULL錯誤處理
0x0000556c1b1af477 : cltq # 將 eax 符號擴充套件至 rax,造成截斷
0x0000556c1b1af479 : add $0x8,%rsp
0x0000556c1b1af47d : retq
...
End of assembler dump.
可以看到,問題出在了 allocate_array
函式。因為 reallocarray
返回指標,返回值應該使用 64 bit 暫存器rax
,但 allocate_array
呼叫 reallocarray
之後,檢查的卻是 32 bit 的 eax
,同時使用 cltq
指令將 eax
符號擴充套件 到 rax
。原因只有一個:allocate_array
看到的 reallocarray
的原型,與 reallocarry
的實際定義不符。翻看編譯日誌,確實找到了 implicit declaration of function 'reallocarray' 相關的警告。configure 階段新增 CFLAGS=-D_GNU_SOURCE
即可解決此問題。
注:此問題不是必現,但編譯/連結選項 -pie 和 核心引數 kernel.randomize_va_space 有助於復現。
總結:
- 隱式宣告的函式在 C 中,返回值被認為是
int
。 - 關注編譯器告警,-Wall -Wextra 要開啟,開發模式下最好開啟 -Werror。
GCC Illegal Instruction——internal compiler error: Illegal instruction
前陣子,接到使用者反饋,在編譯 Nebula Graph 過程中遭遇了編譯器非法指令的錯誤,詳見(#978)[https://github.com/vesoft-inc/nebula/issues/978]
錯誤資訊大概是這樣的:
Scanning dependencies of target base_obj_gch
[ 0%] Generating Base.h.gch
In file included from /opt/nebula/gcc/include/c++/8.2.0/chrono:40,
from /opt/nebula/gcc/include/c++/8.2.0/thread:38,
from /home/zkzy/nebula/nebula/src/common/base/Base.h:15:
/opt/nebula/gcc/include/c++/8.2.0/limits:1599:7: internal compiler error: Illegal instruction
min() _GLIBCXX_USE_NOEXCEPT { return FLT_MIN; }
^~~
0xb48c5f crash_signal
../.././gcc/toplev.c:325
Please submit a full bug report,
with preprocessed source if appropriate.
既然是 internal compiler error,想必是 g++ 本身使用了非法指令。為了定位具體的非法指令集及其所屬模組,我們需要復現這個問題。幸運的是,下面的程式碼片段就能觸發:
#include
int main()
{
return 0;
}
非法指令一定會觸發 SIGILL,又因為 g++ 只是編譯器的入口,真正幹活的是 cc1plus。我們可以使用 gdb 來執行編譯命令,抓住子程式使用非法指令的第一現場:
$ gdb --args /opt/nebula/gcc/bin/g++ test.cpp
gdb> set follow-fork-mode child
gdb> run
Starting program: /opt/nebula/gcc/bin/g++ test.cpp
[New process 31172]
process 31172 is executing new program: /opt/nebula/gcc/libexec/gcc/x86_64-pc-linux-gnu/8.2.0/cc1plus
Thread 2.1 "cc1plus" received signal SIGILL, Illegal instruction.
[Switching to process 31172]
0x00000000013aa0fb in __gmpn_mul_1 ()
gdb> disas
...
0x00000000013aa086 : mulx (%rsi),%r10,%r8
...
Bingo!mulx
屬於 BMI2 指令集,報錯機器 CPU 不支援該指令集。
仔細調查,引入該指令集的是 GCC 的依賴之一,GMP。預設情況下,GMP 會在 configure 階段探測當前機器的 CPU 具體型別,以期最大化利用 CPU 的擴充套件指令集,提升效能,但卻犧牲了二進位制的可移植性。解決方法是,在 configure
之前,使用程式碼目錄中的 configfsf.guess configfsf.sub 替換或者覆蓋預設的 config.guess 和 config.sub
總結:
- 某些依賴可能因為效能或者配置的原因,造成二進位制的不相容。
- 預設引數下,GCC 為了相容性,不會使用較新的指令集。
- 為了平衡相容性和效能,你需要做一些額外的工作,比如像 glibc 那樣在執行時選擇和繫結某個具體實現。
最後,如果你想嘗試編譯一下 Nebula 原始碼可參考以下方式:
bash> git clone https://github.com/vesoft-inc/nebula.git
bash> cd nebula && ./build_dep.sh N
有問題請在 GitHub 或者微信公眾號上留言,也可以新增 Nebula 小助手微訊號:NebulaGraphbot 為好友反饋問題~
附錄
- Nebula Graph:一個開源的分散式圖資料庫
- GitHub:https://github.com/vesoft-inc/nebula
- 知乎:zhihu.com/org/nebulagraph/posts
- 微博:weibo.com/nebulagraph
相關文章
- 記一次線上報錯日誌問題排查
- pytorch 程式碼出現 ‘segmentation fault (core dump)’ 問題PyTorchSegmentation
- 記錄一次gcc的編譯GC編譯
- 什麼是Segmentation fault(Core Dump)? + 我遇到的例項問題Segmentation
- tacmd[203] no segmentation fault(coredump)ACMSegmentation
- Segmentation fault (coredump)的解決Segmentation
- 【分散式Tensorflow(0.11.0)問題 未解決】Segmentation fault (core dumped)分散式Segmentation
- 檢視mysql日誌及日誌編碼問題MySql
- gcc編譯GC編譯
- gcc編譯引數-fPIC的一些問題GC編譯
- ARMCC和GCC編譯ARM程式碼的軟浮點和硬浮點問題GC編譯
- GCC編譯和連結過程GC編譯
- 【Go語言探險】線上奇怪日誌問題的排查Go
- 如何使錯誤日誌更加方便地排查問題
- 執行sqlplus遭遇Segmentation FaultSQLSegmentation
- 記一次oom問題排查OOM
- 記錄一次問題排查
- GCC編譯選項GC編譯
- GCC 編譯選項GC編譯
- gcc 編譯器與 clang 編譯器GC編譯
- mysql執行命令報segmentation fault 錯誤MySqlSegmentation
- gcc 和 g++ 的聯絡和區別,使用 gcc 編譯 c++GC編譯C++
- 排查錯誤日誌
- 記一次 Laravel MethodNotAllowedHttpException 問題排查LaravelHTTPException
- 一次快取效能問題排查快取
- 記一次線上FGC問題排查GC
- gcc編譯cpp檔案GC編譯
- GCC編譯器的使用GC編譯
- sqplus 的 segmentation fault錯誤及解決!Segmentation
- netca 啟動時報錯(Segmentation fault(coredump))Segmentation
- 一次ORA-00600問題的排查和分析(上)
- 一次ORA-00600問題的排查和分析(下)
- 排查log4j不輸出日誌到檔案的問題
- 記一次OOM問題排查過程OOM
- 一次容器MySQL的效能問題排查MySql
- 記一次排查CPU高的問題
- CentOS7編譯和安裝GCC7.5CentOS編譯GC
- centos5.6部署gcc4.7編譯的程式導致問題CentOSGC編譯