gdb笔记

1 篇文章 0 订阅

GDB调试方法有3种:
直接调试:gdb [exec file],用于直接仿真一个执行程序
附属调试:gdb attach pid,用于直接调试一个已运行的程序(ubuntu注意权限问题)
核心转存调试:gdb [exec file] [core-dump file],用于调试core-dump文件

基础指令:
file [exec file] 选择要调试的可执行文件
run/r 重新开始运行文件
start 其中 start 默认在main函数开头有个断点,让程序在 main 函数开头停下来;
list/l 查看源代码
next/n 单步调试,逐过程,函数直接执行
step/s 单步调试,逐语句,跳入函数执行
stepi 次数
nexti 次数
backtrace/bt 查看函数调用的堆栈信息
finish 结束当前函数,返回函数调用点 【不能f】
continue 继续执行
print/p 打印变量
break/b [filename:line_number] 打断点, [文件名:行号],也有多种方式
quit 退出gdb调试

进阶指令:
frame 查看当前帧信息,包括参数,文件所在位置等
info 该指令,可以查看到更多详细信息,如下:
info threads 查看所有线程信息
info sharedlibrary 查看共享库信息
info args 查看参数信息
info breakpoints 查看断点信息
info frame 查看当前帧信息
core-file [core-dump file] 选择core-dump文件
watch [expr] 观察某个表达式的值是否发生变化,如果有变化,马上停住程序。
也可以使用命令 watch (data type)address 的方式,设置监控变量的值。如下所示:
首先,输入p &data 命令,查看data的地址。
然后,可输入 watch *(int *) 0x7fffffffde64 命令设置观察点,监控变量 data 的值。
watch 命令,即变量(或表达式)的值改变,程序都会停下来。
rwatch 命令,即当发生读取变量行为时,程序就会暂停住。
awatch 命令,即当发生读取变量或改变变量值的行为时,程序就会暂停住。
设置特定线程中的监控点: watch cond thread num 命令

examine/x <n/f/u> 查看内存地址的值,addr为地址信息,
n表示内存长度,f表示显示格式,u表示显示字节数(b\h\w\g)
set 设置变量、寄存器、内存的值
signal [number] 发送一个信号给该进程
disassemble 反汇编,查看当前执行时的源代码的机器码

分割窗口:
layout:用于分割窗口,可以一边查看代码,一边测试:
layout src:显示源代码窗口
layout asm:显示反汇编窗口
layout regs:显示源代码/反汇编和CPU寄存器窗口
layout split:显示源代码和反汇编窗口
Ctrl + L:刷新窗口

调试器的backtrace是根据栈里面保存的函数返回地址来显示的。根据栈空间上的返回地址和
调试信息得出来的栈使用量,依次求出调用者函数。也就是说,调试器的backtrace的地址来自
进程的栈上。如果栈被破坏,就不能信任调试器生成的backtrace信息。

1、查看栈帧信息
i frame 帧标号

2、查看所以线程信息
thread apply all bt
或者:t a a bt
再调试某线程:t 线程号

3、查看内存
x/40x 地址
例如先获取数组地址,再打印内容
p/x arrry
x/40xg arrry_addr /* 8字节显示 /
x/40xw arrry_addr /
4字节显示 */

命令语法: x /Nuf experssion

  • N – 需要打印的单元数
  • u – 每个单元的大小
    u对应的意义:
    b 单字节
    h 双字节
    w 4字节
    g 8字节

f – 数据打印的格式
x 16进制
u 无符号十进制
i 指令地址格式

