使用objdump、readelf和addr2line来定位coredump时的exception异常或kernel panic异常
模拟用户层奔溃
发送ABORT信号量来创造一个coredump或tombstone文件:
# kill -6 822
# cat /data/tombstones/tombstone_00
*** Aborted
Register dump:
R0: fffffffc R1: 00000000 R2: 00000002 R3: 00000000
R4: 00000000 R5: bede5c88 R6: 0002f5c8 R7: 000000a2
R8: 01ce8014 R9: 00000000 SL: 47728000 FP: 00000000
IP: 00000000 SP: bede5c58 LR: b6f798c0 PC: 478a7e64
CPSR: 800e0010
Trap: 00000000 Error: 00000000 OldMask: 00000000
Addr: 00000000
Backtrace:
/lib/libpthread.so.0(__nanosleep+0x44)[0x478a7e64]
/usr/bin/avinserver[0x10f64]
/lib/libc.so.6(__libc_start_main+0x114)[0x4776f8fc]
Memory map:
00008000-00027000 r-xp 00000000 103:00 1145 /usr/bin/avinserver
0002e000-00037000 rw-p 0001e000 103:00 1145 /usr/bin/avinserver
01ce8000-01d09000 rw-p 00000000 00:00 0 [heap]
47700000-47720000 r-xp 00000000 103:00 566 /lib/ld-2.20.so
47727000-47728000 r–p 0001f000 103:00 566 /lib/ld-2.20.so
47728000-47729000 rw-p 00020000 103:00 566 /lib/ld-2.20.so
47730000-4774d000 r-xp 00000000 103:00 597 /lib/libgcc_s.so.1
4774d000-47754000 —p 0001d000 103:00 597 /lib/libgcc_s.so.1
47754000-47755000 rw-p 0001c000 103:00 597 /lib/libgcc_s.so.1
47758000-47882000 r-xp 00000000 103:00 579 /lib/libc-2.20.so
47882000-4788a000 —p 0012a000 103:00 579 /lib/libc-2.20.so
4788a000-4788c000 r–p 0012a000 103:00 579 /lib/libc-2.20.so
4788c000-4788e000 rw-p 0012c000 103:00 579 /lib/libc-2.20.so
4788e000-47890000 rw-p 00000000 00:00 0
47898000-478ad000 r-xp 00000000 103:00 628 /lib/libpthread-2.20.so
478ad000-478b4000 —p 00015000 103:00 628 /lib/libpthread-2.20.so
478b4000-478b5000 r–p 00014000 103:00 628 /lib/libpthread-2.20.so
478b5000-478b6000 rw-p 00015000 103:00 628 /lib/libpthread-2.20.so
我们看Backtrace部分最后一个调用位置:
/lib/libpthread.so.0(__nanosleep+0x44)[0x478a7e64]
这里的0x478a7e64为运行时地址,该地址对应/lib/libpthread.so.0里面的__nanosleep()函数偏移0x44位置。
不管这个运行时地址,我们用编译主机的工具链来objdump或readelf来查找__nanosleep()函数的链接地址(注:链接地址和运行时地址是不一样的,内核会有一个重定位的功能,把链接地址挪到运行时地址。如果没有用调试模式编译来加入符号表,则可能没法定位,除非直接理解无符号的纯汇编):
$ ../build/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-readelf -s ../build/tmp/sysroots/ac8317/lib/libpthread-2.20.so |grep __nanosleep
175: 0000fd00 136 FUNC WEAK DEFAULT 12 __nanosleep@@GLIBC_2.4
1269: 0000fce0 28 FUNC LOCAL DEFAULT 12 __nanosleep_nocancel
1590: 0000fd00 136 FUNC WEAK DEFAULT 12 __nanosleep
$ ../build/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-objdump -S ../build/tmp/sysroots/ac8317/lib/libpthread-2.20.so |grep -A 30 “<__nanosleep>”
0000fd00 <__nanosleep>:
fd00: e59fc080 ldr ip, [pc, #128] ; fd88 <__local_syscall_error+0x20>
fd04: e79fc00c ldr ip, [pc, ip]
fd08: e33c0000 teq ip, #0
fd0c: e52d7004 push {r7} ; (str r7, [sp, #-4]!)
fd10: 1a000005 bne fd2c <__nanosleep+0x2c>
fd14: e3a070a2 mov r7, #162 ; 0xa2
fd18: ef000000 svc 0x00000000
fd1c: e49d7004 pop {r7} ; (ldr r7, [sp], #4)
fd20: e3700a01 cmn r0, #4096 ; 0x1000
fd24: 312fff1e bxcc lr
fd28: ea00000e b fd68 <__local_syscall_error>
fd2c: e92d4003 push {r0, r1, lr}
fd30: ebfffba4 bl ebc8 <__pthread_enable_asynccancel>
fd34: e1a0c000 mov ip, r0
fd38: e8bd0003 pop {r0, r1}
fd3c: e3a070a2 mov r7, #162 ; 0xa2
fd40: ef000000 svc 0x00000000
fd44: e1a07000 mov r7, r0
fd48: e1a0000c mov r0, ip
fd4c: ebfffbcc bl ec84 <__pthread_disable_asynccancel>
fd50: e1a00007 mov r0, r7
fd54: e49de004 pop {lr} ; (ldr lr, [sp], #4)
fd58: e49d7004 pop {r7} ; (ldr r7, [sp], #4)
fd5c: e3700a01 cmn r0, #4096 ; 0x1000
ret
fd60: 312fff1e bxcc lr
fd64: eaffffff b fd68 <__local_syscall_error>
0000fd68 <__local_syscall_error>:
T_PSEUDO_END (SYSCALL_SYMBOL)
可以看到,__nanosleep()函数链接地址在0x0000fd00位置。把该链接地址加上tombstone里面的偏移0x44,即0x0000fd44为出错的位置。其实从objdump已经可以看出出错的是
fd44: e1a07000 mov r7, r0
这一行。
根据0x0000fd44用addr2line来定位源代码的文件和行号:
$ ../build/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-addr2line -e ../build/tmp/sysroots/ac8317/lib/libpthread-2.20.so -a -f -p 0x0000fd44
0x0000fd44: $a at /extended/disk9/android19/maxshu/linux_compile/linux_alios_bm/sources/build/tmp/work/armv7a-vfp-neon-poky-linux-gnueabi/glibc/2.20-r0/git/nptl/../sysdeps/unix/syscall-template.S:81
可以看到是syscall-template.S的第81行。
打开该源文件看看:
76 #else
77
78 /* This is a “normal” system call stub: if there is an error,
79 it returns -1 and sets errno. */
80
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
82 ret
83 T_PSEUDO_END (SYSCALL_SYMBOL)
84
85 #endif
可以看到是:
T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
这里出错的。
其实__nanosleep()看源代码,就是用syscall方式进入了内核态,从用户层跟不到了:
/* Pause execution for a number of nanoseconds. */
int
__nanosleep (const struct timespec *requested_time,
struct timespec *remaining)
{
return SYSCALL_CANCEL (nanosleep, requested_time, remaining);
}
hidden_def (__nanosleep)
weak_alias (__nanosleep, nanosleep)
当然该例子因为是用信号量模拟的异常,所以这个退出其实也是正常的系统调用。
模拟内核奔溃
用中断来触发panic错误,导致内核奔溃,重启后会有coredump或上次内核错误的log:
# echo c > /proc/sysrq-trigger
‘Kernel Exception: sysrq_handle_crash+0x14/0x20’ ..collect DB
.[bt]: [<c0008524>] do_DataAbort+0x84/0x104
.[bt]: [<c0012718>] __dabt_svc+0x38/0x60
.[bt]: [<c024a650>] sysrq_handle_crash+0x14/0x20
.[bt]: [<c024acfc>] __handle_sysrq+0x88/0x124
我们看异常出现位置:
.[bt]: [<c024a650>] sysrq_handle_crash+0x14/0x20
这里的c024a650为运行时地址,该地址对应内核vmlinux.bin里面的sysrq_handle_crash()函数偏移0x14位置。
不管这个运行时地址,我们用编译主机的工具链来objdump或readelf来查找sysrq_handle_crash()函数的链接地址(注:链接地址和运行时地址是不一样的,内核会有一个重定位的功能,把链接地址挪到运行时地址):
$ ../build/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-readelf -s ../image-release/vmlinux.bin |grep sysrq_handle_crash
6449: c0248b5c 32 FUNC LOCAL DEFAULT 2 sysrq_handle_crash
$ ../build/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-objdump -S ../image-release/vmlinux.bin |grep -A 30 “<sysrq_handle_crash>”
c0248b5c <sysrq_handle_crash>:
static void sysrq_handle_crash(int key)
{
char *killer = NULL;
panic_on_oops = 1; /* force panic */
c0248b5c: e59f2014 ldr r2, [pc, #20] ; c0248b78 <sysrq_handle_crash+0x1c>
c0248b60: e3a03001 mov r3, #1
c0248b64: e5823000 str r3, [r2]
wmb();
c0248b68: f57ff04e dsb st
*killer = 1;
c0248b6c: e3a02000 mov r2, #0
c0248b70: e5c23000 strb r3, [r2]
c0248b74: e12fff1e bx lr
c0248b78: c08692f4 .word 0xc08692f4
可以看到,sysrq_handle_crash()函数链接地址在0xc0248b5c位置。把该链接地址加上panic里面的偏移0x14,即0xc0248b70为出错的位置。其实从objdump已经可以看出出错的是
c0248b70: e5c23000 strb r3, [r2]
这一行。
根据0xc0248b70用addr2line来定位源代码的文件和行号:
$ ../build/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-addr2line -e ../image-release/vmlinux.bin -a -f -p 0xc0248b70
0xc0248b70: sysrq_handle_crash at /extended/disk9/android19/maxshu/linux_compile/linux_alios_bm/atc_linux/kernel/kernel-3.18/drivers/tty/sysrq.c:141
可以看到是sysrq.c的第141行导致。
从该源文件可以看到是:
135 static void sysrq_handle_crash(int key)
136 {
137 char *killer = NULL;
138
139 panic_on_oops = 1; /* force panic */
140 wmb();
141 *killer = 1;
142 }
这里出错的,其本质还是一个正常操作,用来模拟panic方式。
当然该例子因为是用中断模拟触发的panic异常,所以这里强制panic后退出。