笔者最近在从新整理和编译 Nebula Graph 的第三方依赖,选出两个比较有意思的问题给你们分享一下。linux
在编译 Flex 过程当中,遇到了 Segmentation fault:c++
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:git
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 <+0>: push %r15 0x0000556c1b1ae042 <+2>: lea 0x140fd(%rip),%rax # 0x556c1b1c2146 ... 0x0000556c1b1ae20f <+463>: callq 0x556c1b1af460 <allocate_array> #这里申请了buffer ... => 0x0000556c1b1ae24f <+527>: movb $0x0,(%rax) # 这里向buffer[0]写入一个字节,地址非法,挂掉了 ... (gdb) disas allocate_array Dump of assembler code for function allocate_array: 0x0000556c1b1af460 <+0>: sub $0x8,%rsp 0x0000556c1b1af464 <+4>: mov %rsi,%rdx 0x0000556c1b1af467 <+7>: xor %eax,%eax 0x0000556c1b1af469 <+9>: movslq %edi,%rsi 0x0000556c1b1af46c <+12>: xor %edi,%edi 0x0000556c1b1af46e <+14>: callq 0x556c1b19a100 <reallocarray@plt> # 调用库函数申请内存 0x0000556c1b1af473 <+19>: test %eax,%eax # 判断是否为 NULL 0x0000556c1b1af475 <+21>: je 0x556c1b1af47e <allocate_array+30># 跳转至NULL错误处理 0x0000556c1b1af477 <+23>: cltq # 将 eax 符号扩展至 rax,形成截断 0x0000556c1b1af479 <+25>: add $0x8,%rsp 0x0000556c1b1af47d <+29>: 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
便可解决此问题。github
注:此问题不是必现,但编译/连接选项 -pie 和 内核参数 kernel.randomize_va_space 有助于复现。数据库
总结:bash
int
。前阵子,接到用户反馈,在编译 Nebula Graph 过程当中遭遇了编译器非法指令的错误,详见(#978)[github.com/vesoft-inc/…]微信
错误信息大概是这样的:app
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++ 自己使用了非法指令。为了定位具体的非法指令集及其所属模块,咱们须要复现这个问题。幸运的是,下面的代码片断就能触发:dom
#include <thread>
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 <+38>: mulx (%rsi),%r10,%r8
...
复制代码
Bingo!mulx
属于 BMI2 指令集,报错机器 CPU 不支持该指令集。
仔细调查,引入该指令集的是 GCC 的依赖之一,GMP。默认状况下,GMP 会在 configure 阶段探测当前机器的 CPU 具体类型,以期最大化利用 CPU 的扩展指令集,提高性能,但却牺牲了二进制的可移植性。解决方法是,在 configure
以前,使用代码目录中的 configfsf.guess configfsf.sub 替换或者覆盖默认的 config.guess 和 config.sub
总结:
最后,若是你想尝试编译一下 Nebula 源代码可参考如下方式:
bash> git clone https://github.com/vesoft-inc/nebula.git
bash> cd nebula && ./build_dep.sh N
复制代码
有问题请在 GitHub 或者微信公众号上留言。