GDB调试

首先介绍下GDB常用命令

流程
start <args>或run <args>:bin程序开始执行
c(continue):从断点开始继续执行,直到下一断点或结束
n(next):执行下一行(跳过函数)
s(step):执行下一行(进入函数)
finish:运行到当前函数结束退出
运行状态下ctrl-c:暂停
q(quit),或暂停状态下ctrl-c:退出

断点
b <func-name>:在函数上打断点
b * <address>:在代码段地址上打断点
i b:查看断点
d b <N>:禁用指定编号的断点
e b <N>:启用指定编号的断点
b ... if <expr>:条件断点:若<expr>为真(或非NULL),在断点处暂停

临时进入shell模式
shell(进入)、exit(退出)
帮助:help或?

查看信息
bt(backtrace):查看调用栈
i r:查看寄存器信息
x/Ns:以字符串形式查看指定变量/地址
x/Nd:以十进制数字形式查看指定变量/地址
x/Nx:以十六进制形式查看指定变量/地址
x/Ni:查看指定函数/地址的汇编指令
p <expr>:查看<expr>的值

x/b &str:查看全局变量的字符串,str是一个全局字符串

其它
暂停状态下回车:执行上一条命令
支持上翻、下翻

设置打印字符的个数为不限制数量,设置为unlimited或者zero。

(gdb) set print elements 0
(gdb) show print elements
Limit on string chars or arry elemtns to print is unlimited

CPU寄存器信息

mips

zero:永远为0
v0/v1:临时寄存器;函数返回时存返回值
a0 ~ a3:临时寄存器;调用函数时存参数
t0 ~ t9:临时寄存器。t9常用于临时存要调用的子函数入口地址
s0 ~ s8:非易失性寄存器
gp:全局指针
sp:栈顶指针
ra:当前函数返回地址
pc:当前指令

x86

1) %rax 作为函数返回值使用。
2) %rsp 栈指针寄存器,指向栈顶。
3) %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数等。
4) %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改。
5) %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值。
 
 
栈结构信息
X86_64的栈结构:
 
如上图所示:%rsp寄存器存储的地址指向当前函数的栈顶,%rbp寄存器是我们推栈的关键,它指向的位置保存了两个字,前一个函数栈的%rbp寄存器值和函数返回地址。
 
(三)推栈过程:
 
    下面开始介绍推栈过程,可以发现,推栈的过程就是跟踪查找%rbp寄存器的内容,而且我们无需像MIPS体系那样关注某个函数的堆栈的大小。
 
 1) 设备死机时会进KDB,通过KDB的rd命令可查看当前挂死环境所有寄存器的内容:
[12]kdb> rd
     r15 = 0x0000000000000007      r14 = 0x0000000000000000 
     r13 = 0x0000000000000000      r12 = 0xffffc9005dd6b2b0 
      bp = 0xffff8800462375e8       bx = 0xffff8800463780a0 (这里的bp寄存器和上面提到的%rbp寄存器是等价的)
     r11 = 0x0000000000000000      r10 = 0xffffc9005dd735b0 
      r9 = 0x0000000000000002       r8 = 0xffffc9005dd67130 
      ax = 0x0000000004230423       cx = 0xffffc90061f9b6d3 
      dx = 0x0000000000000095       si = 0x0000000000000000 
      di = 0xffffc9005dd67008  orig_ax = 0xffffffffffffffff 
      ip = 0xffffffffc224156c       cs = 0x0000000000000010  
   flags = 0x0000000000010246       sp = 0xffff880046237570 
      ss = 0x0000000000000018 &regs = 0xffff8800462374d8

2) SP寄存器指向当前调用栈的栈顶,通过md命令可查看调用栈的内容,而IP寄存器里存储的是正在执行的函数地址
[12]kdb> md 0xffff880046237570    
fa_traverse_dfa+0x1ec             
0xffff880046237570 ffff880046237580 0000000000000018   .u#F............
0xffff880046237580 ffff880046237696 ffff880046378355   .v#F....U.7F....
0xffff880046237590 0000000000000000 ffffc9005dd7772e   .........w.]....
0xffff8800462375a0 ffff8800462376e4 ffff880046378356   .v#F....V.7F....
0xffff8800462375b0 ffff880046378010 0000000042dd1653   ..7F....S..B....
0xffff8800462375c0 0000000000000001 0000000000000000   ................
0xffff8800462375d0 0000000000000000 0000000000000346   ........F.......
0xffff8800462375e0 0000000000000000                                     // 3)BP寄存器指向的地址(0xffff8800462375e8),存储了前一个函数栈的BP和返回地址。ffff880046237638 是前一个函数栈的BP值, ffffffffc22427a9是函数返回地址。
ffff880046237638   ........8v#F....
0xffff8800462375f0 ffffffffc22427a9                                   

fa_traverse+0x69        // 4)通过函数返回地址得到一个调用函数名称fa_traverse
0000000000000000   .'$.............
0xffff880046237600 ffff880046237688 0000000000000000   .v#F............
0xffff880046237610 0000000000000000 0000000000000000   ................
0xffff880046237620 0000000000000000 ffffffffc31ff918   ................
0xffff880046237630 ffffc9005dd67000                                 // 5)同时,我们得到了fa_traverse函数的BP寄存器的值,它指向的地址(ffff880046237638)同样存储了一个函数的BP和返回地址。
ffff8800462376c8   .p.].....v#F....
0xffff880046237640 ffffffffc22428ed                                 // 6)以此类推, 接着向下推栈即可。

