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 で得られるアドレスで、複数並べることができる。