使用gcc的-finstrument-functions选项进行函数跟踪【转】
转自:http://blog.csdn.net/jasonchen_gbd/article/details/44044899
版权声明:本文为博主原创文章,转载请附上原博链接。
编译时如果为gcc加上“-finstrument-functions”选项,那在每个函数的入口和出口处会各增加一个额外的hook函数的调用,增加的这两个函数分别为:
- void __cyg_profile_func_enter (void *this_fn, void *call_site);
- void __cyg_profile_func_exit (void *this_fn, void *call_site);
其中第一个参数为当前函数的起始地址,第二个参数为返回地址,即caller函数中的地址。
这是什么意思呢?例如我们写了一个函数func_test(),定义如下:
- static void func_test(v)
- {
- /* your code... */
- }
那通过-finstrument-functions选项编译后,这个函数的定义就变成了:
- static void func_test(v)
- {
- __cyg_profile_func_enter(this_fn, call_site);
- /* your code... */
- __cyg_profile_func_exit(this_fn, call_site);
- }
我们可以按照自己的需要去实现这两个hook函数,这样我们就可以利用this_fn和call_site这两个参数大做文章。
例如下面这段代码:
- instrfunc.c:
- #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);
- }
- int do_multi(int a, int b)
- {
- return a * b;
- }
- int do_calc(int a, int b)
- {
- return do_multi(a, b);
- }
- int main()
- {
- int a = 4, b = 5;
- printf("result: %d\n", do_calc(a, b));
- return 0;
- }
这段代码中实现了两个hook函数,即打印出所在函数的函数地址以及返回地址。
编译代码:
- [zhenfg@ubuntu]code:$ gcc -finstrument-functions instrfunc.c -o instrfunc
- [zhenfg@ubuntu]code:$ ./instrfunc
- __cyg_profile_func_enter: func = 0x8048521, called by = 0xb75554e3
- __cyg_profile_func_enter: func = 0x80484d8, called by = 0x8048562
- __cyg_profile_func_enter: func = 0x804849a, called by = 0x8048504
- __cyg_profile_func_exit: func = 0x804849a, called by = 0x8048504
- __cyg_profile_func_exit: func = 0x80484d8, called by = 0x8048562
- result: 20
- __cyg_profile_func_exit: func = 0x8048521, called by = 0xb75554e3
通过反汇编的代码(objdump -D instrfunc)可以看到,这些地址和函数的对应关系为:
- __cyg_profile_func_enter: func = 0x8048521(main), called by = 0xb75554e3
- __cyg_profile_func_enter: func = 0x80484d8(do_calc), called by = 0x8048562(main)
- __cyg_profile_func_enter: func = 0x804849a(do_multi), called by = 0x8048504(do_calc)
- __cyg_profile_func_exit: func = 0x804849a(do_multi), called by = 0x8048504(do_calc)
- __cyg_profile_func_exit: func = 0x80484d8(do_calc), called by = 0x8048562(main)
- result: 20
- __cyg_profile_func_exit: func = 0x8048521(main), called by = 0xb75554e3
实际上这就给出了函数的调用关系。
如果不想跟踪某个函数,可以给该函数指定“no_instrument_function”属性。需要注意的是,__cyg_profile_func_enter()和__cyg_profile_func_exit()这两个hook函数是一定要加上“no_instrument_function”属性的,不然,自己跟踪自己就会无限循环导致程序崩溃,当然,也不能在这两个hook函数中调用其他需要被跟踪的函数。
得到一系列的地址看起来不太直观,我们更希望看到函数名,幸运的是,addr2line工具为我们提供了这种可能。我们先看一下addr2line的使用方法:
- [zhenfg@ubuntu]code:$ addr2line --help
- Usage: addr2line [option(s)] [addr(s)]
- Convert addresses into line number/file name pairs.
- If no addresses are specified on the command line, they will be read from stdin
- The options are:
- @<file> Read options from <file>
- -a --addresses Show addresses
- -b --target=<bfdname> Set the binary file format
- -e --exe=<executable> Set the input file name (default is a.out)
- -i --inlines Unwind inlined functions
- -j --section=<name> Read section-relative offsets instead of addresses
- -p --pretty-print Make the output easier to read for humans
- -s --basenames Strip directory names
- -f --functions Show function names
- -C --demangle[=style] Demangle function names
- -h --help Display this information
- -v --version Display the program's version
首先要注意,使用addr2line工具时,需要用gcc的“-g”选项编译程序增加调试信息。
同样是上面的程序,我们加上-g选项再编译一次:
- [zhenfg@ubuntu]code:$ gcc -g -finstrument-functions instrfunc.c -o instrfunc
- [zhenfg@ubuntu]code:$ ./instrfunc
- __cyg_profile_func_enter: func = 0x8048521, called by = 0xb757d4e3
- __cyg_profile_func_enter: func = 0x80484d8, called by = 0x8048562
- __cyg_profile_func_enter: func = 0x804849a, called by = 0x8048504
- __cyg_profile_func_exit: func = 0x804849a, called by = 0x8048504
- __cyg_profile_func_exit: func = 0x80484d8, called by = 0x8048562
- result: 20
- __cyg_profile_func_exit: func = 0x8048521, called by = 0xb757d4e3
使用addr2line尝试查找0x8048504地址所在的函数:
- [zhenfg@ubuntu]code:$ addr2line -e instrfunc -a 0x8048504 -fp -s
- 0x08048504: do_calc at instrfunc.c:25
这样一来,就可以通过gcc的“-finstrument-functions”选项结合addr2line工具,方便的对一个程序中的函数进行跟踪。并且既然我们可以自己实现hook函数,那不仅仅可以用来跟踪函数调用关系,你可以在hook函数中添加自己想做的事情,例如添加一些统计信息。
另外,我们知道__builtin_return_address(level)宏可以获得不同层级的函数返回地址,但是在某些体系架构(如mips)中,__builtin_return_address(level)只能获得当前函数的直接调用者的地址,即level只能是0,那这时,就可使用上述方法来跟踪函数调用关系(mips中竟然能用,确实有些小吃惊)。
接下来可以看一下gcc是如何将hook函数嵌入各个函数中的,以反汇编代码中的do_multi()函数为例(这是mips的汇编代码),在mips中,ra寄存器用来存储返回地址,a0-a3用来做函数参数。
- 004006c8 <do_multi>:
- 4006c8: 27bdffd8 addiu sp,sp,-40
- 4006cc: afbf0024 sw ra,36(sp) ;;存储ra寄存器(返回地址)的值
- 4006d0: afbe0020 sw s8,32(sp)
- 4006d4: afb1001c sw s1,28(sp)
- 4006d8: afb00018 sw s0,24(sp)
- 4006dc: 03a0f021 move s8,sp
- 4006e0: 03e08021 move s0,ra ;;s0 = ra
- 4006e4: afc40028 sw a0,40(s8)
- 4006e8: afc5002c sw a1,44(s8)
- 4006ec: 02001021 move v0,s0 ;;v0 = s0
- 4006f0: 3c030040 lui v1,0x40
- 4006f4: 246406c8 addiu a0,v1,1736 ;;将本函数的地址赋值给a0寄存器
- 4006f8: 00402821 move a1,v0 ;;将返回地址ra的值赋值给a1寄存器
- 4006fc: 0c100188 jal 400620 <__cyg_profile_func_enter> ;;调用hook函数
- 400700: 00000000 nop
- 400704: 8fc30028 lw v1,40(s8)
- 400708: 8fc2002c lw v0,44(s8)
- 40070c: 00000000 nop
- 400710: 00620018 mult v1,v0
- 400714: 00008812 mflo s1
- 400718: 02001021 move v0,s0
- 40071c: 3c030040 lui v1,0x40
- 400720: 246406c8 addiu a0,v1,1736 ;;将本函数的地址赋值给a0寄存器
- 400724: 00402821 move a1,v0 ;;将返回地址ra的值赋值给a1寄存器
- 400728: 0c10019d jal 400674 <__cyg_profile_func_exit> ;;调用hook函数
- 40072c: 00000000 nop
- 400730: 02201021 move v0,s1
- 400734: 03c0e821 move sp,s8
- 400738: 8fbf0024 lw ra,36(sp) ;;恢复ra寄存器(返回地址)的值
- 40073c: 8fbe0020 lw s8,32(sp)
- 400740: 8fb1001c lw s1,28(sp)
- 400744: 8fb00018 lw s0,24(sp)
- 400748: 27bd0028 addiu sp,sp,40
- 40074c: 03e00008 jr ra
- 400750: 00000000 nop
上述反汇编的代码中,使用“-finstrument-functions”选项编译程序所增加的指令都已注释出来,实现没什么复杂的,在函数中获得自己的地址和上一级caller的地址并不是什么难事,然后将这两个地址传给__cyg_profile_func_enter和__cyg_profile_func_exit就好了。
使用gcc的-finstrument-functions选项进行函数跟踪【转】的更多相关文章
- GCC编译命令常用选项
GCC是GUN Compiler Collection的简称,除编译程序外,还包含其他相关工具.GCC可将高级语言编写的源代码构建成计算机直接执行的二进制代码.GCC是Linux平台下最常用的编译程序 ...
- go1.6.2 linux/amd64 的一个bug: gcc: 无法识别的选项‘-no-pie’
如果你的linux是中文版的. 即:执行 # echo $LANG 命令,返回的是 zh_CN.UTF-8 则会出现这个bug, 如果是 en_US.UTF-8 则没有这个问题. 这个bug的一个 ...
- 前端测试框架Jest系列教程 -- Global Functions(全局函数)
写在前面: Jest中定义了很多全局性的Function供我们使用,我们不必再去引用别的包来去实现类似的功能,下面将列举Jest中实现的全局函数. Jest Global Functions afte ...
- Ubuntu gcc错误:对'log'等函数未定义的引用
Ubuntu gcc错误:对'log'等函数未定义的引用 a.c #include <stdio.h>#include <math.h>int main(){ float ...
- gcc/g++ -O 优化选项说明
查查gcc手册就知道了,每个编译选项都控制着不同的优化选项 下面从网络上copy过来的,真要用到这些还是推荐查阅手册 -O设置一共有五种:-O0.-O1.-O2.-O3和-Os. 除了-O0以外,每一 ...
- Jmeter_选项_函数助手对话框_CSVRead
1.之前有写过Jmeter_请求原件之参数化CSV 2.这次是使用函数助手 CSVRead去进行处理 3.作用注册10个账户 4.线程组设置,10个线程进行,因为是注册10个账户,如果线程设置为1,循 ...
- Xdebug文档(四)函数跟踪
Xdebug能让你把所有函数调用,包括参数和返回值以不同的格式记录到文件中. 这些号称“函数跟踪”功能能帮助你面对一个新应用程序,亦或者在程序运行时你想弄清楚它在做什么.函数跟踪功能可以选择性地显示函 ...
- Linux TTY函数跟踪
1. 介绍 本文介绍了TTY打开.TTY读和TTY写操作的函数跟踪过程 2. 示例 下面是一个简单的Linux TTY打开和读写过程 #include <termios.h> #inclu ...
- Linux下gcc编译控制动态库导出函数小结
根据说明文档“How To Write Shared Libraries"介绍, 有四种方法: 1. 在方法声明定义时,加修饰:__attribute__((visibility(" ...
随机推荐
- 直接用bcdedit创建bootmgr数据库和修复启动菜单
直接用bcdedit创建bootmgr数据库和修复启动菜单 使用下面方法之前需要bcdedit和bootsect两条命令,这两条命令可以到vista或者windows 7安装光盘上获得.bootsec ...
- Title Case a Sentence-freecodecamp算法题目
Title Case a Sentence(中单词首字母大写) 要求 确保字符串的每个单词首字母都大写,其余部分小写. 像'the'和'of'这样的连接符同理. 思路 将句子小写化后用.split(& ...
- Statues CodeForces - 129C(bfs)
In this task Anna and Maria play a game with a very unpleasant rival. Anna and Maria are in the oppo ...
- poj 2385 树上掉苹果问题 dp算法
题意:有树1 树2 会掉苹果,奶牛去捡,只能移动w次,开始的时候在树1 问最多可以捡多少个苹果? 思路: dp[i][j]表示i分钟移动j次捡到苹果的最大值 实例分析 0,1 1,2...说明 偶数 ...
- SPOJ QTREE4 - Query on a tree IV 树分治
题意: 给出一棵边带权的树,初始树上所有节点都是白色. 有两种操作: C x,改变节点x的颜色,即白变黑,黑变白 A,询问树中最远的两个白色节点的距离,这两个白色节点可以重合(此时距离为0). 分析: ...
- UVa 1407 树形背包 Caves
这道题可以和POJ 2486 树形背包DP Apple Tree比较着来做. 参考题解 #include <iostream> #include <cstdio> #inclu ...
- Linux权限和指令的关系
1.让用户能进入某目录称为”可工作目录“的基本权限为何: 可使用的指令:例如cd等变换工作目录的指令: 目录所需权限:用户对这个目录至少需要具有x的权限 额外需求:如果用户想要在这个目录内利用ls查阅 ...
- LoadRunner11使用方法以及注意点收集
一:安装loadrunner http://jingyan.baidu.com/article/f7ff0bfc1cc82c2e26bb13b7.html http://www.cnblogs.com ...
- 令人惊叹的Visual Studio Code插件
vscode是一款开源且优秀的编辑器,接下来让我吐血推荐一下我工作使用过的令人惊叹的Visual Studio Code插件. 代码编辑插件 vscode-color-highlight ------ ...
- A Few Laughing Men
A Few Laughing Men CodeChef - LAUGHMEN Balaji is a great person to hang out with. He tells really am ...