环境准备:vmlinux和SYS_MINI_RDUMP
examine 命令(简写是 x )来查看内存地址中的值。
x/<n/f/u> <addr>
n 是一个正整数,表示显示内存的长度,从当前地址向后显示几个地址的内容。
f 表示显示的格式,
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。
<addr>表示一个内存地址。
n/f/u三个参数可以一起使用。
x /nfu 0x<addr>:查看内存地址中的值。
f 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。
u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节
GDB实现机制
gdb、glibc和linux都是属于GNU框架之下,gdb和strace命令一样,都是基于linux内核提供的ptrace系统调用实现的。
ptrace(process trace)系统调用允许父进程监视或者操控子进程的内部执行状态,修改子线程的内存和寄存器,因此它通常被gdb或者strace这类跟踪调试或者代码分析的工具使用。
gdb设置断点的原理是插桩,一段代码执行到桩处,则触发SIGTRAP(Trace/breakpoint trap)信号量
比如x/128xg 0xffffff800852ff78,具体如下:
addr2line/gdb/objdump/c++filt
用于解析backtrace,工具路径: addr2line/objdump/ c++filt
直接启动gdb,进入gdb命令行,然后将vmlinux和SYS_MINI_RDUMP放到gdb所在的目录中,直接输入如下命令:
file vmlinux
core SYS_MINI_RDUMP
disassemble
list
i r(打印寄存器)
p $pc(打印pc)
disassemble 0xffffffc00009713c
disassemble $pc
f 1
up/down
list __sync_icache_dcache
p page
bt
gdb) bt
#0 0xffffff800852ffa0 in sysrq_handle_crash (key=<optimized out>)
at ap/src/kernel/linux/v4.xx/drivers/tty/sysrq.c:147
#1 0xffffff8008530670 in __handle_sysrq (key=99, check_mask=false)
at ap/src/kernel/linux/v4.xx/drivers/tty/sysrq.c:560
#2 0xffffff8008530c18 in write_sysrq_trigger (file=<optimized out>, buf=<optimized out>, count=2, ppos=<optimized out>)
at ap/src/kernel/linux/v4.xx/drivers/tty/sysrq.c:1105
#3 0xffffff80082ebd78 in proc_reg_write (file=0xffffffc00f748700, buf=0x203c63e0 <error: Cannot access memory at address 0x203c63e0>, count=2, ppos=0xffffff800c0a3e40)
at ap/src/kernel/linux/v4.xx/fs/proc/inode.c:245
#4 0xffffff8008275b94 in __vfs_write (file=0xffffffc00f748700, p=0x203c63e0 <error: Cannot access memory at address 0x203c63e0>, count=2, pos=0xffffff800c0a3e40)
at ap/src/kernel/linux/v4.xx/fs/read_write.c:487
#5 0xffffff8008275e9c in vfs_write (file=0xffffffc00f748700, buf=0x203c63e0 <error: Cannot access memory at address 0x203c63e0>, count=2, pos=0xffffff800c0a3e40)
at ap/src/kernel/linux/v4.xx/fs/read_write.c:551
#6 0xffffff800827618c in ksys_write (fd=<optimized out>, buf=0x203c63e0 <error: Cannot access memory at address 0x203c63e0>, count=2)
at ap/src/kernel/linux/v4.xx/fs/read_write.c:603
#7 0xffffff800827621c in __do_sys_write (count=<optimized out>, buf=<optimized out>, fd=<optimized out>)
at ap/src/kernel/linux/v4.xx/fs/read_write.c:615
#8 __se_sys_write (count=<optimized out>, buf=<optimized out>, fd=<optimized out>)
at ap/src/kernel/linux/v4.xx/fs/read_write.c:612
#9 __arm64_sys_write (regs=0xffffff800c0a3ec0) at ap/src/kernel/linux/v4.xx/fs/read_write.c:612
#10 0xffffff8008099ae0 in __invoke_syscall (syscall_fn=<optimized out>, regs=<optimized out>)
at ap/src/kernel/linux/v4.xx/arch/arm64/kernel/syscall.c:48
#11 invoke_syscall (syscall_table=<optimized out>, sc_nr=<optimized out>, scno=<optimized out>, regs=<optimized out>)
at ap/src/kernel/linux/v4.xx/arch/arm64/kernel/syscall.c:48
#12 el0_svc_common (regs=0xffffff800c0a3ec0, scno=<optimized out>, syscall_table=0xffffff8008c016b0 <sys_call_table>, sc_nr=<optimized out>)
at ap/src/kernel/linux/v4.xx/arch/arm64/kernel/syscall.c:114
#13 0xffffff8008099b9c in el0_svc_handler (regs=0xffffff800c0a3ec0)
at ap/src/kernel/linux/v4.xx/arch/arm64/kernel/syscall.c:160
#14 0xffffff8008084cc8 in el0_svc () at ap/src/kernel/linux/v4.xx/arch/arm64/kernel/entry.S:939
最后的bt是查看当前的调用栈。
gdb 查看bss data text
(gdb) info files
Symbols from "vmlinux".
Local core dump file:
`SYS_MINI_RDUMP', file type elf64-littleaarch64.
0xffffff8009c02000 - 0xffffff8009c04000 is load2
0xffffff8009992000 - 0xffffff800999a000 is load3
0xffffff800c0a0000 - 0xffffff800c0a4000 is load4
0xffffff800982e000 - 0xffffff8009836000 is load5
0xffffff8009888000 - 0xffffff8009890000 is load6
0xffffffc02dc58000 - 0xffffffc02dc60000 is load7
0xffffff800852c000 - 0xffffff8008534000 is load8
0xffffffc03d0ef000 - 0xffffffc03d0f7000 is load9
0xffffffc00f745000 - 0xffffffc00f74d000 is load10
0xffffff800980b000 - 0xffffff8009814000 is load11
0xffffffc00e7a8000 - 0xffffffc00e7b0000 is load12
0xffffff80090ec000 - 0xffffff80090f4000 is load13
0xffffff8008bfe000 - 0xffffff8008c06000 is load14
0xffffffc03ef41000 - 0xffffffc03ef49000 is load15
Local exec file:
`vmlinux', file type elf64-littleaarch64.
Entry point: 0xffffff8008080000
0xffffff8008080000 - 0xffffff8008081000 is .head.text
0xffffff8008081000 - 0xffffff8008bf6f88 is .text
0xffffff8008bf6f88 - 0xffffff8008bf6fe8 is .plt
0xffffff8008c00000 - 0xffffff80090fe65c is .rodata
0xffffff80090fe660 - 0xffffff80090ffb10 is .init.eh_frame
0xffffff80090ffb10 - 0xffffff8009101bb0 is .pci_fixup
0xffffff8009101bb0 - 0xffffff800910ce58 is __ksymtab
0xffffff800910ce58 - 0xffffff8009117c00 is __ksymtab_gpl
0xffffff8009117c00 - 0xffffff800911d554 is __kcrctab
0xffffff800911d554 - 0xffffff8009122c28 is __kcrctab_gpl
0xffffff8009122c28 - 0xffffff800915a171 is __ksymtab_strings
0xffffff800915a178 - 0xffffff800915de18 is __param
0xffffff800915de18 - 0xffffff800915e000 is __modver
0xffffff800915e000 - 0xffffff8009160b50 is __ex_table
0xffffff8009160b50 - 0xffffff8009160b8c is .notes
0xffffff8009200000 - 0xffffff800926c1e4 is .init.text
0xffffff800926c1e4 - 0xffffff8009271eb4 is .exit.text
0xffffff8009271eb4 - 0xffffff80092a1878 is .altinstructions
0xffffff80092a1878 - 0xffffff80092b65f4 is .altinstr_replacement
0xffffff80092b7000 - 0xffffff8009324bf0 is .init.data
0xffffff8009325000 - 0xffffff8009334458 is .data..percpu
0xffffff8009334458 - 0xffffff800967d268 is .rela.dyn
0xffffff8009800000 - 0xffffff8009954190 is .data
0xffffff8009954190 - 0xffffff80099541c8 is .got.plt
0xffffff80099541c8 - 0xffffff80099682ec is __bug_table
0xffffff8009968800 - 0xffffff800996880c is .mmuoff.data.write
0xffffff8009969000 - 0xffffff8009969008 is .mmuoff.data.read
0xffffff8009969008 - 0xffffff8009969200 is .pecoff_edata_padding
0xffffff800996a000 - 0xffffff8009ba3918 is .bss
其他工具待研究:
1、Trace32
主要用到它的模拟器功能,调试方法跟gdb功能类似,不同的是T32支持图行界面,用于离线调试full-ramdump比较方便(gdb只能调试mini_rdump)
2、Crash
命令行界面,内置gdb引擎,用法跟gdb类似,主要用于离线调试full-raumdump,比如展讯平台KE标准分析工具就是这个.
其他方法可参考:
(OBJDUMP + ADD2LINE or NM + ADD2LINE)可以定位dump
./scripts/decode_stacktrace.sh vmlinux . . < kerneloops.txt(也可以定位到具体crash代码行)
其他说明:
有关Gdb和addr2line解析地址(栈帧)不匹配的处理办法:
为什么地址无法解析?
因为KASLR(Kernel Address Space Layout Randomization)。在Android O版本后,KASLR被强制打开,用来加强系统安全性问题。
KASLR简单描述就是在每次开机都会给kernel生成一个offset,所有VA都是经过offset计算的。而我们的符号表vmlinux 是没有经过offset计算,所以地址配置不上。
打开SYS_REBOOT_REASON,如果看到Kernel Offset不为0,则需要额外计算,如果是0,则不需要额外计算。
WDT status: 2 fiq step: 71 exception type: 2
Kernel Offset: 0x0
Addr2line比较简单,只需要加载时address –offset值即可。
例如:
地址换算:new addr = 0xffffff800852ff78 offset =0xffffff800852ff78- 0x1e1bc00000