gcc 参数_Dev 日志 | Segmentation Fault 和 GCC 编译问题排查

2a8815a42678cad2177bf94f963c3057.png

摘要

笔者最近在重新整理和编译 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.lmake[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:976976 action_array[0] = '0';(gdb) disasDump 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_arrayDump 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)[github.com/vesoft-inc/…]

错误信息大概是这样的:

Scanning dependencies of target base_obj_gch[ 0%] Generating Base.h.gchIn 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 instructionmin() _GLIBCXX_USE_NOEXCEPT { return FLT_MIN; }^~~0xb48c5f crash_signal../.././gcc/toplev.c:325Please 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.cppgdb> set follow-fork-mode childgdb> runStarting 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/cc1plusThread 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.gitbash> cd nebula && ./build_dep.sh N复制代码

有问题请在 GitHub 或者微信公众号上留言。

附录

  • Nebula Graph:一个开源的分布式图数据库
bf60a0c52b281453e662c0fa75736ecc.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值