GDB
使用gdb前需要加编译参数 -g
要调试C/C++的程序,首先在编译时,我们必须要把调试信息源程序信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数可以做到这一点。如:
gcc -g hello.c -o hello
g++ -g hello.cpp -o hello
如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。
gdb调试带参数的程序
gdb --args ./testprg arg1 arg2
gdb 的三种调试方式
- attach 并调试一个已经运行的进程
- 运行并调试一个新的进程
- 远程调试目标机上新创建的进
gdb 中按 ctrl + c 可以继续设置断点
gdb attach 挂载到一个正在执行程序
Until指令
不带参数的 until 命令让目标程序运行至当前函数中当前行后的任意一行。和 next 命令类似,这种 until 命令也是用指令级单步来实现的,但不同的是它的 step_range_start 设定为当前函数的起始位置,也就是说,若指令级单步过程中 pc 向函数前部移动,程序是不会停止的,仅当程序单步至当前行后的某一行时程序才会停止,这就提供了一种跳出循环体的快捷方式。
带参数的 until 命令让目标程序继续运行,直至达到指定位置为止。因为只要在当前函数体内, until 命令的目的地址可以任意指定,因此不能再用指令级单步来实现它,而是采用在指定地址插入临时断点,然后让目标程序继续运行直至遇到断点停止的方法。
关于 until 命令需要注意的是,不管带参数还是不带参数, until 都是针对当前函数内部而言的,也就是说,只要 pc 离开当前函数体程序就会停止。
提示:warning: exec file is newer than core file.
可执行文件比Core文件生成日期晚,一般不会有什么问题,只是这个core文件应该不是由这个可执行文件执行时生成滴
调试时倘若发现其栈中变量不合理
应该考虑是,因为参数地址传递的原因当值在后面代码执行时对栈中变量做了修改
查看栈或反编译中真实函数名
c++filt用于将编译的二进制文件中的函数名转换为编译前名字
多线程调试指令
info threads 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。 前面有*的是当前调试的线程。
thread ID 切换当前调试的线程为指定ID的线程。
break thread_test.c:123 thread apply all在所有线程中相应的行上设置断点
break thread_test.c:123 thread ID
break thread_test.c:123 thread ID if bartab > lim 指定了断点设置在的源程序的行号。ID指定了线程的ID
thread apply ID1 ID2 command 让一个或者多个线程执行GDB命令command。
thread apply all command 让所有被调试线程执行GDB命令command。
set scheduler-locking off|on|step 控制当前线程执行时,其他线程的执行与否。
off 不锁定任何线程,也就是所有线程都执行,这是默认值。
on 只有当前被调试程序会执行。
step 在单步的时候,除了next过一个函数的情况以外,只有当前线程会执行。
栈查看
backtrace bt 打印当前的函数调用栈的所有信息。
如:
(gdb) bt
#0 func (n=250) at tst.c:6
#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
从上可以看出函数的调用栈信息:__libc_start_main --> main() --> func()
backtrace <n> bt <n> n是一个正整数,表示只打印栈顶上n层的栈信息。 backtrace <-n> bt <-n> -n表一个负整数,表示只打印栈底下n层的栈信息。 如果你要查看某一层的信息,你需要在切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。
frame <n> f <n> n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
up <n> 表示向栈的上面移动n层,可以不打n,表示向上移动一层。
down <n> 表示向栈的下面移动n层,可以不打n,表示向下移动一层。
上面的命令,都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三个命令: select-frame <n> 对应于 frame 命令。 up-silently <n> 对应于 up 命令。 down-silently <n> 对应于 down 命令。
反汇编disassemble
不带参数
默认的反汇编范围是 所选择帧的pc附近的函数
单个参数
可以是pc, 当然也可以是函数名,因为函数名 也是一个 地址; 这样范围就是该pc附近的函数
disass func_name
两个参数
反汇编一段内存地址, 第1个参数是起始地址,第2个是终止地址
disas /r 0x401365,0x401370
x来查看返回反汇编指令 如: x/3i $pc 显示pc开始的3条指令
info 命令:我们可以查看指定内存地址信息
16进制地址前面必须加0x,否则就会被当做十进制啦
"info symbol 内存地址" 可以获取内存地址所在的 symbol 相关信息:
(gdb) info symbol 0x00000001000017f7
main + 343 in section LC_SEGMENT.__TEXT.__text of /Users/LuoZhaohui/Library/Developer/Xcode/DerivedData/RunTimeSystem-anzdlhiwvlbizpfureuvenvmatnp/Build/Products/Debug/RunTimeSystem
info line命令来查看源代码在内存中的地址。info line后面可以跟“行号”,“函数名”,“文件名:行号”,“文件名:函数名”,这个命令会打印出所指定的源码在运行时的内存地址
"info line *内存地址" 可以获取内存地址所在的代码行相关信息:
(gdb) info line *0x00000001000017f7
Line 62 of "/Users/LuoZhaohui/Documents/Study/RunTimeSystem/RunTimeSystem/main.m" starts at address 0x1000017f7 <main+343> and ends at 0x10000180a <main+362>.
(gdb) info line tst.c:func
Line 5 of "tst.c" starts at address 0x8048456 <func+6> and ends at 0x804845d <func+13>.
查看main函数的 开始和结束地址
(gdb) info line main
Line 34 of “rank.c” starts at address 0×804847f andends at 0×8048493
指定源文件的路径
某些时候,用-g编译过后的执行程序中只是包括了源文件的名字,没有路径名。GDB提供了可以让你指定源文件的路径的命令,以便GDB进行搜索。
directory <dirname ... >
dir <dirname ... >
加一个源文件路径到当前路径的前面。如果你要指定多个路径,UNIX下你可以使用“:”,Windows下你可以使用“;”。
directory
清除所有的自定义的源文件搜索路径信息。
show directories
显示定义了的源文件搜索路径。
用list命令来打印程序的源代码
list <linenum>
显示程序第linenum行的周围的源程序。
list <function>
显示函数名为function的函数的源程序。
list
显示当前行后面的源程序。
list -
显示当前行前面的源程序。
一般是打印当前行的上5行和下5行,如果显示函数是是上2行下8行,默认是10行,当然,你也可以定制显示的范围,使用下面命令可以设置一次显示源程序的行数。
set listsize <count>
设置一次显示源代码的行数。
show listsize
查看当前listsize的设置。
list命令还有下面的用法:
list <first>, <last>
显示从first行到last行之间的源代码。
list , <last>
显示从当前行到last行之间的源代码。
list +
往后显示源代码。
一般来说在list后面可以跟以下这们的参数:
<linenum> 行号。
<+offset> 当前行号的正偏移量。
<-offset> 当前行号的负偏移量。
<filename:linenum> 哪个文件的哪一行。
<function> 函数名。
<filename:function> 哪个文件中的哪个函数。
<*address> 程序运行时的语句在内存中的地址。(必须在address前面加上0x,如:list *0x88888888)
设置输出到文件
在Gdb中要学会多用help
栈指针无法访问情况
可以通过Excel,构造出以1000进制或1024进制的相对SP寄存器偏移进行栈地址查找,或者后面可以改良为基于SP地址在一定范围内查找栈的脚本。
x/a $sp + 0
x/a $sp + 1024
x/a $sp + 2048
x/a $sp + 3072
x/a $sp + 4096
x/a $sp + 5120
x/a $sp + 6144
x/a $sp + 7168
x/a $sp + 8192
x/a $sp + 9216
x/a $sp + 10240
x/a $sp + 11264
x/a $sp + 12288
x/a $sp + 13312
set logging off
set logging file FILE