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編譯
- 記一次編譯GCC的經歷編譯GC
- GCC編譯遇到“a label can only be part of a statement and a declaration is not a statement”問題GC編譯
- GCC編譯和連結過程GC編譯
- gcc 和 g++ 的聯絡和區別,使用 gcc 編譯 c++GC編譯C++
- 【Go語言探險】線上奇怪日誌問題的排查Go
- 記一次oom問題排查OOM
- 記錄一次問題排查
- Linux中gcc編譯工具LinuxGC編譯
- 排查錯誤日誌
- 一次快取效能問題排查快取
- 記一次 Laravel MethodNotAllowedHttpException 問題排查LaravelHTTPException
- CentOS7編譯和安裝GCC7.5CentOS編譯GC
- Notepad++編譯和執行C語言 (GCC)編譯C語言GC
- 排查log4j不輸出日誌到檔案的問題
- 探索gcc編譯最佳化細節 編譯器最佳化gcc -o3GC編譯
- GCC編譯過程(預處理->編譯->彙編->連結)GC編譯
- k8s-nginx二進位制報Illegal instruction (core dumped)K8SNginxStruct
- 記一次OOM問題排查過程OOM
- 一次容器MySQL的效能問題排查MySql
- 記一次排查CPU高的問題
- 記一次線上FGC問題排查GC
- Dev 日誌 | 文章《快速體驗知識圖譜 OwnThink》中的技術問題dev
- libmemcached編譯問題IBM編譯
- SPI編譯問題編譯
- GCC編譯器背後的故事GC編譯
- 當import matplotlib.pyplot as ply 出現Segmentation fault (core dumped)ImportSegmentation
- 【問題記錄】—.NetCore 編譯問題NetCore編譯
- Greenplumsegment級問題的排查-gp_session_role=utility(含csvlog日誌格式解讀)Session
- 記一次Razor Pages無法編譯問題及解決編譯
- Mybatis PageHelper編譯SQL引發的一次效能問題.18286262MyBatis編譯SQL
- 多執行緒 日誌 和截圖的問題執行緒
- 從一次問題排查聊聊問什麼要懂原理
- Linux環境下, 原始碼編譯安裝詳解 (編譯CMake 3.15 和 gcc 5.3.0 為例)Linux原始碼編譯GC
- 記一次 Kafka 重啟失敗問題排查Kafka
- 一次IOS通知推送問題排查全過程iOS