AOPっぽいC言語(関数へのenter/exitをフックする)
BINARY HACKS p300
まずは写経。
taka@ubuntu:~$ more aop.c #include__attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *func_address, void *call_site) { printf("function start %p %p\n", func_address, call_site); } __attribute__((no_instrument_function)) void __cyg_profile_func_exit(void *func_address, void *call_site) { printf("function end %p %p\n", func_address, call_site); } int add(int a, int b) { return a + b; } int main() { int result = add(3, 4); printf("result = %d\n", result); } taka@ubuntu:~$ cc -o aop aop.c -finstrument-functions taka@ubuntu:~$ ./aop function start 0x8048441 0xb7ec8ea2 function start 0x8048404 0x8048484 function end 0x8048404 0x8048484 result = 7 function end 0x8048441 0xb7ec8ea2 taka@ubuntu:~$
わりとさくっと動きます。
ただ、コールされた関数のアドレスが分かってもあまり面白くはない(というか役に立たない)ので、どうにかしてアドレスから関数名が取り出せないものかと、BINARY HACKSをひっくり返してみる。
と、p254に「libbfdでシンボルの一覧を取得する」ってのがあり、これが使えそうということで試してみる。
またもや写経。
その前に、apt-get install binutils-devしてます。
taka@ubuntu:~$ more bfdtest.c #include#include #include #include void dump_symbols(const char *filename) { bfd *abfd; asymbol *store; char *p; void *minisyms; int symnum, i; size_t size; int dyn = 0; int ret; abfd = bfd_openr(filename, NULL); assert(abfd); ret = bfd_check_format(abfd, bfd_object); assert(ret); if(!(bfd_get_file_flags(abfd) & HAS_SYMS)) { assert(bfd_get_error() == bfd_error_no_error); bfd_close(abfd); return; } store = bfd_make_empty_symbol(abfd); symnum = bfd_read_minisymbols(abfd, dyn, &minisyms, &size); assert(symnum >= 0); p = (char *)minisyms; for(i = 0; i < symnum; i++) { asymbol *sym = bfd_minisymbol_to_symbol(abfd, dyn, p, store); const char *name = bfd_asymbol_name(sym); int value = bfd_asymbol_value(sym); printf("%08X %s\n", value, name); p += size; } free(minisyms); bfd_close(abfd); } int main(int argc, char *argv[]) { dump_symbols("./aop"); return 0; } taka@ubuntu:~$ cc -o bfdtest bfdtest.c -lbfd taka@ubuntu:~$ ./bfdtest ... 省略 ... 080482D8 _init 08048404 add 08048320 _start 080484B0 __libc_csu_init 080496DC __bss_start 080483C0 __cyg_profile_func_enter 08048441 main 00000000 __libc_start_main@@GLIBC_2.0 080496D0 data_start 00000000 printf@@GLIBC_2.0 08048578 _fini 080496DC _edata 08048368 __i686.get_pc_thunk.bx 080496E0 _end 0804859C _IO_stdin_used 080496D0 __data_start 00000000 _Jv_RegisterClasses 00000000 __gmon_start__ taka@ubuntu:~$
というわけで、なんとなく関数のアドレスと関数名の関連が取れている模様。
これをさっきのaopっぽいやつと合わせワザしてみる。
taka@ubuntu:~$ more aop3.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <bfd.h> __attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *func_address, void *call_site) { char out_symname[256]; dump_symbols("./aop3", func_address, out_symname); printf("function start %s\n", out_symname); } __attribute__((no_instrument_function)) void __cyg_profile_func_exit(void *func_address, void *call_site) { char out_symname[256]; dump_symbols("./aop3", func_address, out_symname); printf("function end %s\n", out_symname); } int add(int a, int b) { return a + b; } int main() { int result = add(3, 4); printf("result = %d\n", result); }
taka@ubuntu:~$ more getsymbol.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <bfd.h> void dump_symbols(const char *filename, int addr, char *out_symname) { bfd *abfd; asymbol *store; char *p; void *minisyms; int symnum, i; size_t size; int dyn = 0; int ret; abfd = bfd_openr(filename, NULL); assert(abfd); ret = bfd_check_format(abfd, bfd_object); assert(ret); if(!(bfd_get_file_flags(abfd) & HAS_SYMS)) { assert(bfd_get_error() == bfd_error_no_error); bfd_close(abfd); return; } store = bfd_make_empty_symbol(abfd); symnum = bfd_read_minisymbols(abfd, dyn, &minisyms, &size); assert(symnum >= 0); p = (char *)minisyms; for(i = 0; i < symnum; i++) { asymbol *sym = bfd_minisymbol_to_symbol(abfd, dyn, p, store); const char *name = bfd_asymbol_name(sym); int value = bfd_asymbol_value(sym); if(addr == value) { strcpy(out_symname, name); } p += size; } free(minisyms); bfd_close(abfd); }
taka@ubuntu:~$ more comp.sh cc -c -o getsymbol.o getsymbol.c cc -Wl,-soname,libgetsymbol.so -o libgetsymbol.so -shared getsymbol.o cc aop3.c -o aop3 -L. -lgetsymbol -lbfd -finstrument-functions taka@ubuntu:~$ ./comp.sh taka@ubuntu:~$ ./aop3 function start main function start add function end add result = 7 function end main taka@ubuntu:~$
なんとなく出来たっぽい。
ファイル名が固定打ち(./aop3)なので、argv[0]から取り回せばもう少しマシかも。
しかしまあ、これだけのことやるのに大変ですね。。