4、查看变量值
有时候,你需要查看一段连续的内存空间的值。比如数组的一段,
或是动态分配的数据的大小。你可以使用GDB的“@”操作符,“@”的左边是
第一个内存的地址的值,“@”的右边则你你想查看内存的长度
(gdb) p/x *pkt_user@1
$5 = {ctrl_head_is_set = 0x0, payload_head_is_set = 0x0, payload_is_set = 0x0, tx_performance_open = 0x0, rx_pkts_display = 0x0,
tx_pkts_display = 0x0, rx_pd_head_display = 0x0, tx_pd_head_display = 0x0, rx_ctrl_head_display = 0x0, tx_ctrl_head_display = 0x0,
tx_busy = 0x0, payload_head_len = 0x0, payload_len = 0x0, packet_len = 0x0, rx_pkts_display_len = 0x0, tx_pkts_display_len = 0x0,
pkt_num_to_send = 0xffffffff, pkt_rxtx_mode = 0x3, payload_list = 0x6808b8, pkt_ctrl_head = {eop_flag = 0x0, sop_flag = 0x0,
head_parity = 0x0}, loop_pkt = {loop_mode = 0x0, strip_len = 0x0, head_pos = 0x0, head_len = 0x0, valid_head_len = 0x0, head = {0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}
(gdb)

5、查看当前堆栈信息:
i frame 或者 info frame

6、堆栈切换
up 数字
down 数字

7、查看pc寄存器地址处的代码
查看出问题时的 pc指针(rip)指向的地址 如: rip = 0x8048484
再查看该地址处的代码: x/5i $rip

8、查看函数参数:
a、info args 命令显示函数的入参

b、打断点的时候使用 b *func, 在断电处 查看寄存器值,命令:i r,
整型和指针型的参数会从左往右依次保存到 rdi、rsi、rdx、rcx、r8、r9中,
浮点数会保存到xmm0、xmm1…中,多余这些寄存器的参数会保存到堆栈上,
因此,利用GDB在希望确认的函数开头中断之后,查看寄存器或栈即可获得参数内容。
【b func】命令可以将断电设置到汇编语言层次的函数开头,如果不加
设置的是地址偏后一点的源代码级别的开头,函数开头会进行栈相关操作,可查看保存在栈上的参数。
查看栈数据:x/3xg $rsp, 栈的开头保存了函数的返回地址,后面是参数

9、watch 与 display 指令
watch 命令是一个强大的命令,它可以用来监视一个变量或者一段内存,当这个变量或者该内存处的值发生变化时,GDB 就会中断下来
形式一:整型变量
int i;
watch i
形式二:指针类型
char *p;
watch p 与 watch *p
注意:watch p 与 watch *p 是有区别的,前者是查看 *(&p),是 p 变量本身;后者是 p 所指内存的内容。
我们需要查看地址,因为目的是要看某内存地址上的数据是怎样变化的。
如果是地址的话,我们要这样写 watch 0x8600,如果是变量的话,可以 watch tmp,
直接指定地址而不是指定变量名或符号时,要在地址前面加上

形式三:watch 一个数组或内存区间
char buf[128];
watch buf
这里是对 buf 的 128 个数据进行了监视,此时不是采用硬件断点,而是用软中断实现的。
用软中断方式去检查内存变量是比较耗费 CPU 资源的,精确地指明地址是硬件中断。

display 命令监视的变量或者内存地址,每次程序中断下来都会自动输出这些变量或内存的值。
例如,假设程序有一些全局变量,每次断点停下来我都希望 GDB 可以自动输出这些变量的最新值,
那么使用“display 变量名”设置即可。

10、finish 与 return
我们在某个函数中调试一段时间后,不需要再一步步执行到函数返回处,希望直接执行完当前函数并回到上一层调用处,
就可以使用 finish 命令。与 finish 命令类似的还有 return 命令,return 命令的作用是结束执行当前函数,
还可以指定该函数的返回值。
这里需要注意一下二者的区别:finish 命令会执行函数到正常退出该函数,
而return 命令是立即结束执行当前函数并返回,也就是说,如果当前函数还有剩余的代码未执行完毕,也不会执行了

实际调试时,还有一个 until 命令(简写为 u)可以指定程序运行到某一行停下来

11 、gdb attach到某个进程上面
gdb --pid=pid号,例如: gdb -pid=5242

结束进程
kill -9 pid

gdbserver :

目标板子上面要有gdbserver, 是一个程序,它允许宿主机可以通过网络,远程调试目标板
目标板子上:gdbserver 192.168.xx.xx:1234 ./helloworld
192.168.xx.xx:IP地址信息
1234:自定义端口号
./helloworld:运行要仿真的程序
此时gdbserver监听端口号1234,并等待客户端连接。

宿主机上:
$ gdb
(gdb) target remote 192.168.xx.xx:1234
Remote debugging using :1234
c #运行

target remote:远程连接到指定IP的端口
c:全速运行

gdbserver [ …]
comm:通信方式选择,可以是USB、TCP等多种方式

program:要调试的程序

gdbserver --attach
pid:是当前正在运行的进程的进程 ID。

反汇编:objdump -S a.out > 1.txt (nm命令)

多线程调试:
为了解决调试多线程程序时出现的线程切换问题,GDB 提供了一个在调试时将程序执行流锁定在当前
调试线程的命令:set scheduler-locking on。当然也可以关闭这一选项,使用 set scheduler-locking off

添加条件断点:
添加条件断点的命令是 break [lineNo] if [condition], 其中 lineNo 是程序触发断点后需要停下的位置,condition 是断点触发的条件。
例如:break 11 if i5000
添加条件断点还有一个方法就是先添加一个普通断点,然后使用“condition 断点编号断点触发条件”这样的方式来添加。添加一下上述断点
(gdb) b 11
Breakpoint 1 at 0x400514: file test1.c, line 11.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000400514 in main at test1.c:11
(gdb) condition 1 i
5000
(gdb) r

反汇编:
a、
disassemble [Function]
disassemble [Address]
disassemble [Start] [End]

disas 地址

b、
panic:

我们看到当前epc指针指向的地址为:xxx_state_context+0xb0/0x5624(偏移为0xb0),使用下面步骤:

1)反汇编命令:你的编译工具链路径/objdump -S 你的内核vmlinx或模块.ko >dump.txt

2)获取函数地址:在dump.txt中查找xxx_state_context,找到该函数地址,假如为00000034<xxx_state_context>

