背景:
用户反馈的问题,有时候我们拿到log,发现有crash问题,有堆栈打印,能看到具体出错的函数,但是无法定位具体出错的行数和内容,这个时候就需要用到反汇编辅助我们定位问题。
反汇编方法:
通过objdump反汇编so:
objdump -d ~/shared/libxxx.so > ~/aaa.s
这样反汇编的内容就会保存到aaa.s文件中
demo查看crash问题:
首先从上面的log可以看出,程序crash的原因为signal 11 也就是空指针问题,具体出错的地址是: 0x00000000000000b8, 也就是这个地址里面存放的是空。从backtrace可以看到,出错的函数是TeaInsideSdk::Engine::OnRecvMessage,在这个函数里面,调用的下一步的函数入口是出错的地址: 0x00000000000000b8。也就是说,出错的地址是一个指针,指向了一个函数。
然后我们就可以从反汇编的文件继续查看,其中出错前执行的是00000000000b2810,所以直接从此处查看反汇编。
对应的反汇编就是这段代码。
从上面的反汇编代码可以看出,首先是调用了ParseDeviceControlResponseParams函数,然后调用了notify,然后做了一系列处理,最终出错。此时需要看c++代码,定位位置:
通过TeaInsideSdk::Engine::OnRecvMessage和ParseDeviceControlResponseParams关键信息可以定位对应代码如下:
从上面的信息汇总来看,x1就是delegate_,x4就是delegate_的虚函数地址,x5就是delegate_->OnDeviceControlResult函数的入口地址。
所以,出错的原因就是OnDeviceControlResult这个函数调用失败。
查看delegate_对应的类,发现我们有新加一个虚函数,所以定位问题可能是客户拿到我们的sdk后,替换了so,但是未替换头文件导致。因为so中保存的虚函数表和头文件中不一致,所以运行出错。客户更新头文件后运行成功。
知识课堂:
crash log中的信息说明:
fault addr: (错误地址)是指在发生段错误(Segmentation Fault,信号为 SIGSEGV)时,程序试图访问的内存地址。在这个例子中
pc列表示导致段错误(SIGSEGV)的指令所在的地址。每一行对应一个栈帧(stack frame),表示调用链中的一个函数调用。栈帧从底部到顶部表示调用顺序,即底部的函数先被调用,顶部的函数后被调用
x0 至 x28:这些是ARM64架构的通用寄存器,用于存储临时数据和函数参数。
lr(链接寄存器):在函数调用过程中,这个寄存器保存了返回地址,即函数执行完毕后要跳转到的地址。在这个例子中,它的值为:0000007613cc4814。
sp(栈指针寄存器):这个寄存器指向当前栈顶的位置。在这个例子中,它的值为:000000760b41dac0。
pc(程序计数器):这个寄存器存储了当前正在执行的指令的地址。在这个例子中,它的值为:00000000000000b8。
pst(程序状态寄存器):这个寄存器包含了处理器的状态信息,如条件标志、中断使能等。在这个例子中,它的值为:0000000080001000。
汇编指令说明:
MOV - 将数据从一个位置复制到另一个位置。
JMP - 无条件跳转到指定的地址
LDR - Load Register,用于将内存中的数据加载到寄存器中
CBZ - Compare and Branch on Zero,如果寄存器的值为零,则跳转到指定的地址。
BL - Branch with Link,用于跳转到指定的地址,并将当前指令的地址保存到链接寄存器(LR)中,以便在返回时使用。
BLR - Branch with Link and Return,这是一个特殊的BL指令,用于跳转到链接寄存器(LR)中存储的地址,并从该地址处的函数返回