如果想在 V2EX 获得更好的推广效果,欢迎了解 PRO 会员机制:
https://www.sunp.eu.org/pro/about

如果你经常使用铜币置顶主题,持有 V2EX Solana Token 会在每日签到时获得额外铜币:
https://www.sunp.eu.org/solana
NebulaGraph
V2EX  ›  推广

Dev 日志 | 一次 Segmentation Fault 和 GCC Illegal Instruction 编译问题排查

  •  
  •   NebulaGraph · Nov 20, 2019 · 1366 views
    This topic created in 2386 days ago, the information mentioned may be changed or developed.

    摘要

    笔者最近在重新整理和编译 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 <+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 即可解决此问题。

    注:此问题不是必现,但编译 /链接选项 -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 <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

    总结:

    • 某些依赖可能因为性能或者配置的原因,造成二进制的不兼容。
    • 缺省参数下,GCC 为了兼容性,不会使用较新的指令集。
    • 为了平衡兼容性和性能,你需要做一些额外的工作,比如像 glibc 那样在运行时选择和绑定某个具体实现。

    最后,如果你想尝试编译一下 Nebula 源代码可参考以下方式:

    bash> git clone https://github.com/vesoft-inc/nebula.git
    bash> cd nebula && ./build_dep.sh N
    

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

    附录

    No Comments Yet
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3216 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 13:29 · PVG 21:29 · LAX 06:29 · JFK 09:29
    ♥ Do have faith in what you're doing.