3)获取pc指针地址:00000034+0xb0=0x000000e4

4)定位到具体函数某一行,命令:

你的编译工具链路径/addr2line 0x000000e4 -e 你的内核vmlinx或模块.ko -f xxx_state_context

假如编译工具链中有gdb的话,可以更快捷的定位到具体代码,执行命令:

#你的编译工具链gdb路径/gdb 你的内核vmlinx或模块.ko

(gdb) b *xxx_state_context+0xb0

汇编:
编写assemble.c

/* 编译,禁用优化 */
/opt/SF9228/gcc_tool/bin/aarch64-linux-gnu-gcc -O0 assemble.c -o assemble

/* --no-show-raw-insn 不输出机器语言 */
/opt/SF9228/gcc_tool/bin/aarch64-linux-gnu-objdump -d --no-show-raw-insn assemble

[hujingzheng@localhost assemble]$ /opt/SF9228/gcc_tool/bin/aarch64-linux-gnu-objdump -d --no-show-raw-insn assemble

关于barrier()宏,jkl大师是这么说的:
CPU越过内存屏障后,将刷新自己对存储器的缓冲状态。这条语句实际上不生成任何代码,但可使gcc在
barrier()之后刷新寄存器对变量的分配。

也就是说,barrier()宏只约束gcc编译器,不约束运行时的CPU行为。 举例:
            
1        int a = 5, b = 6;
2        barrier();
3        a = b;
    
在line 3,GCC不会用存放b的寄存器给a赋值,而是invalidate b的Cache line,重新读内存中的b值,赋值给a

