1、入参与返回值
| CPU | 入参 | 返回值 |
|---|---|---|
| x86 | ebp+8, ebp+12, ebp+16, ebp+20 | eax |
| x86_64 | rdi, rsi, rdx, rcx, r8, r9 | rax |
| arm64 | x0, x1, x2, x3, x4, x5, x6, x7 | x0 |
返回值需要在执行finish后查看
2、常见命令
gdb -q bin // 启动时不显示提示信息
handle SIG35 SIG36 SIGUSR2 nostop noprint // 屏蔽某些信号
set height 0 // 不限制输出行数
set print pretty on // 美化结构体打印
i thread // 查看所有线程信息
thread threadno // 切换到指定线程
thread apply all bt // 查看所有线程栈信息
printf "Line[%u], No[%u]\n", $rdi, $rsi // 打印信息
set print thread-events off // 不显示线程启动和退出信息
call getpid() // 获取拉起进程的pid
shell date +%H:%M:%S.%N // 加shell可以执行linux命令
i r // 查看寄存器信息
generate-core-file // 生成core文件
bt n(-n) // 显示栈顶(栈底)的n层栈
x addr // 解析addr对应的函数
x/s str // 显示str对应的字符串
x/nbx // 显示n个单字节
x/nhx // 显示n个双字节
x/nwx // 显示n个4字节
x/ngx // 显示n个8字节
frame n // 切换到第n层栈帧(切换栈帧后i r查看的寄存器可能并不可信)
disassemble func // 反汇编函数
disassemble func, +n // 反汇编函数的指定n字节
disassemble /m func // 显示源码和汇编(编译选项要加-g)
disassemble /r func // 显示汇编和机器码
disassemble /mr func // 显示源码、汇编和机器码
watch *addr // 监控地址被写
rwatch *addr // 监控地址被读
awatch *addr // 监控地址被读写
ptype /o struct xxx_type // 查看结构体成员的偏移量
whatis xxx // 查看变量xxx的结构体名称
n // 执行1行
ni // 执行1条指令
ptype /o pthread // 查看线程结构体的各个字段
info proc mappings // 查看maps信息
set disassembly-flavor intel // 转换为intel格式的汇编
set disassembly-flavor att // 转换为att格式的汇编(默认格式)
set max-value-size unlimited // 解除gdb的变量显示设置
3、条件断点
x86 32位: b func if *(unsigned int*)($ebp+8) == 226 && *(unsigned int*)($ebp+16) == 206
x86 64位: b func if $rdi == 226 && $rdx == 206
arm 64位: b func if $x0 == 226 && $x2 == 206
线程断点:
b func thread threadno if condition
b func if $_thread == threadno
字符断点: b func if *(char*)($ebp+8)=='A'
4、查看所有线程栈信息
gdb attach [pid]
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
i thread
thread apply all bt
det
q
5、解决attach读取较大dbg符号文件导致丢心跳问题
gdb -q xxx.bin
attach pid
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
set height 0
det
q
6、查看调用栈
gdb attach [pid]
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
b func
command
bt
det
q
end
c
7、抓取异常时刻的调用栈和寄存器
gdb attach [pid]
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
c
bt
i r
8、查看入参和返回值
gdb attach [pid] ----------//进程的pid由ps -ef | grep xxx查询到
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
define oops
bt ----------// 显示调用栈
i r ----------// 显示寄存器信息,主要看入参,x86 32位进程看入参要替换成x/8wx $ebp+8
fin ----------// 表示执行完当前打断点的函数func
i r ----------// 在fin之后表示显示返回值,x86,x86_64,arm的返回值依次对应eax,rax,x0
det
q
end
b func ----------// func即打断点的函数
command
oops
end
c
9、查看出参
gdb attach `pidof demo`
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
define ops
bt
i r
x/s $rdi
p $rsi
set $a=$rdx
fin
x/5wx $a
x/s $a
p *(unsigned int*)($a+16)
det
q
end
b func
command
ops
end
c
10、在函数func2执行之后再执行函数func1
gdb attach `pidof demo`
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
b func1
command
bt
det
q
end
disable 1
b func2
command
enable 1
bt
c
end
c
11、重定向到linux屏幕
# 重定向后,调用printf的打印会输出到指定的屏幕
gdb attach [pid]
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
call (int)close(1)
call (int)close(2)
# 此处的路径用tty查询
call (int)("/dev/tty1", 2)
detach
q
12、扫描core文件1
define mem_scan
set logging file xxx.txt
set logging on
set logging overwrite on
set height 0
set print pretty on
set $g_index = 0
while (xxx)
printf "**************************\n"
// 具体扫描实现
printf "**************************\n"
set = $g_index + 1
end
set logging off
end
mem_scan
13、扫描core文件2
gdb attach [pid]
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
# 数组
set $array = {1, 3, 5, 7, 9}
set $num = sizeof($array) / 4
set $index = 0
while ($index < $num)
set $linkNo = $array[$index]
set $tmp = *(unsigned long*)&g_state + $linkNo * 8
set $plane = *(unsigned long*)$tmp
printf "Plane[%u]\n", $plane
set $index = $index + 1
end
det
q
14、内存搜索
搜索某块内存中的value
find [/sn] start_addr, +len, val1 [, val2, ...]
find [/sn] start_addr, end_addr, val1 [, val2, ...]
s:表示搜索value所占的字节数,b/h/w/g
n:表示最多输出n个查找结果
具体示例
0x7f55fa12ce70: 0x12345678 0x00000000 0x00000000 0x12340000
0x7f55fa12ce80: 0xdeaddead 0x00000000 0xdeaddead 0x00000000
(gdb) find /w2 0x7f55fa12ce70, +0x20, 0xdeaddead
0x7f55fa12ce80
0x7f55fa12ce88
2 patterns found.
(gdb) find /w2 0x7f55fa12ce70, 0x7f55fa12ce90, 0xdeaddead
0x7f55fa12ce80
0x7f55fa12ce88
2 patterns found.
15、gdb shell脚本
#!/bin/bash
function RunGDB()
{
local cmdFile=gdb.txt
echo "$@" > "$cmdFile"
gdb -q -batch -x "$cmdFile" 2>/dev/null
rm "$cmdFile"
}
function AttachByPid()
{
local pid=$1
local gdbCmdPre="
attach $pid
set height 0
set print thread-events off
set print inferior-events off
handle SIG35 SIG36 SIGUSR2 nostop noprint"
local gdbCmd="$gdbCmdPre
b FuncD"'
command
bt
i r $rsp $rbp
set $tmprbp = $rbp
set $index = 0
while $index < 4
set $nextrbp = *(unsigned long*)$tmprbp
set $func = *(unsigned long*)($tmprbp + 8)
printf "rbp[0x%lx], func[0x%lx]\n", $tmprbp, $func
set $tmprbp = $nextrbp
set $index = $index + 1
end
det
q
end
c'
while true
do
RunGDB "$gdbCmd"
sleep 2
done
}
AttachByPid "$@"
实际使用需要修改commmad和end之间的内容。
shell脚本2
# cmd.gdb
set height 0
handle SIG35 SIG36 SIGUSR2 nostop noprint
i thread
thread apply all bt
det
q
gdb attach pidof test --batch --command=cmd.gdb
16、p errno
gdb 在p errno 时报错
Cannot find thread-local storage for LWP 1181993, shared library /usr/lib64/libc.so.6
Cannot find thread-local variables on this target
修改成p *(unsigned int*)((void *)__errno_location())
0x0000000000401210 <+118>: callq 0x401030 <__errno_location@plt>
17、compile
# 编译动态库(--prefix指定生成gdb的路径)
./configure --prefix=xxx
make -j8
# 编译静态库
./configure --prefix=xxx --disable-interprocess-agent
make LDFLAGS=-static -j8
18、advance 用法
以下代码为例
#include <cstdio>
static void TestGdbAdvance()
{
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
printf("sum = %d\n", sum);
return;
}
int main()
{
TestGdbAdvance();
return 0;
}
adv test.cpp:9 可以直接跳出for循环,执行到第9行,相当于下面的效果,区别是adv没有断点。
b test.cpp:9
c
19、tips
frame切换栈帧后,易失性寄存器的值不准确。
总而言之,一些挥发性寄存器的值可能随着栈帧的展开而不断的累加错误。也就是这里所说的:如果栈帧越靠外层,这些挥发性寄存器的值就越容易不准确。
<https://www.cnblogs.com/tsecer/p/11371549.html>