1 空指针core dump文件分析
把core文件从开发板拷贝到本地主机,用gdb命令打开,命令格式为:
aarch64-unknown-nto-qnx7.1.0-gdb executable-file core-file
打开一个因为空指针问题导致的coredump文件:
可以发现是SIGSEGV信号导致的:
出错地址为:
在 gdb中输入 info sharedlibrary:
可以发现0x0000000078cf3ff8 这个出错地址比程序加载的动态库libqcx_max96722_avm.so的起始地址大,因为这边没有加载libqcx_max96722_avm.so 库到gdb中,所以gdb只知道该库加载的起始地址,不知道该库的大小。可以使用readelf 工具,来查看该动态库在内存中的镜像有多大,利用 readelf -l 命令,查看动态库的program header:
第一列type指的是该动态库的属性,LOAD 属性说明该部分运行的时候会加载进系统RAM 中。该共享库在RAM中的大小即为0x0000000000008d40+0x0000000000011c50=1A990,
所以libqcx_max96722_avm.so在RAM中的结束地址为0x0000000078cf2000+1A990=78d0c990, 所以程序的出错地址就在libqcx_max96722_avm.so程序中。
利用objdump命令,对libqcx_max96722_avm.so 动态库进行反汇编:
aarch64-unknown-nto-qnx7.1.0-objdump -S libqcx_max96722_avm.so
反汇编生成的文件汇编地址是从0开始的,而出错地址和反汇编地址之间,会有一个offset, 出错地址在libqcx_max96722_avm.so反汇编文件中的地址需要减去该动态库在RAM中的起始地址
0x0000000078cf3ff8 - 0x0000000078cf2000=1ff8,出错地址在反汇编文件中为1ff8:
1ff8中的汇编代码的意思是把0写入x0寄存器指向的地址中,用info reg看一下x0寄存器值:
向地址为0的内存写入了0,所以是空指针操作导致了coredump。
观察汇编发现该x0的值就是detect_null_pointer的参数,而detect_null_pointer 又由MAX96722_AVMIoctl调用:
在0x20a8的位置,从 sp+88的地址处取出值,传给x0,而 sp+88地址的值,又是在0x2068处,写入的0值,
这应该是在栈中初始化的一个指针,对比源码:
2 double free core dump文件分析
把core文件从开发板拷贝到本地主机,用gdb命令打开,命令格式为:
aarch64-unknown-nto-qnx7.1.0-gdb executable-file core-file
Core dump是SIGABRT 信号导致的:
并没有显示有效的出错地址:
用bt命令看一下调用栈信息:
可以看到一些调用栈信息,但并不完整,我们从头开始追溯整个调用栈:
用info all-registers看一下寄存器信息:
简单介绍一下arm64通用寄存器的基本作用。
• X0~X7:用于传递子程序参数和结果,使用时不需要保存,多余参数采用堆栈传递,64位返回结果采用X0表示,128位返回结果采用X1:X0表示。
• X8:用于保存子程序返回地址, 尽量不要使用 。
• X9~X15:临时寄存器,使用时不需要保存。
• X16~X17:子程序内部调用寄存器,使用时不需要保存,尽量不要使用。
• X18:平台寄存器,它的使用与平台相关,尽量不要使用。
• X19~X28:临时寄存器,使用时必须保存。
• X29:帧指针寄存器fp,用于连接栈帧,使用时需要保存。
• X30:链接寄存器LR
• X31:堆栈指针寄存器SP
这边重点关注sp 寄存器,这是栈的底部,以及x29寄存器,用来存储栈的顶部。
先看一下出错的pc地址,为0x782e8430
info sharedlibrary 看一下动态库的地址分布:
该地址在libc.so.5 的虚拟地址范围中(0x0000000078293000 ~0x0000000078359ee8)。
c语言函数调用的时候,整个栈空间大概是这样子的:
发生core dump时,fp的地址为0xf13ebc0, 记录该 fp为fp1,查看 该地址的值:
0x0f13ec20 为上一个fp的地址,记录为fp2,函数调用返回地址存在0xf13ebc0 +8=0xf13ebc8,为 lr2:
arm64一条指令占4个字节,PC地址还需要-4,为0x782ce77c-4=0x782ce778,为pc2,该地址还在标准库函数中,运用上面的规律,接着往前回溯: fp3为0x0f13ec60, lr3为*(0x0f13ec60+8)=0x78a65190, pc3为0x78a65190-4=0x78A6518C
0x78A6518C的地址在libqcx_max96712.so中,栈回溯到这边可以停止了。整个栈的内存分布如下图:
下面看一下0x78A6518C这个地址在libqcx_max96712.so中的位置,0x78A6518C-0x0000000078a5b000=a18c
查看反汇编文件中a18c是什么代码:
这是一个free的函数调用,其中参数为x0, sp+88的地址获得,再往前看又是一个free,也是一样的参数,说明该代码free了两次,存在问题,源码如下: