环境:gcc (OpenWrt/Linaro GCC 4.8)
以如下的简单代码为例,说明gdb的使用。
1 void func1(int a, int b) 2 { 3 int c; 4 c = a + b; 5 } 6 7 int main(void) 8 { 9 10 func1(11,22); 11 return 100; 12 }
1. gdb 下一步的命令
a.执行下一行语句(语句级别)
next(或n) 执行下一行语句,如果是函数调用则直接将函数执行完;相当于visual studio调试器中的"Step Over (单步跟踪)"
step(或s) 执行下一行语句,如果是函数调用则进入函数中。即常说的单步调试,相当于visual studio调试器中的"Step Into (单步跟踪进入)"
这两个命令必须在有源代码调试信息的情况下才可以使用(GCC编译时使用“-g”参数)
b.执行下一条指令(指令级)
si 类似于s;
ni 类似于n;
这两个命令(si/ni)所针对的是汇编指令
2. disassemble
可以反汇编当前函数或者指定的函数,
单独用disassemble命令是反汇编当前函数,
如果disassemble命令后面跟函数名或地址,则反汇编指定的函数.
(gdb) disassemble Dump of assembler code for function main: 0x00008440 <+0>: push {r11, lr} 0x00008444 <+4>: add r11, sp, #4 => 0x00008448 <+8>: mov r0, #11 0x0000844c <+12>: mov r1, #22 0x00008450 <+16>: bl 0x8410 <func1> 0x00008454 <+20>: mov r3, #100 ; 0x64 0x00008458 <+24>: mov r0, r3 0x0000845c <+28>: pop {r11, pc} End of assembler dump. (gdb) disassemble Dump of assembler code for function func1: 0x00008410 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008414 <+4>: add r11, sp, #0 0x00008418 <+8>: sub sp, sp, #20 0x0000841c <+12>: str r0, [r11, #-16] 0x00008420 <+16>: str r1, [r11, #-20] => 0x00008424 <+20>: ldr r2, [r11, #-16] 0x00008428 <+24>: ldr r3, [r11, #-20] 0x0000842c <+28>: add r3, r2, r3 0x00008430 <+32>: str r3, [r11, #-8] 0x00008434 <+36>: sub sp, r11, #0 0x00008438 <+40>: pop {r11} ; (ldr r11, [sp], #4) 0x0000843c <+44>: bx lr End of assembler dump.
3. 显示寄存器的信息
info registers可以显示所有寄存器的当前值(不包括浮点寄存器)
info all-registers(包括浮点寄存器)
简写: i r
1 (gdb) info registers 2 r0 0xb 11 3 r1 0x16 22 4 r2 0xbefffe2c 3204447788 5 r3 0x8440 33856 6 r4 0xbeffff14 3204448020 7 r5 0x8304 33540 8 r6 0xbefffc80 3204447360 9 r7 0x8 8 10 r8 0x0 0 11 r9 0x0 0 12 r10 0xb6ffef7c 3070226300 13 r11 0xbefffc64 3204447332 14 r12 0xbefffd68 3204447592 15 sp 0xbefffc50 0xbefffc50 16 lr 0x8454 33876 17 pc 0x8424 0x8424 <func1+20> 18 cpsr 0x60000010 1610612752 19 (gdb) i r 20 r0 0xb 11 21 r1 0x16 22 22 r2 0xbefffe2c 3204447788 23 r3 0x8440 33856 24 r4 0xbeffff14 3204448020 25 r5 0x8304 33540 26 r6 0xbefffc80 3204447360 27 r7 0x8 8 28 r8 0x0 0 29 r9 0x0 0 30 r10 0xb6ffef7c 3070226300 31 r11 0xbefffc64 3204447332 32 r12 0xbefffd68 3204447592 33 sp 0xbefffc50 0xbefffc50 34 lr 0x8454 33876 35 pc 0x8424 0x8424 <func1+20> 36 cpsr 0x60000010 1610612752
4.查看内存的值:
gdb中使用"x"命令来打印内存的值,格式为"x/nfu addr"。含义为以f格式打印从addr开始的n个长度单元为u的内存值。参数具体含义如下:
a)n:输出单元的个数。
b)f:是输出格式。比如x是以16进制形式输出,o是以8进制形式输出,等等。
x(hex) 按十六进制格式显示变量。
d(decimal) 按十进制格式显示变量。
u(unsigned decimal) 按十进制格式显示无符号整型。
o(octal) 按八进制格式显示变量。
t(binary) 按二进制格式显示变量。
a(address) 按十六进制格式显示变量。
c(char) 按字符格式显示变量。
f(float) 按浮点数格式显示变量
c)u:标明一个单元的长度。b是一个byte,h是两个byte(halfword),w是四个byte(word),g是八个byte(giant word)
1 举例: 2 以16进制格式打印从add开始的16个byte的值: 3 (gdb) x/16xb $r11 4 0xbefffc64: 0x6c 0xfc 0xff 0xbe 0x00 0x00 0x00 0x00 5 0xbefffc6c: 0xc4 0xcc 0xfc 0xb6 0x00 0x90 0xfd 0xb6
5.打印栈帧信息:
frame 打印当前栈帧的简要信息。
1 (gdb) frame 2 #0 func1 (a=11, b=22) at hello.c:17 3 17 in hello.c
6.info frame 打印当前栈帧的详细信息。
1 (gdb) info frame 2 Stack level 0, frame at 0xbefffc68: 3 pc = 0x8434 in func1 (hello.c:17); saved pc 0x8454 4 called by frame at 0xbefffc70 5 source language c. 6 Arglist at 0xbefffc64, args: a=11, b=22 7 Locals at 0xbefffc64, Previous frame's sp is 0xbefffc68 8 Saved registers: 9 r11 at 0xbefffc64
7.info frame args 打印指定栈帧的详细信息
1 (gdb) i f 1 2 Stack frame at 0xbefffc70: 3 pc = 0x8454 in main (hello.c:22); saved pc 0xb6fcccc4 4 caller of frame at 0xbefffc68 5 source language c. 6 Arglist at 0xbefffc6c, args: 7 Locals at 0xbefffc6c, Previous frame's sp is 0xbefffc70 8 Saved registers: 9 r11 at 0xbefffc68, lr at 0xbefffc6c
8.info args 打印函数参数信息
1 (gdb) info args 2 a = 11 3 b = 22
9.info locals 打印当前可访问的局部变量的信息。
1 (gdb) info locals 2 c = 0
10.怎样知道一个栈帧的大小?
fp和sp之间的区域就是一个函数的栈帧的大小。
fp一般是指r11寄存器。
sp一般是指r13寄存器。
1 (gdb) i r 2 r0 0xb 11 3 r1 0x16 22 4 r2 0xb 11 5 r3 0x8440 33856 6 r4 0xbeffff14 3204448020 7 r5 0x8304 33540 8 r6 0xbefffc80 3204447360 9 r7 0x8 8 10 r8 0x0 0 11 r9 0x0 0 12 r10 0xb6ffef7c 3070226300 13 r11 0xbefffc64 3204447332 14 r12 0xbefffd68 3204447592 15 sp 0xbefffc50 0xbefffc50 16 lr 0x8454 33876 17 pc 0x8428 0x8428 <func1+24> 18 cpsr 0x60000010 1610612752 19 20 (gdb) disassemble 21 Dump of assembler code for function func1: 22 0x00008410 <+0>: push {r11} ; (str r11, [sp, #-4]!) 23 => 0x00008414 <+4>: add r11, sp, #0 ;将sp中的内存地址放入r11,即设置fp 24 0x00008418 <+8>: sub sp, sp, #20 ;将sp中的内存地址减小20,即设置func1的栈顶指针,如上两条指令执行完后,该函数的栈帧的大小就确定了,20 bytes 25 0x0000841c <+12>: str r0, [r11, #-16] ;函数参数赋值,将r0中的值存入[r11, #-16]内存地址,即函数实参入栈 26 0x00008420 <+16>: str r1, [r11, #-20] ;函数参数赋值,将r1中的值存入[r11, #-16]内存地址 27 0x00008424 <+20>: ldr r2, [r11, #-16] 28 0x00008428 <+24>: ldr r3, [r11, #-20] 29 0x0000842c <+28>: add r3, r2, r3 30 0x00008430 <+32>: str r3, [r11, #-8] 31 0x00008434 <+36>: sub sp, r11, #0 32 0x00008438 <+40>: pop {r11} ; (ldr r11, [sp], #4) 33 0x0000843c <+44>: bx lr 34 End of assembler dump.
有时在调试过程中,想记录下来操作的过程及相关信息,那么 执行set logging on即可打卡记录,默认存在gdb.txt。
当然也可以自己指定log文件,set logging file logname
set logging overwrite on
命令可以让输出覆盖之前的日志文件;而 “set logging redirect on
”命令会让gdb的日志不会打印在终端