问题:
本系列期望用一个简单的实例来展示一些调试技巧。不时更新。
本期问题是:如何才能快速定位到打开某个文件的代码?(对应open/fopen, 读者可举一反三到其它系统函数)。当面对屎山代码时,GDB的条件断点也许能帮你从成百上千个open/fopen调用中快速锁定到目标。
思路:
如果有代码,当然可以直接搜关键字(文件名),如果关键字不好搜那就不如GDB调试来的快,这儿我们只着重讲GDB的办法。显然就是在open或fopen上下条件断点,条件就是比较正要打开的文件是否是我们要关注的(下面假设文件名为“bb”).
巧合的是open/fopen的第一个参数都是要打开的文件名,所以问题就变成了比较两个字符串,第一个是open/fopen的第一个参数,第二个是“bb”. 在x86_64平台上,函数的第一个参数用寄存器rdi来传递,
故条件断点大概为:
b fopen if 比较($rdi, "bb")
b open if 比较($rdi, "bb")
为便于调试,先写个简单的程序
#include<stdio.h>
#include<stdlib.h>
//2021/1/1
//mzhai
//test for GDB conditional break
int main(int argc, char* argv[])
{
char str[]="hello";
printf("sizeof(str)=%d\n", sizeof(str));
fopen("aa","r");
printf("after aa\n");
fopen("bb","r");
printf("end\n");
}
第一种办法:
比较字符串,第一想法就是调用strcmp
[mzhai]$ gdb a.out
(gdb) b fopen if strcmp($rdi,"bb")==0
No symbol "strcmp" in current context.
(gdb) b main
Breakpoint 1 at 0x4005bc: file test.c, line 7.
(gdb) r
(gdb) b fopen if strcmp($rdi,"bb")==0
Breakpoint 2 at 0x4004a0
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004005bc in main at test.c:7
breakpoint already hit 1 time
2 breakpoint keep y 0x00000000004004a0 <fopen@plt>
stop only if strcmp($rdi,"bb")==0
(gdb) c
Continuing.
sizeof(str)=6
after aa
Breakpoint 2, 0x00000000004004a0 in fopen@plt ()
完美跳过了第一个fopen. 不过要注意程序运行后才会加在libc才能用strcmp, 这也是一开始出现No symbol “strcmp” in current context.的原因。
第二种:
除了使用libc库里的strcmp,GDB也提供了比较字符串的内部函数_streq, GDB叫convenience function, 可以在GDB中查看器帮助文档。
(gdb) help function
Placeholder command for showing help on convenience functions.
List of function subcommands:
function _memeq -- $_memeq - compare bytes of memory
function _regex -- $_regex - check if a string matches a regular expression
function _streq -- $_streq - check string equality
function _strlen -- $_strlen - compute string length
function caller_is -- Return True if the calling function's name is equal to a string
function caller_matches -- Return True if the calling function's name matches a string
function in_scope -- Return True if all the given variables or macros are in scope
实践如下:
(gdb) b fopen if $_streq((char*)$rdi,"bb")
Breakpoint 1 at 0x4004a0
(gdb) r
Starting program: /home/mzhai/cprograms/a.out
sizeof(str)=6
after aa
Breakpoint 1, 0x00000000004004a0 in fopen@plt ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7.x86_64
推荐第二种。