gdb – 按预定义规则跳过某个文件的进一步步骤?
假设我有这个文件:
xb@dnxb:/tmp/c$ cat helloworld.h void hello(); xb@dnxb:/tmp/c$ cat helloworld.c #include void hello() { printf("Hello world!\n"); printf("Next line\n"); } xb@dnxb:/tmp/c$ cat main.c #include #include "helloworld.h" int main(void) { hello(); return 0; }
并编译:
xb@dnxb:/tmp/c$ gcc -g3 -shared -o libhello.so -fPIC helloworld.c -std=c11 xb@dnxb:/tmp/c$ gcc -g3 main.c -o main -Wl,-rpath,"$PWD" -L. -lhello
然后用gdb调试:
xb@dnxb:/tmp/c$ gdb -q -n ./main Reading symbols from ./main...done. (gdb) b main Breakpoint 1 at 0x40062a: file main.c, line 5. (gdb) r Starting program: /tmp/c/main Breakpoint 1, main () at main.c:5 5 hello(); (gdb) s hello () at helloworld.c:3 3 printf("Hello world!\n");
此时,反复按Enter键 (相当于键入s
并反复按Enter键 ):
(gdb) _IO_puts (str=0x7ffff7bd9689 "Hello world!") at ioputs.c:33 33 ioputs.c: No such file or directory. (gdb) 35 in ioputs.c (gdb) strlen () at ../sysdeps/x86_64/strlen.S:66 66 ../sysdeps/x86_64/strlen.S: No such file or directory. (gdb)
如果我只关心helloworld.c
而没有进一步进入上面的printf()
的ioputs.c
怎么办?
xb@dnxb:/tmp/c$ gdb -q -n ./main Reading symbols from ./main...done. (gdb) b main Breakpoint 1 at 0x40062a: file main.c, line 5. (gdb) r Starting program: /tmp/c/main Breakpoint 1, main () at main.c:5 5 hello(); (gdb) s hello () at helloworld.c:3 3 printf("Hello world!\n"); (gdb) n Hello world! 4 printf("Next line\n"); (gdb)
这就是我想要的,但它需要我手动发现我在helloworld.c
,因此是时候输入n
。 我的期望是:
(gdb) s hello () at helloworld.c:3 3 printf("Hello world!\n");
按Enter将跳过自定义文件名的步入,例如helloworld.c
在这种情况下直接跳到printf("Next line\n");
:
(gdb) Hello world! 4 printf("Next line\n"); (gdb)
好处是我不必发现我应该在哪里停止并更改为n
,特别是如果代码层次结构很大并且我可能会多次进入helloworld.c
。 我只需要反复按Enter键并跳过不想要的深度/水平。
我该怎么做?
[码]
我在我的gdb启动文件~/.gdbinit
编写了这段代码(在编写此代码之前,前6行是我现有的行):
set environment HISTSIZE 10000000 set history filename ~/.gdb_history set history save on set history size 10000000 set history expansion on show history #compile/make gdb failed #rf: http://stackoverflow.com/a/33663513/1074998 #use `make install` #rf: http://stackoverflow.com/a/15306144/1074998 #for step into strcpy(), memcpy() #gcc -fno-builtin -g foo.c #other rf: https://ubuntuforums.org/showthread.php?t=1572766 #rf: https://codywu2010.wordpress.com/2014/09/13/why-is-my-gdb-step-command-behavior-changed/ # printf step into 1st instruction is: _IO_puts (kali default gdb) vs puts (self-compile gdb) #rf: http://stackoverflow.com/a/31076131/1074998 #nosharedlibrary, sharedlibrary #rf: http://www.sourceware.org/gdb/onlinedocs/gdb/Source-Path.html #dir #rf2: https://www.chemie.fu-berlin.de/chemnet/use/info/gdb/gdb_8.html #dir #rf: http://stackoverflow.com/a/20116541/1074998 #`apt-get source libc6` and put the path of stdio-common (use `find . -name '*stdio-common*'` to find out) dir '/home/xiaobai/note/src/gdb-7.11.1/glibc-2.24/stdio-common' #rf: http://stackoverflow.com/a/29956038/1074998 dir '/usr/src/glibc/glibc-2.24/malloc' set step-mode on #rf: https://sourceware.org/gdb/onlinedocs/gdb/Define.html #set max-user-call-depth 100000000 #type alias 'my' enough, nod ni type `mystart` define mystart #reset to fix "Value can't be converted to integer." #initialized with enough buffer to fix "Too many array elements" #`set {char [4096]}$fpath = 0` failed #rf1: http://reverseengineering.stackexchange.com/a/2216/15176 #rf2: http://stackoverflow.com/a/30955291/1074998 #rf3: http://serverfault.com/a/306726/210566 ,and http://stackoverflow.com/a/14508945/1074998 #max file fullpath is 4096 set $fpath = "" set $dindex = 0 set $excludeFile="/tmp/cc/helloworld2.c" set $excludeFile2="/home/xiaobai/note/src/gdb-7.11.1/glibc-2.24/sysdeps/x86_64/strchr.S" #to ensure `set $fpath = $mallocFile` later will not truncate buffer set $mallocFile = $fpath #set it to "mallo.c" if not do `sudo apt-get install glibc-source`, `cd /usr/src/glibc`, `sudo tar xvf glibc-N.NN.tar.xz`, and put `dir '/usr/src/glibc/glibc-N.NN/malloc'` on top of this file, rf: http://stackoverflow.com/a/29956038/1074998 set $mallocFile = "/usr/src/glibc/glibc-2.24/malloc/malloc.c" b main r end #rf: http://stackoverflow.com/a/1530774/1074998 define pFullPathTitle printf "\n******************** %s ********************\n", $fpath end define pFilePathTitle printf "\n******************** <%s> ********************\n", $fpath end define sn set $dindex += 1 echo \n[customStepping]\n\n #rf: https://sourceware.org/gdb/onlinedocs/gdb/Logging-Output.html set logging off set logging file /tmp/gdbFile set logging redirect on set logging on #ensure put this after `set logging on` otherwise will not work set logging overwrite #http://stackoverflow.com/a/1907066/1074998 #info source #Bcareful might got extra headline "warning: Currently logging to /tmp/gdbFile. Turn the logging off and on to make the new setting effective." #and line "^Located in " will not exist at all if no full filepath which was so common info source set logging off #rf1: http://stackoverflow.com/a/6889615/1074998 #shell var to gdb #rf2: http://unix.stackexchange.com/a/151609/64403 #eval #rf3: http://www.delorie.com/gnu/docs/gdb/gdb_118.html #set var #rf4: https://sourceware.org/gdb/download/onlinedocs/gdb/Convenience-Vars.html #rf5: http://unix.stackexchange.com/a/320342/64403 #sed shell echo 'set $fpath = "'`cat /tmp/gdbFile | grep '^Located' | sed 's/^Located in //g'`'"' > /tmp/gdbFile.sources source /tmp/gdbFile.sources #rf: https://sourceware.org/gdb/onlinedocs/gdb/Command-Files.html #if-else #rf2: https://blogs.oracle.com/ksplice/entry/8_gdb_tricks_you_should #if #rf: http://stackoverflow.com/a/7424716/1074998 #strcmp if strcmp($fpath, $excludeFile) == 0 pFullPathTitle echo File #1 [Stepping] Disabled \n n else if strcmp($fpath, $excludeFile2) == 0 pFullPathTitle echo File #2 [Stepping] Disabled \n n else if strcmp($fpath, $mallocFile) == 0 #due to gdb freeze at this point, so i disable it #manually run no such problem, weird # #2925 victim = _int_malloc (ar_ptr, bytes); set $fpath = $mallocFile pFullPathTitle echo [mallo.c] freeze and should come here [Stepping] N/A\n\n info source #rf1: http://stackoverflow.com/a/9220953/1074998 #rf2: http://stackoverflow.com/questions/39124817/gdb-freezes-in-malloc finish #n else if strcmp($fpath, "") == 0 #`info source`'s "Located in " line not even exist ! #get single filename and set it to $fpath, source it, then print the title with this filename shell echo 'set $fpath = "'`cat /tmp/gdbFile | grep '^Current source file is ' | sed 's/^Current source file is //g'`'"' > /tmp/gdbFile.sources source /tmp/gdbFile.sources pFilePathTitle s else pFullPathTitle s end end end end end
[说明]
[1]
基本的想法是在遇到文件完整路径时做n
而不是s
,我想跳过踩踏步。
[2]
“AAAAA …”(4096个字符串)字符串是我能找到的唯一可以保留内存$fpath
的完整文件路径的方法,它需要在程序运行之前进行动态更新。 它可能有更好的方法来处理它,如果你知道更好的方式,请善意评论。
[3]
我的第一个问题是解析info source
中混合的完整文件路径。 我修改了gdb源代码( gdb/source.c
的source_info
的s->filename
是完整的s->filename
路径)并编译它。 但是失败了,它在printf
的第一个_IO_puts
中显示了_IO_puts
而不是_IO_puts
,我不记得我做了什么,现在它只改为libc.so.6
:
(gdb) s hello () at helloworld.c:3 3 printf("Hello world!\n"); (gdb) 0x00007ffff788a160 in printf () from /lib/x86_64-linux-gnu/libc.so.6 (gdb) 0x00007ffff788a167 in printf () from /lib/x86_64-linux-gnu/libc.so.6 ... `s->filename` still give me /tmp/cc/helloworld.c which is not what i want.
但之后我意识到我可以/tmp/gdbFile
info source
输出保存到/tmp/gdbFile
(使用set logging
hack)。 现在使用gdb命令shell
用常规Unix工具( grep
/ sed
)解析文件并将其重新格式化为/tmp/gdbFile.sources
,然后使用gdb命令source
设置变量:
$ cat /tmp/gdbFile warning: Currently logging to /tmp/gdbFile. Turn the logging off and on to make the new setting effective. Current source file is main.c Compilation directory is /tmp/cc Located in /tmp/cc/main.c Contains 7 lines. Source language is c. Producer is GNU C11 6.1.1 20160802 -mtune=generic -march=x86-64 -g3 -fno-builtin. Compiled with DWARF 2 debugging format. Includes preprocessor macro info. $ cat /tmp/gdbFile.sources set $fpath = "/usr/src/glibc/glibc-2.24/sysdeps/unix/sysv/linux/_exit.c" $
[4]
/home/xiaobai/note/src/gdb-7.11.1/glibc-2.24/sysdeps/x86_64/strchr.S
是一个示例,显示它可以禁用多个+任何文件名。 如果我启用步骤到printf
将调用此文件。 (在我的问题中只需将文件名helloworld.c
更改为helloworld2.c
即可测试)
[5]
此代码使用文件路径中的空白空间进行测试,例如/tmp/c\ c/main
[6]
如果我想排除很多文件路径,我很难扩展,因为我必须手动插入if-else-end
。 如果您知道更好的方式,请发表评论。 我也有点担心if-esle-end
深度的递归可能会导致问题(我不确定gdb如何在内部处理它),所以我确保n
或s
是条件中的最后一条指令。
[7]
幸运的是,在我运行sn
之后, Enter不会更改为s
/ n
( sn
最后一条指令),即重复按Enter键将保持行为为sn
),否则我不知道如何分配它。
[结果]
从printf
按Enter键进入helloworld.c
会自动将s
更改为n
,然后从此文件返回后返回s
。 请注意,我的自定义函数名为mystartup
(如果没有其他命令名称以“my”开头, my
应该工作)和sn
(不替换s
,所以你可以在helloworld.c
调用s
来踩踏:):
xb@dnxb:/tmp/cc$ gdb -q /tmp/c\ c/main expansion: History expansion on command input is on. filename: The filename in which to record the command history is "/home/xiaobai/.gdb_history". remove-duplicates: The number of history entries to look back at for duplicates is 0. save: Saving of the history record on exit is on. size: The size of the command history is 10000000. Reading symbols from /tmp/cc/main...done. (gdb) my Breakpoint 1 at 0x40063a: file main.c, line 5. Breakpoint 1, main () at main.c:5 5 hello(); (gdb) sn [customStepping] ******************** /usr/src/glibc/glibc-2.24/sysdeps/unix/sysv/linux/_exit.c /tmp/cc/main.c ******************** hello () at helloworld.c:3 3 printf("Hello world!\n"); (gdb) [customStepping] ******************** /tmp/cc/helloworld.c ******************** File #1 [Stepping] Disabled Hello world! 4 printf("Next line\n"); (gdb) [customStepping] ******************** /tmp/cc/helloworld.c ******************** File #1 [Stepping] Disabled Next line 5 } (gdb) [customStepping] ******************** /tmp/cc/helloworld.c ******************** File #1 [Stepping] Disabled main () at main.c:6 6 return 0; (gdb) [customStepping] ******************** /tmp/cc/main.c ******************** 7 } (gdb) [customStepping] ******************** /tmp/cc/main.c ******************** __libc_start_main (main=0x400636 , argc=1, argv=0x7fffffffd468, init=, fini= , rtld_fini= , stack_end=0x7fffffffd458) at ../csu/libc-start.c:325 325 exit (result); (gdb) ...
GDB的skip命令可以让您跳过不感兴趣的函数或文件。 它的语法是跳过uninteresting_function或跳过文件uninteresting.file 。 ( 这里有完整的文档)
在您的情况下,您应该尝试跳过文件ioputs.c 。
Gdb 7.12在文件和函数名称上引入了模式,因此如果需要,您应该构建/安装该版本。 此版本允许您(例如) 跳过文件-gfi / lib / * ,它跳过位于/ lib /中的任何源文件…