之前将了crash中查看结构体,这个是非常常用调试内核状态的方式,文章是《使用crash查看内核结构体》,但是大部分情况下,我们需要看的是堆栈信息。为了演示,这里根据crash中的bt信息,手动推导函数堆栈,用作熟悉crash工具的回溯栈区的方式
这里直接获取了ping的bt,如下
crash> bt 49105 PID: 49105 TASK: ffffff805e2ee580 CPU: 0 COMMAND: "ping" #0 [ffffffc0149b3830] __switch_to at ffffffc008017540 #1 [ffffffc0149b3850] __schedule at ffffffc0095162d0 #2 [ffffffc0149b38f0] schedule at ffffffc009516900 #3 [ffffffc0149b3910] schedule_timeout at ffffffc00951ac64 #4 [ffffffc0149b39a0] __skb_wait_for_more_packets at ffffffc0091a0248 #5 [ffffffc0149b3a20] __skb_recv_datagram at ffffffc0091a0e00 #6 [ffffffc0149b3a90] skb_recv_datagram at ffffffc0091a0ea0 #7 [ffffffc0149b3ac0] ping_recvmsg at ffffffc00928a7ec #8 [ffffffc0149b3b20] inet_recvmsg at ffffffc009279340 #9 [ffffffc0149b3b70] sock_recvmsg at ffffffc009188084 #10 [ffffffc0149b3ba0] ____sys_recvmsg at ffffffc009188d3c #11 [ffffffc0149b3c90] ___sys_recvmsg at ffffffc00918c410 #12 [ffffffc0149b3d80] __sys_recvmsg at ffffffc00918c7e4 #13 [ffffffc0149b3e20] __arm64_sys_recvmsg at ffffffc00918c864 #14 [ffffffc0149b3e30] el0_svc_common at ffffffc008025508 #15 [ffffffc0149b3e70] do_el0_svc at ffffffc008025690 #16 [ffffffc0149b3e80] el0_svc at ffffffc009513510 #17 [ffffffc0149b3ea0] el0_sync_handler at ffffffc009513d54 #18 [ffffffc0149b3fe0] el0_sync at ffffffc008011e14 PC: 0000007f86ec2994 LR: 000000558e2a9f6c SP: 0000007fcdff7a20 X29: 0000007fcdff7a20 X28: 00000000000000c0 X27: 0000007fcdff7ae0 X26: 0000007fcdff7bb8 X25: 000000558e2c1000 X24: 0000007fcdff7b38 X23: 0000000000000000 X22: 000000558e2c2078 X21: 0000007f870ca710 X20: 0000007fcdff7b00 X19: 0000000000000003 X18: 0000000000000001 X17: 0000007f86ec2960 X16: 000000558e2c1b48 X15: 000000007fffffde X14: 0000000000000001 X13: 0000000000000037 X12: 000000007fffffff X11: 00000012b9b749a3 X10: 0014d207f0963169 X9: 0000000000000018 X8: 00000000000000d4 X7: 00000000001d1c32 X6: 0000000029aaaaf1 X5: 0000000000000080 X4: 0000000000000001 X3: 0000007f870c9f10 X2: 0000000000000000 X1: 0000007fcdff7b00 X0: 0000000000000003 ORIG_X0: 0000000000000003 SYSCALLNO: d4 PSTATE: 60001000
我们知道crash更多是用于内核的问题排查,在使用ping的过程中,内核并没有死锁和堆栈,所以当前寄存器是用户空间的寄存器的值,这些不是很方便crash排查,但这不妨碍我们回溯内核栈,下面基于此bt的信息来回溯栈
我们知道aarch64的FP和LR寄存器他们的作用如下
同样的,我们还知道,aarch64中指令大小是32位,那么对应4字节。所以我们可以演示上述两点
根据上面的堆栈,我们知道最后是在ffffffc008017540也就是函数__switch_to中切出,那么我们知道__switch_to的x29寄存器是ffffffc0149b3830,于是我们读出其值如下
crash> rd ffffffc0149b3830 ffffffc0149b3830: ffffffc0149b3850
可以看到ffffffc0149b3850是__schedule的x29寄存器。可以发现x29具备list的特性,所以为了直接回溯,借用list指令可以一次性读出所有的x29寄存器,如下
crash> list ffffffc0149b3830 ffffffc0149b3830 ffffffc0149b3850 ffffffc0149b38f0 ffffffc0149b3910 ffffffc0149b39a0 ffffffc0149b3a20 ffffffc0149b3a90 ffffffc0149b3ac0 ffffffc0149b3b20 ffffffc0149b3b70 ffffffc0149b3ba0 ffffffc0149b3c90 ffffffc0149b3d80 ffffffc0149b3e20 ffffffc0149b3e30 ffffffc0149b3e70 ffffffc0149b3e80 ffffffc0149b3ea0 ffffffc0149b3fe0
我们批量得到了x29寄存器的值,那么我们可以知道x30寄存器是x29+8。同样因为是地址+8,我们借助list的特性,可以得到如下
crash> list -s list_head.prev ffffffc0149b3830 ffffffc0149b3830 prev = 0xffffffc0095162d4 <__schedule+692> ffffffc0149b3850 prev = 0xffffffc009516904 <schedule+68> ffffffc0149b38f0 prev = 0xffffffc00951ac68 <schedule_timeout+376> ffffffc0149b3910 prev = 0xffffffc0091a024c <__skb_wait_for_more_packets+276> ffffffc0149b39a0 prev = 0xffffffc0091a0e04 <__skb_recv_datagram+124> ffffffc0149b3a20 prev = 0xffffffc0091a0ea4 <skb_recv_datagram+60> ffffffc0149b3a90 prev = 0xffffffc00928a7f0 <ping_recvmsg+112> ffffffc0149b3ac0 prev = 0xffffffc009279344 <inet_recvmsg+76> ffffffc0149b3b20 prev = 0xffffffc009188088 <sock_recvmsg+72> ffffffc0149b3b70 prev = 0xffffffc009188d40 <____sys_recvmsg+128> ffffffc0149b3ba0 prev = 0xffffffc00918c414 <___sys_recvmsg+124> ffffffc0149b3c90 prev = 0xffffffc00918c7e8 <__sys_recvmsg+96> ffffffc0149b3d80 prev = 0xffffffc00918c868 <__arm64_sys_recvmsg+32> ffffffc0149b3e20 prev = 0xffffffc00802550c <el0_svc_common+108> ffffffc0149b3e30 prev = 0xffffffc008025694 <do_el0_svc+28> ffffffc0149b3e70 prev = 0xffffffc009513514 <el0_svc+28> ffffffc0149b3e80 prev = 0xffffffc009513d58 <el0_sync_handler+168> ffffffc0149b3ea0 prev = 0xffffffc008011e18 <el0_sync+344> ffffffc0149b3fe0 prev = 0x0
可以看到,这里的x30寄存器保存这返回地址的下一个指令,那么我们计算返回地址就是上述地址-4即可。
至此,我们通过一个例子,将crash的bt的回栈进行了解析。