regex_traverse+0x7d
0000000000000000   .($.............
0xffff880046237650 ffff880046237680 ffff880046237690   .v#F.....v#F....
0xffff880046237660 ffff8800462376e4 0000000000000000   .v#F............
[12]kdb> 
0xffff880046237670 ffff880046237688 0000000000000000   .v#F............
0xffff880046237680 0000000000000000 0000000000000001   ................
0xffff880046237690 0000000000000000 0000000000000000   ................
0xffff8800462376a0 ffffffffc31ff660 ffff880029099c00   `..........).... 
0xffff8800462376b0 0000000000000000 0000000000000010   ................
0xffff8800462376c0 0000000000000000 
ffff8800462376f8   .........v#F....
0xffff8800462376d0 ffffffffc223f533

pcre_engine_regex_check+0x43
ffffffffc210f000   3.#.............
0xffff8800462376e0 00003a98c31ff660 ffff880029099c00   `....:.....)....
0xffff8800462376f0 ffffffffc31ff660 
ffff880046237778   `.......xw#F....
0xffff880046237700 ffffffffc237dea1 

dcdm_pcre_new_single_rule_proc+0xf1
0000020000000200   ..7.............
0xffff880046237710 0000000000000000 ffff880029099c30   ........0..)....
0xffff880046237720 ffffe90142dd16f0 ffffe90142dd16d8   ...B.......B....
0xffff880046237730 ffffffffc31ff918 0000000000000000   ................
0xffff880046237740 ffff880029099c1c ffff8800462377a8   ...).....w#F....
0xffff880046237750 0000000000000000 ffff880046370000   ..........7F....
0xffff880046237760 ffffffffc31ff660 0000000000000000   `...............
0xffff880046237770 ffff880029099c00 
ffff8800462377d8   ...).....w#F....
0xffff880046237780 ffffffffc237e34b 

dcdm_pcre_new_flow_proc+0x1eb
ffff880065a04880   K.7......H.e....
0xffff880046237790 0000000165a1e0f6 ffffe90142dd16c0   ...e.......B....
0xffff8800462377a0 ffff880065a04880 ffff8800462377d8   .H.e.....w#F....
0xffff8800462377b0 ffff880065a04880 ffff880046370000   .H.e......7F....
0xffff8800462377c0 ffffe90142dd16a8 000000000002d2a0   ...B............
0xffff8800462377d0 ffffe90142dd1518 ffff8800462377f8   ...B.....w#F....
0xffff8800462377e0 ffffffffc237e3cb ffff880065a04880   ..7......H.e....
0xffff8800462377f0 ffff880046370000 ffff880046237898   ..7F.....x#F....
0xffff880046237800 ffffffffc237d311 0000000000000000   ..7.............
0xffff880046237810 0000000000000000 ffff880046237868   ........hx#F....
0xffff880046237820 ffffffffc21eb9ff 471d0201ba190101   ...............G
0xffff880046237830 000000061606db27 ffff880046237858   '.......Xx#F....
0xffff880046237840 ffff880065a04880 ffffffffc5ed6860   .H.e....`h......
0xffff880046237850 42000004c2380ca3 ffff880046237898   ..8....B.x#F....
0xffff880046237860 00000001c23785f4 ffff8800462378b8   ..7......x#F....

调试进程

使用方法
gdb -p <pid>
适用场景
某进程运行过程中会出故障,需要跟踪执行过程
例:页面下发配置时发生500错误,需分析web_main代码流程
某进程已经出故障但未崩溃,需要查看当前正在哪个流程中
例:串口卡住,但远程诊断能用,需要析vtysh卡在何处
操作流程:
进入gdb模式;
打断点;
c;
进行页面/命令行等操作,使进程运行至断点处;
分析寄存器、内存信息。
反复进行3~5步操作,直到分析明白。
q;

调试bin

使用方法
gdb <bin-file-path>
适用场景
某程序执行时不会转变成后台进程,运行过程中会出故障,需要跟踪执行过程
例:配置恢复有启动项失败,需要从startup程序跟踪其执行流程
某程序执行时会转变成后台进程,但进入自身循环之前(相当于进程启动阶段)会出故障,需要分析该过程执行流程
例:eventd进程进入死循环前出故障,需要分析该执行过程
set follow-fork-mode (parent|child)命令用法
用于指定跟随子进程还是父进程(默认为parent)
当前进程切换成后台进程时,执行set follow-fork-mode child
调用生成子进程的函数(如system、popen、exec系列函数等)前,执行set follow-fork-mode parent
操作流程:
与后台进程类似,但在1~3之间执行set follow-fork-mode child,确定已切换到后台后set follow-fork-mode parent切回来。

调试启动项:

先找到要调用的启动项所在文件startup_N_*、所在行M
对/usr/bin/startup挂gdb
先打断点、执行set follow-fork-mode child
运行:r -p N M

原文地址:https://www.cnblogs.com/gaoshaonian/p/10219680.html