目次ログを取っている環境が i386だったり x86_64だったりするので、stackframeの addressの出方もまちまちだったりするが、 その辺は爽やかにスルーする方向でお願いしますw
動機Solaris10上のコマンドDTraceにある機能 ustack() みたいなことを Linux上でもやってみたいということで、なんとかならんか調べてみた。 具体的にはこんな感じ。lynx@root[119] dtrace -n 'syscall::read:entry { ustack(); exit(0)}' -c 'ls /' dtrace: description 'syscall::read:entry ' matched 1 probe TT_DB devices lib opt tmp archives etc lost+found platform usr bin export mnt proc var boot home mnt2 sbin vol dev kernel net system zfs dtrace: pid 18116 has exited CPU ID FUNCTION:NAME 3 45642 read:entry libc.so.1`_read+0xa libproc.so.1`Preadauxvec+0xd7 libproc.so.1`Pupdate_maps+0x47 libproc.so.1`Pupdate_syms+0x1f libdtrace.so.1`dt_proc_rdevent+0xe4 libdtrace.so.1`dt_proc_bpmatch+0xc6 libdtrace.so.1`dt_proc_control+0x4a9 libc.so.1`_thr_setup+0x5b libc.so.1`_lwp_start
First Stepまずは呼ばれたら、stack frameを順に追って、自分の手前までの分をdumpする Library関数 int mydump() を作るところから始めてみる。glibc 2.1以降で実装されているbacktraceがそのまま使えるか試してみた。まず素で使ってみる $ cat test.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <execinfo.h> void foo() { void *trace[BUFSIZ]; size_t size = backtrace(trace, sizeof(trace) / sizeof(trace[0])); char **strings = backtrace_symbols(trace, size); for ( int i = 0; i < size; i++ ) printf("%s\n", strings[i]); free(strings); } int main() { foo(); return 0; } $ ./test ./test(foo+0x1c) [0x400834] ./test(main+0xe) [0x4008c5] /lib64/tls/libc.so.6(__libc_start_main+0xdb) [0x34be11c4bb] ./test [0x40078a]backtrace()は表示用のバッファサイズ(trace)とnestの深さ(n)の分だけ表示できる
2nd Step毎回コードに直書きするのはどうかと思うので、foo()の部分だけ libraryに入れて、 コードからはその関数を呼び出す形に変えてみた。ついでにもう1枚、libを挟んでみた。まずはsoに入れる部分 $ cat tracehere.c #include <stdio.h> #include <stdlib.h> #include <execinfo.h> int tracehere() { void *trace[BUFSIZ]; size_t size; char **strings; size_t i; size = backtrace(trace, sizeof(trace)); strings = backtrace_symbols(trace, size); printf("Obtained %zd stack frames.\n", size); for ( i = 0; i < size; i++ ) { printf("%s\n", strings[i]); } free(strings);// the array 'strings' is malloced by backtrace_symbols(). return 0; }次に上記のsoに入れた関数を呼び出す方と実行結果。 $ cat mock.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int tracehere(); void foo() { tracehere(); } $ cat backtrace.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int foo(); int main(int argc, char *argv[]) { foo(); return (0); } $ make run export LD_LIBRARY_PATH=.; ./backtrace Obtained 5 stack frames. ./libmito.so(tracehere+0x1c) [0x2a956827d8] ./libmock.so(foo+0xe) [0x2a955576ba] ./backtrace(main+0x19) [0x400721] /lib64/tls/libc.so.6(__libc_start_main+0xdb) [0x34be11c4bb] ./backtrace [0x40067a] シンボル名は特別なリンカ・オプションを使用しないと利用できない場合がある。 GNU リンカを使用するシステムでは、 -rdynamic リンカ・オプションを使う必要がある。 "static" な関数のシンボル名は公開されず、バックトレースでは利用できない点に注意すること。と書いてあるの
3rd Stepbacktrace()は glibc(2.1以上)に依存するので、他OSへの portingも考えると http://sourceware.org/binutils/docs/bfd/index.html?とかを使って頑張ってみる。とりあえず objdump --syms optionでSTDOUTに出力してるのは、関数ポインタabfd->xvec->_bfd_print_symbolのさしてる先で、elf 32bitバイナリの場合、elf.cにある bfd_elf_print_symbol()でやってる。TBD
4th Stepここまでは tracehere()をチェック箇所に埋めこんで rebuildする必要があったが、live debugしたい対象を rebuildするのはむずかしい。なので、以下のような感じで動くようにしてみる。
作り方はこんな感じで想定中。
TBD
参考文献
参考文献からのメモ
$ catchsegv --help Usage: catchsegv PROGRAM ARGS... --help print this help, then exit --version print version number, then exit For bug reporting instructions, please see: <http://www.gnu.org/software/libc/bugs.html>. ということで、試してみた。テストコードと実行結果はこんな感じ。 $ cat segvtest.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int foo() { char *a = NULL; printf("%s\n", a); return 0; } int main(int argc, char *argv[]) { foo(); return (0); } $ ./segvtest Segmentation fault /lib/libSegFault.soをLD_PRELOADに刺して実行した結果はこんな感じ。wrapper scriptの catchsegvから実行した結果も同じだった。(そりゃそうだ。) $ setenv LD_PRELOAD /lib/libSegFault.so $ ./segvtest *** Segmentation fault Register dump: EAX: 00000000 EBX: 00254ff4 ECX: 00000000 EDX: 00000001 ESI: 00377ca0 EDI: 00000000 EBP: bfb9fbe8 ESP: bfb9fbc8 EIP: 001700d3 EFLAGS: 00210246 CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b Trap: 0000000e Error: 00000004 OldMask: 00000000 ESP/signal: bfb9fbc8 CR2: 00000000 Backtrace: /lib/i686/nosegneg/libc.so.6(strlen+0x33)[0x1700d3] /lib/i686/nosegneg/libc.so.6(_IO_puts+0x25)[0x15a405] ./segvtest(foo+0x18)[0x804855c] ./segvtest(main+0x16)[0x8048579] /lib/i686/nosegneg/libc.so.6(__libc_start_main+0xe0)[0x116f70] ./segvtest[0x8048491] Memory map: 00101000-00253000 r-xp 00000000 fd:00 4124746 /lib/i686/nosegneg/libc-2.6.so 00253000-00255000 r--p 00151000 fd:00 4124746 /lib/i686/nosegneg/libc-2.6.so 00255000-00256000 rw-p 00153000 fd:00 4124746 /lib/i686/nosegneg/libc-2.6.so 00256000-00259000 rw-p 00256000 00:00 0 00348000-00353000 r-xp 00000000 fd:00 4124787 /lib/libgcc_s-4.1.2-20070503.so.1 00353000-00354000 rw-p 0000a000 fd:00 4124787 /lib/libgcc_s-4.1.2-20070503.so.1 0035c000-00377000 r-xp 00000000 fd:00 4124744 /lib/ld-2.6.so 00377000-00378000 r--p 0001a000 fd:00 4124744 /lib/ld-2.6.so 00378000-00379000 rw-p 0001b000 fd:00 4124744 /lib/ld-2.6.so 0077c000-0077d000 r-xp 0077c000 00:00 0 [vdso] 00918000-0091b000 r-xp 00000000 fd:00 4124753 /lib/libSegFault.so 0091b000-0091c000 r--p 00002000 fd:00 4124753 /lib/libSegFault.so 0091c000-0091d000 rw-p 00003000 fd:00 4124753 /lib/libSegFault.so 08048000-08049000 r-xp 00000000 fd:00 9466779 /home/mage/sym-works/backtrace/tests/segvtest/segvtest 08049000-0804a000 rw-p 00000000 fd:00 9466779 /home/mage/sym-works/backtrace/tests/segvtest/segvtest 088df000-08900000 rw-p 088df000 00:00 0 b7f4f000-b7f50000 rw-p b7f4f000 00:00 0 b7f6b000-b7f6c000 rw-p b7f6b000 00:00 0 bfb8c000-bfba1000 rw-p bfb8c000 00:00 0 [stack] Segmentation fault
残課題
|