デバッガ上で実行されているか調べる(Plan9の場合)
Plan9では,UNIXのシグナルに相当するノート(note)って仕組みがある.基本的な考えはシグナル同じだけど,シグナル番号はなくて,文字列で識別する.分散環境を考えたら,結局文字列が一番ポータブルだったということか*1.
ノートに対するハンドラは,atnotify(2)を使って設定する.第一引数でハンドラを指定し,第二引数が非0の場合はハンドラを登録,0の場合はシステムデフォルトに戻すことを指定する.そして,ハンドラでは,第二引数を調べることでどんなノートが発生したのか判定できる.例えば,DELETEでプログラムを止めた場合(SIGINTに相当)は,"Interrupt",kill(1)した場合は,"kill"という文字列が渡される.ハンドラが非0を返せば,プログラムは続行するが,0を返せば終了する.
#include <u.h> #include <libc.h> int handler(void *, char *msg) { print("note: %s\n", msg); return 0; } void main(int argc, char **argv) { atnotify(handler, 1); sleep(100 * 1000); exits(nil); }
ノートを発行するには,postnote(2)を使う.UNIXのkillのように歴史的な理由に引きずられることなく,わかりやすい名前になっている.使用例として,Binary Hacks #92「Cのプログラムの中でブレークポイントを設定する」のデバッガ上で実行されているか調べるハックを真似てみる.
#include <u.h> #include <libc.h> static int in_acid = 1; int handler(void *, char *msg) { if (strncmp(msg, "sys: breakpoint", 15) == 0) { in_acid = 0; return 1; } return 0; } void main(int argc, char **argv) { atnotify(handler, 1); postnote(PNPROC, getpid(), "sys: breakpoint"); if (in_acid) { print("being debugged\n"); } else { print("not being debugged\n"); } exits(nil); }
postnoteでは,getid(2)で自分のpidを取得して,自分宛に"sys: breakpoint"というノートを発行している.ちなみに,postnoteは,/proc/
通常実行時は,ハンドラがブレークポイントを処理するけど,デバッガ上での実行時は,デバッガに取られてしまうので,ハンドラに処理が移らない.
cpu% chkdeb not being debugged cpu% cpu% acid chkdeb chkdeb:386 plan 9 executable /sys/lib/acid/port /sys/lib/acid/386 acid: new() 7290795: system call _main SUBL $0x48,SP 7290795: breakpoint main+0x3 MOVL $handler(SB),AX acid: cont() 7290795: system call pwrite+0x7 RET acid: cont() being debugged <stdin>:4: (error) msg: pid=7290795 startstop: process exited acid: