先看看前面写的例子test.c:
1 #include <stdio.h>
2
3 void func4()
4 {
5 char *p = NULL;
6 *p = 0x5;//出错地方;
7 }
8
9 void func3()
10 {
11 int var4 = 4;
12 }
13
14
15 void func2()
16 {
17 int var3 = 3;
18 func3();
19 func4();
20 }
21
22 void func1()
23 {
24 int var1,var2;
25 var1 = 2;
26 }
27
28 void main()
29 {
30 int var0 = 1;
31 func1();
32 func2();
33 return;
34 }
35
反汇编出来的代码test.S:
00000738 <func4>:
738: b081 sub sp, #4
73a: 2000 movs r0, #0
73c: 9000 str r0, [sp, #0]
73e: 2105 movs r1, #5
740: 7001 strb r1, [r0, #0]
742: b001 add sp, #4
744: 4770 bx lr
00000746 <func3>:
746: b081 sub sp, #4
748: 2004 movs r0, #4
74a: 9000 str r0, [sp, #0]
74c: b001 add sp, #4
74e: 4770 bx lr
00000750 <func2>:
750: b580 push {r7, lr}
752: b082 sub sp, #8
754: 2003 movs r0, #3
756: 9001 str r0, [sp, #4]
758: f7ff fff5 bl 746 <func3>
75c: f7ff ffec bl 738 <func4>
760: b002 add sp, #8
762: bd80 pop {r7, pc}
00000764 <func1>:
764: b082 sub sp, #8
766: 2002 movs r0, #2
768: 9001 str r0, [sp, #4]
76a: b002 add sp, #8
76c: 4770 bx lr
0000076e <main>:
76e: b580 push {r7, lr}
770: b082 sub sp, #8
772: 2001 movs r0, #1
774: 9001 str r0, [sp, #4]
776: f7ff fff5 bl 764 <func1>
77a: f7ff ffe9 bl 750 <func2>
77e: b002 add sp, #8
780: bd80 pop {r7, pc}
发生问题的backtrace:
Backtrace:
#00 pc 00000740 /system/bin/test
#01 pc 0000075d /system/bin/test
#02 pc 0000077b /system/bin/test
#03 pc 0001708c /system/lib/libc.so (__libc_init+84)
#04 pc 00000660 /system/bin/test
(1)addr2line
将地址转换为地址所在的文件及行数:
$ ./arm-linux-androideabi-addr2line -e symbols/test -f 00000740
func4
/proc/self/cwd/vendor/libtest/test.c:6
所以可以定位test.c第6行出现问题;
(2)nm
列出该文件的符号(函数,变量,文件等),包含名字、地址、大小;
-D, –dynamic: 只显示动态符号;
-g, –extern-only: 只显示外部符号;
-u, –undefined-only: 只显示未定义的符号;
-C, –demangle[=STYLE]: 反重整符号为可读方式自动识别格式;
-l, –line-numbers:多显示符号所在文件和行数;
$ ./arm-linux-androideabi-nm -C -l -g symbols/test
U abort
U __aeabi_memclr8
U __aeabi_memcpy
00004000 A __bss_start
U __cxa_atexit
U dladdr
00004000 A _edata
00004005 A _end
00003e3c T __FINI_ARRAY__
U fprintf
00000764 T func1 /proc/self/cwd/vendor/libtest/test.c:22
00000750 T func2 /proc/self/cwd/vendor/libtest/test.c:15
00000746 T func3 /proc/self/cwd/vendor/libtest/test.c:9
00000738 T func4 /proc/self/cwd/vendor/libtest/test.c:3
U __gnu_Unwind_Find_exidx
00003e34 T __INIT_ARRAY__
U __libc_init
0000076e T main /proc/self/cwd/vendor/external/libtest/test.c:28
00003e2c D __PREINIT_ARRAY__
U __register_atfork
U __snprintf_chk
U __stack_chk_fail
U __stack_chk_guard
U stderr
(3)objdump
查看对象文件(.so/.a或应用程序)的内容信息;
-a, –archive-headers: 显示库(*.a)成员信息
-f, –file-headers:显示obj中每个文件的整体头部摘要信息
-h, –[section-]headers:显示目标文件各个section的头部摘要信息
-x, –all-headers: 显示所有头部摘要信息
-d, –disassemble:反汇编代码段
-D, –disassemble-all: 反汇编所有段
-S, –source:反汇编出源代码,额外有debug信息,隐含-d,如果编译时有-g,效果
更明显
-t, –syms: 显示符号表
-r, –reloc: 显示重定位记录
-C, –demangle[=STYLE]: 反重整符号为可读方式
$ ./arm-linux-androideabi-objdump -f symbols/test
symbols/test: file format elf32-littlearm
architecture: arm, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x00000600
(4)readelf
看elf文件(.so/.a或应用程序)的内容信息;
-a, –all: 显示所有可显示的内容
-h –file-header: 显示ELF文件头
-l –segments: 显示程序头组
-S –sections: 显示节头组
-t: 显示节头细节
-e –headers: 等效于-h -l -S
-s –syms: 显示符号表
-n –notes: 显示内核说明
-r –relocs: 显示重定位信息
-u –unwind: 显示解栈信息
-d –dynamic: 显示动态节
-p –string-dump=
./arm-linux-androideabi-readelf -h symbols/test
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x600
Start of program headers: 52 (bytes into file)
Start of section headers: 96780 (bytes into file)
Flags: 0x5000200, Version5 EABI, soft-float ABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 39
Section header string table index: 38
(5)c++filt
反重整C++符号为可读方式;
$ ./arm-linux-androideabi-c++filt _ZN20android_audio_legacy22AudioPolicyManagerBase17set
SystemPropertyEPKcS2
android_audio_legacy::AudioPolicyManagerBase::setSystemProperty(char const*, char const*)
(5)gdb
gdb调试在前获取带有符号的bin文件,从AEE log当中获取的coredump来调试,这里主要讲静态调试,当发生coredump的时候,通过带有符号信息的bin文件和coredump来分析问题;
$ ./arm-linux-androideabi-gdb symbols/test PROCESS_COREDUMP
(gdb) set solib-search-path out/target/product/xxx/symbols/system/lib/
(gdb) set solib-absolute-prefix out/target/product/xxx/symbols/
这样设置之后就可以通过gdb调试这个程序了;
(gdb) bt //堆栈信息
#0 0xaaaaa740 in func4 () at vendor/libtest/test.c:6 //当前栈;
#1 0xaaaaa760 in func2 () at vendor/libtest/test.c:19
#2 0xaaaaa77e in main () at vendor/libtest/test.c:32
info f:打印出更为详细的当前栈层的信息(目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等):
(gdb) frame 0
#0 0xaaaaa740 in func4 () at vendor/libtest/test.c:6
6 in vendor/libtest/test.c
(gdb) info frame
Stack level 0, frame at 0xfffef9d0:
pc = 0xaaaaa740 in func4 (vendor/libtest/test.c:6); saved pc 0xaaaaa760
called by frame at 0xfffef9e0
source language c.
Arglist at 0xfffef9cc, args:
Locals at 0xfffef9cc, Previous frame's sp is 0xfffef9d0
(gdb) frame 1
#1 0xaaaaa760 in func2 () at vendor/libtest/test.c:19
19 in vendor/libtest/test.c
(gdb) info frame
Stack level 1, frame at 0xfffef9e0:
pc = 0xaaaaa760 in func2 (vendor/libtest/test.c:19); saved pc 0xaaaaa77e
called by frame at 0xfffef9f0, caller of frame at 0xfffef9d0
source language c.
Arglist at 0xfffef9d0, args:
Locals at 0xfffef9d0, Previous frame's sp is 0xfffef9e0
Saved registers:
r7 at 0xfffef9d8, lr at 0xfffef9dc
info args:打印出当前函数的参数名及其值。info locals:打印出当前函数中所有局部变量及其值。
(gdb) info locals
p = 0
info registers:打印出所有寄存器的值;
//frame 0
(gdb) info registers
r0 0x0 0
r1 0x5 5
r2 0xfffefa2c 4294900268
r3 0x0 0
r4 0xaaaaa76f 2863310703
r5 0xfffefa24 4294900260
r6 0x1 1
r7 0xfffefa2c 4294900268
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0xfffefa00 4294900224
r12 0xf759b85c 4149852252
sp 0xfffef9d0 0xfffef9d0
lr 0xaaaaa761 -1431656607
pc 0xaaaaa760 0xaaaaa760 <func2+16>
cpsr 0x70030 458800
//frame 1
(gdb) info registers
r0 0x0 0
r1 0x5 5
r2 0xfffefa2c 4294900268
r3 0x0 0
r4 0xaaaaa76f 2863310703
r5 0xfffefa24 4294900260
r6 0x1 1
r7 0xfffefa2c 4294900268
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0xfffefa00 4294900224
r12 0xf759b85c 4149852252
sp 0xfffef9cc 0xfffef9cc
lr 0xaaaaa761 -1431656607
pc 0xaaaaa740 0xaaaaa740 <func4+8>
cpsr 0x70030 458800
根据上面的信息可以大概画出当前栈的情况:
查看存储器状态:
(gdb) p a //定义了一个数组:a[3]
$1 = {5, 5, 5}
(gdb) p &a
$2 = (int (*)[3]) 0xfffef9d8
(gdb) p &a[2]
$4 = (int *) 0xfffef9e0
(gdb) p &a[1]
$5 = (int *) 0xfffef9dc
(gdb) p &a[0]
$6 = (int *) 0xfffef9d8
(gdb) x/5wx 0xfffef9d8 //显示5个单元,w四字节一个单元,x十六进制显示
0xfffef9d8: 0x00000005 0x00000005 0x00000005 0x00000005
0xfffef9e8: 0xfffefa2c
/*b表示单字节,h表示双字节,w表示四字节,g表示八字节*/
/*
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
*/