libSegFault.so#

libSegFault.so はプロセスが Segmentation Fault で終わった際にスタックトレースを表示するライブラリ。 GDB に比べるとスタックトレースで関数名を表示するのに追加の作業が必要になって手間がかかるが、シグナルハンドラを利用して動作するという仕組みで、 Segmentation Fault しない限りは何も処理を行わないため、 GDB 経由では発生しなくなってしまうバグも再現してスタックトレースを取ることができるという利点がある。

Ubuntu 上でのインストール#

Ubuntu では apt のパッケージ gcc の依存パッケージの一部としてインストールされる。

Note

Ubuntu 20.04 では、 apt の gcc パッケージの依存パッケージに gcc-9 があり、 gcc-9 の依存パッケージに libc6 があり、 libc6 内に /lib/x86_64-linux-gnu/libSegFault.so がある。

Ubuntu 22.04 では apt の glibc-tools パッケージ内に /usr/lib/x86_64-linux-gnu/libSegFault.so がある。

実験#

次のような明らかに Segmentation Fault になる C++ のプログラムを用意する。

void test(int* a) {
    *a = 0;
}

int main() {
    test(nullptr);
}

これをデバッグ用にビルドする。

$ g++ -g test.cpp -o test

これを普通に実行すると、次のようになる。

$ ./test
Segmentation fault (core dumped)

libSegFault.so をロードするようにすると、次のようになる。

$ LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libSegFault.so ./test
*** signal 11
Register dump:

 RAX: 0000000000000000   RBX: 0000000000000000   RCX: 000056489f19ddf8
 RDX: 00007ffdd6090f28   RSI: 00007ffdd6090f18   RDI: 0000000000000000
 RBP: 00007ffdd6090df0   R8 : 00007f3ed341af10   R9 : 00007f3ed3609040
 R10: 00007f3ed3603908   R11: 00007f3ed361e6c0   R12: 00007ffdd6090f18
 R13: 000056489f19b142   R14: 000056489f19ddf8   R15: 00007f3ed363d040
 RSP: 00007ffdd6090df0

 RIP: 000056489f19b139   EFLAGS: 00010246

 CS: 0033   FS: 0000   GS: 0000

 Trap: 0000000e   Error: 00000006   OldMask: 00000000   CR2: 00000000

 FPUCW: 0000037f   FPUSW: 00000000   TAG: 00000000
 RIP: 00000000   RDP: 00000000

 ST(0) 0000 0000000000000000   ST(1) 0000 0000000000000000
 ST(2) 0000 0000000000000000   ST(3) 0000 0000000000000000
 ST(4) 0000 0000000000000000   ST(5) 0000 0000000000000000
 ST(6) 0000 0000000000000000   ST(7) 0000 0000000000000000
 mxcsr: 1f80
 XMM0:  00000000000000000000000000000000 XMM1:  00000000000000000000000000000000
 XMM2:  00000000000000000000000000000000 XMM3:  00000000000000000000000000000000
 XMM4:  00000000000000000000000000000000 XMM5:  00000000000000000000000000000000
 XMM6:  00000000000000000000000000000000 XMM7:  00000000000000000000000000000000
 XMM8:  00000000000000000000000000000000 XMM9:  00000000000000000000000000000000
 XMM10: 00000000000000000000000000000000 XMM11: 00000000000000000000000000000000
 XMM12: 00000000000000000000000000000000 XMM13: 00000000000000000000000000000000
 XMM14: 00000000000000000000000000000000 XMM15: 00000000000000000000000000000000

Backtrace:
./test(+0x1139)[0x56489f19b139]
./test(+0x1154)[0x56489f19b154]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f3ed3229d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f3ed3229e40]
./test(+0x1065)[0x56489f19b065]

Memory map:

