GDBまとめ ①コアダンプを採取する

参考書籍 : Debug Hacks


①プロセスのコアダンプを採取する
コアダンプの採取で、問題が発生したときの状態を保存できる。
コアダンプを取ることによって、手元に再現環境がなくてもデバッグできる。
コアダンプを確認するには、ulimitを使う。

$ ulimit -c
0

0となっている場合はコアダンプが無効なので、ulimitで有効化する

$ ulimit -c unlimited

このコマンドで、コアファイルのサイズを無制限にし、問題発生時のプロセスのメモリをすべてダンプできる。
コアダンプを採る設定でエラーを起こすと、coreというコアファイルがカレントディレクトリに作成される

# file core
core: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from './a.out',
real uid: 0, effective uid: 0, real gid: 0, effective gid: 0, execfn: './a.out', platform: 'x86_64'

 


②コアファイルをGDBデバッグする
作成されたコアファイルを使い、GDBデバッグするには次のようにする。

$ gdb ./a.out -c core
GNU gdb (Debian 9.2-1) 9.2
...
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000055634d651135 in main () at err.c:6
6               *p = 1;
(gdb) 

err.cの6行目でSIGSEGV(セグメンテーション違反)を起こしているとわかる。
セグメンテーション違反はアクセスが許可されていないアドレスへのアクセスなので、その部分を修正する。
gdbのlistコマンドで周辺のソースを確認できる。

(gdb) list
1       #include <stdio.h>
2
3       int main()
4       {
5               int *p = NULL;
6               *p = 1;
7               return 0;
8       }

周辺のソースから、NULLポインタ参照が行われているとわかる。



③専用ディレクトリにコアダンプを生成する。
デフォルトではコアダンプがカレントディレクトリに生成されてしまうので、ダンプ先を設定する。
sysctl変数のkernel.core_patternに、ダンプ先をフルパスで指定することで変更できる。
/etc/sysctl.confを編集し、ダンプ先を指定する。

$ cat /etc/sysctl.conf | grep core
kernel.core_pattern = /root/dump/%t-%e-%p-%c.core
kernel.core_uses_pid = 0
$ sysctl -p

先程のa.outを実行すると、/root/dumpの下にコアファイルが作成される。

$ ls /root/dump/
1595254562-a.out-32941-18446744073709551615.core

このファイル名は、"時刻-プロセス名-PID-コアダンプ最大サイズ.core"になっている。



ユーザーモードヘルパーを使って自動でコアダンプを圧縮する
コアダンプを自動で圧縮するためには、/proc/sys/kernel/core_patternを書き換える

$ echo "|/usr/local/sbin/core_helper.sh %t %e %p %c" > /proc/sys/kernel/core_pattern 
$ cat /proc/sys/kernel/core_pattern
|/usr/local/sbin/core_helper.sh %t %e %p %c
$ sysctl -p

core_helper.shの中身は簡単で、

$ cat /usr/local/sbin/core_helper.sh
#!/bin/sh

exec gzip - > /root/dump/$1-$2-$3-$4.core.gz
$ chmod +x /usr/local/sbin/core_helper.sh

単純にコアファイルを圧縮しているだけ。



⑤システム全体でコアダンプを有効化する(Kali Linuxの場合)
Kali Linuxではログイン時に/etc/profile/profile.d以下のスクリプトが全て実行される。
profile.d以下に、core.shを作り、ログイン時にコアダンプを有効化するようにする。

$ cat /etc/profile.d/core.sh 
#!/bin/sh

ulimit -S -c unlimited > /dev/null 2>&1
echo "|/usr/local/sbin/core_helper.sh %t %e %p %c" > /proc/sys/kernel/core_pattern
sysctl -p

/proc/sys/kernel/core_patternはcoreに初期化されてしまうので、上書きするようにする。
次に、/etc/sysctl.confに以下の設定を追加する。

fs.suid_dumpable=1

この設定で、SUIDされたプログラムもコアダンプをするようになる。



⑥コアダンプマスキングを利用して共有メモリをスキップする
大きな共有メモリを利用するアプリケーションをコアダンプしていると、ディスクの圧迫やシステムの負荷が高くなる。
プロセスごとに、コアダンプさせるメモリセグメントを選択する機能がカーネル2.6.23以降から実装されている。
設定方法は、/proc//coredump_filterを通じて行う。
coredump_filterは7bitのビットマスクでメモリタイプを表しており
・ビット0 : 匿名プライベートメモリ
・ビット1 : 匿名共有メモリ
・ビット2 : ファイルを使用するプライベートメモリ
・ビット3 : ファイルを使用する共有メモリ
・ビット4 : ELFファイルマッピング(カーネル2.6.24以降から)
・ビット5 : プライベートなヒュージページ
・ビット6 : 共有されたヒュージページ
デフォルトは00000033(b00110011)になっている。