void __cyg_profile_func_enter (void *this_fn,
void *call_site);
void __cyg_profile_func_exit (void *this_fn,
void *call_site);
The first argument is the address of the start of the current function, which may be looked up exactly in the symbol table.#include <stdio.h> #define DUMP(func, call) \ printf("%s: func = %p, called by = %p\n", __FUNCTION__, func, call) void __attribute__((__no_instrument_function__)) __cyg_profile_func_enter(void *this_func, void *call_site) { DUMP(this_func, call_site); } void __attribute__((__no_instrument_function__)) __cyg_profile_func_exit(void *this_func, void *call_site) { DUMP(this_func, call_site); } main() { puts("Hello World!"); return 0; }編譯與執行:
$ gcc -finstrument-functions hello.c -o hello $ ./hello __cyg_profile_func_enter: func = 0x8048468, called by = 0xb7e36ebc Hello World! __cyg_profile_func_exit: func = 0x8048468, called by = 0xb7e36ebc看到 "__attribute__" 就知道一定是 GNU extension,而前述的 man page 也提到 -finstrument-functions 會在每次進入與退出函式前呼叫 "__cyg_profile_func_enter" 與 "__cyg_profile_func_exit" 這兩個 hook function。等等,「進入」與「退出」是又何解?C Programming Language 最經典之處在於,雖然沒有定義語言實做的方式,但實際上 function call 皆以 stack frame 的形式存在,去年在「深入淺出 Hello World」Part II 有提過。所以上述那一大段英文就是說,如果我們不透過 GCC 內建函式 "__builtin_return_address" 取得 caller 與 callee 相關的動態位址,那麼仍可透過 -finstrument-functions,讓 GCC 合成相關的處理指令,讓我們得以追蹤。而看到 __cyg 開頭的函式,就知道是來自 Cygnus 的貢獻,在 gcc 2.x 內部設計可瞥見不少。
$ gcc -g -finstrument-functions wrong.c -o wrong $ ./wrong 程式記憶體區段錯誤發生什麼事情呢?請出 gdb 協助:
$ gdb ./wrong
GNU gdb 6.6-debian
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) run
Starting program: /home/jserv/HelloWorld/helloworld/instrument/wrong
Program received signal SIGSEGV, Segmentation fault.
0x0804841d in __cyg_profile_func_enter (this_func=0x8048414, call_site=0x804842d) at wrong.c:7
7 {
(gdb) bt
#0 0x0804841d in __cyg_profile_func_enter (this_func=0x8048414, call_site=0x804842d) at wrong.c:7
#1 0x0804842d in __cyg_profile_func_enter (this_func=0x8048414, call_site=0x804842d) at wrong.c:7
#2 0x0804842d in __cyg_profile_func_enter (this_func=0x8048414, call_site=0x804842d) at wrong.c:7
...
#30 0x0804842d in __cyg_profile_func_enter (this_func=0x8048414, call_site=0x804842d) at wrong.c:7
#31 0x0804842d in __cyg_profile_func_enter (this_func=0x8048414, call_site=0x804842d) at wrong.c:7
#32 0x0804842d in __cyg_profile_func_enter (this_func=0x8048414, call_site=0x804842d) at wrong.c:7
---Type to continue, or q to quit---
既然 "__cyg_profile_func_enter" 是 function hook,則本身不得也被施以 "function instrument",否則就無止盡遞迴了,不過我們也可以發現一件有趣的事情:$ nm wrong | grep 8048414 08048414 T __cyg_profile_func_enter如我們所見,"__cyg_profile_func_enter" 的位址被不斷代入 __cyg_profile_func_enter function arg 中。GNU binutils 裡面有個小工具 addr2line,我們可以該工具取得虛擬位址對應的行號或符號:
$ addr2line -e wrong 0x8048414 /home/jserv/HelloWorld/helloworld/instrument/wrong.c:7就 Linux 應用程式開發來說,我們可透過這個機制作以下應用:
$ objdump -d hello | grep -B2 puts ... (省略) ... -- 8048488: e8 87 ff ff ff call 8048414 <__cyg_profile_func_enter> 804848d: c7 04 24 df 85 04 08 movl $0x80485df,(%esp) 8048494: e8 c3 fe ff ff call 804835c看來 GCC 自動生成 __cyg_profile_func_enter 的 function call 動作,並置於 puts 呼叫之前,我們繼續觀察:
$ objdump -d hello | grep -A5 puts ... (省略) ... -- 8048494: e8 c3 fe ff ff call 804835cputs 呼叫之後,GCC 也自動生成 __cyg_profile_func_exit 的 function call 動作,這樣的技巧也被用於 KFT (Kernel Function Trace,前身為 KFI [Kernel function Instrumentation]),同樣以 GCC "-finstrument-functions" 來達到對每個 Linux Kernel function enter/exit 的追蹤功能,但得考慮大量的 static inline function。8048499: bb 00 00 00 00 mov $0x0,%ebx 804849e: 8b 45 04 mov 0x4(%ebp),%eax 80484a1: 89 44 24 04 mov %eax,0x4(%esp) 80484a5: c7 04 24 68 84 04 08 movl $0x8048468,(%esp) 80484ac: e8 8d ff ff ff call 804843e <__cyg_profile_func_exit>
每次都覺得jserv大的文都像是變魔術...
很神
谢谢分享。
由 lgfang 發表於 September 27, 2008 11:05 AM