Unable to handle kernel paging request at virtual address ffff0000029ee000
[ 2311.891407] Mem abort info:
[ 2311.894640] ESR = 0x96000007
[ 2311.898146] EC = 0x25: DABT (current EL), IL = 32 bits
[ 2311.904078] SET = 0, FnV = 0
[ 2311.907548] EA = 0, S1PTW = 0
[ 2311.911059] Data abort info:
[ 2311.914334] ISV = 0, ISS = 0x00000007
[ 2311.918572] CM = 0, WnR = 0
[ 2311.921994] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000080d2e000
[ 2311.929116] [ffff0000029ee000] pgd=00000000b7ffa003, p4d=00000000b7ffa003, pud=00000000b7ff9003, pmd=00000000b7fe9003, pte=0101010101010101
[ 2311.942571] Internal error: Oops: 96000007 [#2] PREEMPT SMP
[ 2311.948358] Modules linked in: esc_linuxum(O)
[ 2311.953196] CPU: 0 PID: 191 Comm: gdb Tainted: G D O 5.10.0 #20
[ 2311.960429] Hardware name: Netfactory soc205 EVB (DT)
[ 2311.965744] pstate: 20000005 (nzCv daif -PAN -UAO -TCO BTYPE=–)
[ 2311.972131] pc : __arch_copy_to_user+0x100/0x158
[ 2311.977009] lr : copyout+0x90/0xa0
[ 2311.980585] sp : ffff800010f7bb80
[ 2311.984084] x29: ffff800010f7bb80 x28: ffff0000014b4490
[ 2311.989754] x27: ffff800010f7bd40 x26: ffff000037122098
[ 2311.995418] x25: ffff800010f7bd30 x24: 0000000000000000
[ 2312.001062] x23: 0000000000000000 x22: ffff0000029ee000
[ 2312.006713] x21: 0000000000001000 x20: 0000000000001000
[ 2312.012366] x19: ffff800010f7bd40 x18: 0000000000000000
[ 2312.018011] x17: 0000000000000000 x16: 0000000000000000
[ 2312.023659] x15: 0000000000000000 x14: 0000000000000000
[ 2312.029297] x13: 0000000000000000 x12: 0000000000000000
[ 2312.034937] x11: 0000000000000000 x10: 0000000000000000
[ 2312.040579] x9 : 0000000000000000 x8 : 0000000000000000
[ 2312.046219] x7 : 0000000000000000 x6 : 0000000034d42e50
[ 2312.051859] x5 : 0000000034d43e50 x4 : 0000000000000000
[ 2312.057509] x3 : 0000000034d42e50 x2 : 0000000000000f80
[ 2312.063156] x1 : ffff0000029ee000 x0 : 0000000034d42e50
[ 2312.068792] Call trace:
[ 2312.071496] __arch_copy_to_user+0x100/0x158
[ 2312.076001] copy_page_to_iter+0xe0/0x3a0
[ 2312.080289] generic_file_buffered_read+0x308/0x8e0
[ 2312.085408] generic_file_read_iter+0xf8/0x188
[ 2312.090118] new_sync_read+0x100/0x178
[ 2312.094118] vfs_read+0x13c/0x1c8
[ 2312.097680] ksys_read+0x64/0xe8
[ 2312.101150] __arm64_sys_read+0x18/0x20
[ 2312.105263] el0_svc_common.constprop.3+0x98/0x1e8
[ 2312.110286] do_el0_svc+0x18/0x20
[ 2312.113861] el0_svc+0x18/0x48
[ 2312.117148] el0_sync_handler+0x88/0xb0
[ 2312.121211] el0_sync+0x140/0x180
[ 2312.124845] Code: d503201f d503201f d503201f d503201f (a8c12027)
[ 2312.131193] —[ end trace 9521a398016246ee ]—
Segmentation fault

[ 2312.124845] Code: d503201f d503201f d503201f d503201f (a8c12027)

code表示kernel panic时PC地址指向的地址里的数据,用括号括起来是PC地址指向的地址的instruction,前面几个是PC地址前面的几个instruction

运行地址的改变
1、直接指定地址并调用,c语言中的if或for语句等进行条件判断时会用到这种方式。
2、指定一块内存区域,其中保存了跳转地址
3、执行了ret指令,用于函数结束时返回调用者函数//栈被破坏

栈被破坏是挂机调试方式:将错误的地址当做数据,寻找复制该数据的地方。
这种调查中,很重要的是需要怀疑数据是否为字符串的一部分,因为错误的将数据写入地址的典型
情况之一就是字符串复制。可查看堆栈指针附近数据

//查看sp堆栈指针前15字节地址出开始的30个字节内容
(gdb) x/30c $esp -15

(gdb) p (char *)$esp -20

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值