56489f19a000-56489f19b000 r--p 00000000 08:02 7080907                    /home/user/test
56489f19b000-56489f19c000 r-xp 00001000 08:02 7080907                    /home/user/test
56489f19c000-56489f19d000 r--p 00002000 08:02 7080907                    /home/user/test
56489f19d000-56489f19e000 r--p 00002000 08:02 7080907                    /home/user/test
56489f19e000-56489f19f000 rw-p 00003000 08:02 7080907                    /home/user/test
5648a0e4d000-5648a0e6e000 rw-p 00000000 00:00 0                          [heap]
7f3ed3200000-7f3ed3228000 r--p 00000000 08:02 3541339                    /usr/lib/x86_64-linux-gnu/libc.so.6
7f3ed3228000-7f3ed33bd000 r-xp 00028000 08:02 3541339                    /usr/lib/x86_64-linux-gnu/libc.so.6
7f3ed33bd000-7f3ed3415000 r--p 001bd000 08:02 3541339                    /usr/lib/x86_64-linux-gnu/libc.so.6
7f3ed3415000-7f3ed3419000 r--p 00214000 08:02 3541339                    /usr/lib/x86_64-linux-gnu/libc.so.6
7f3ed3419000-7f3ed341b000 rw-p 00218000 08:02 3541339                    /usr/lib/x86_64-linux-gnu/libc.so.6
7f3ed341b000-7f3ed3428000 rw-p 00000000 00:00 0
7f3ed35c7000-7f3ed35ca000 r--p 00000000 08:02 3541592                    /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
7f3ed35ca000-7f3ed35e1000 r-xp 00003000 08:02 3541592                    /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
7f3ed35e1000-7f3ed35e5000 r--p 0001a000 08:02 3541592                    /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
7f3ed35e5000-7f3ed35e6000 r--p 0001d000 08:02 3541592                    /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
7f3ed35e6000-7f3ed35e7000 rw-p 0001e000 08:02 3541592                    /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
7f3ed35e7000-7f3ed35ea000 rw-p 00000000 00:00 0
7f3ed35fa000-7f3ed35fb000 r--p 00000000 08:02 3540104                    /usr/lib/x86_64-linux-gnu/libSegFault.so
7f3ed35fb000-7f3ed35fe000 r-xp 00001000 08:02 3540104                    /usr/lib/x86_64-linux-gnu/libSegFault.so
7f3ed35fe000-7f3ed35ff000 r--p 00004000 08:02 3540104                    /usr/lib/x86_64-linux-gnu/libSegFault.so
7f3ed35ff000-7f3ed3600000 r--p 00005000 08:02 3540104                    /usr/lib/x86_64-linux-gnu/libSegFault.so
7f3ed3600000-7f3ed3601000 rw-p 00006000 08:02 3540104                    /usr/lib/x86_64-linux-gnu/libSegFault.so
7f3ed3601000-7f3ed3603000 rw-p 00000000 00:00 0
7f3ed3603000-7f3ed3605000 r--p 00000000 08:02 3541333                    /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f3ed3605000-7f3ed362f000 r-xp 00002000 08:02 3541333                    /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f3ed362f000-7f3ed363a000 r--p 0002c000 08:02 3541333                    /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f3ed363b000-7f3ed363d000 r--p 00037000 08:02 3541333                    /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f3ed363d000-7f3ed363f000 rw-p 00039000 08:02 3541333                    /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ffdd6073000-7ffdd6094000 rw-p 00000000 00:00 0                          [stack]
7ffdd6114000-7ffdd6118000 r--p 00000000 00:00 0                          [vvar]
7ffdd6118000-7ffdd611a000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]
Segmentation fault (core dumped)

Backtrace 内の ./test(+0x1139)[0x56489f19b139] のような表記の内括弧内の 0x1139 がバイナリ内の相対的なアドレスを示す。次の節で相対的なアドレスから関数名を取り出す方法を説明する。

関数名の取得#

関数名は addr2line というコマンドで取得できる。

Note

Ubuntu 20.04, 22.04 で addr2line は apt の binutils パッケージにある。

例えば、前述の test.cpp のスタックトレースにある 0x1139, 0x1154 は以下のようにして関数名とファイルの情報に変換できる。

$ addr2line -e test -f -C 0x1139 0x1154
test(int*)
/home/user/test.cpp:2
main
/home/user/test.cpp:7
  • -e のあとにバイナリのパスを入力する。

  • -f, -C は関数名を表示するためのオプション。

  • その他の引数が libSegFault.so で得られるアドレスで、複数並べることができる。