Dev 日誌 | 一次 Segmentation Fault 和 GCC Illegal Instruction 編譯問題排查

nebulagraph發表於2019-11-20
https://i.iter01.com/images/2ce8e928726c50d33857acc2026aab76515a35adf1084fa1345d9a8553d2f31f.jpg

摘要

筆者最近在重新整理和編譯 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 為好友反饋問題~

附錄


https://i.iter01.com/images/760289c9262e00dc9711a6f04ff0660bd2ef6328a6adc941b07b32d31fb557bb.jpg

相關文章