Native崩溃说明 so库崩溃SIGBUS(bad memory access) 和 SIGSEGV(Invalid memory reference)

Posted Mars-xq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Native崩溃说明 so库崩溃SIGBUS(bad memory access) 和 SIGSEGV(Invalid memory reference)相关的知识,希望对你有一定的参考价值。

参考:友盟 : Native崩溃说明

一、概念说明

在应用崩溃的时候,我们将会获取到两个信息:

  • signal: 信号量,下文将会详细的说明不同的信号量及其含义

  • code: 错误码, 除了几个所有信号量(signal) 公共的错误码(code),一般不同信号量(signal)有特定的错误码(code),可以看做是信号量(signal)的补充说明。

二、信号量(signal) 和 错误码(code)说明

本节只介绍与崩溃相关的几个 signal(CrashSDK 捕获的 signal)及相关 code 的说明,更多内容见后面的参考文献或网上找资料了解。

1. SIGILL

SIG是信号名的通用前缀,ILLillegal instruction( 非法指令 ) 的缩写。

对应的数值为 4

SIGILL 是当一个进程尝试执行一个非法指令时发送给它的信号。

常见原因有:

  • CPU架构不匹配
  • .so 文件被破坏,或者代码段被破坏导致;
  • 主动崩溃,如 _builtintrap() 也会使用非法指令来实现。

si_addr 为出错的指令。

该信号量中常见的错误码说明:

Code说明
ILL_ILLOPC非法的操作码(opcode)
ILL_ILLOPN非法的操作数(operand)
ILL_ILLADR非法的寻址模式
ILL_ILLTRP非法的trap
ILL_PRVOPC特权操作码(Privileged opcode)
ILL_PRVREG特权寄存器(Privileged register)
ILL_COPROC协处理器错误
ILL_BADSTK内部堆栈错误

2. SIGBUS

SIG是信号名的通用前缀,BUS是bus error (总线错误 ) ,意味着系统检测到硬件问题后发送给进程的信号。

对应的数值为7

通常该信号的产生不是因为硬件有物理上的损坏,而是代码实现有 bug 导致 ,如地址不对齐,或者不存在的物理地址等。

si_addr 为所访问的非法地址。

该信号量中常见的错误码说明:

Code说明
BUS_ADRALN访问的地址不对齐。32位处理器一般要求指针是4字节对齐的
BUS_ADRERR访问不存在的物理地址。一般是由于 mmap 的文件发生 truncated 导致。常见于文件访问过程中,被删除或者替换;或 mmap 到内存后,继续向文件写入且导致文件 truncated,再读取时就会出现该错误;另外, mmap 且访问超过文件实际大小的空间时,也可能会出现该错误
BUS_OBJERR特定对象的硬件错误
BUS_MCEERR_ARBUS_MCEERR_AR
BUS_MCEERR_AQBUS_MCEERR_AQ

参考:浅谈CPU内存访问要求对齐的原因

SIGBUS(BUS_ADRALN) 解决方案:

在POSIX兼容的平台上, bus error (总线错误) 是系统检测到硬件问题后发送给进程的信号,但是通常该信号的产生不是因为硬件有物理上的损坏,更多的还是程序 bug 导致的。

总线错误几乎总是由于对未对齐的读或写引起的。它之所以称为总线错误是因为对未对齐的内存访问时,被阻塞的组件就是地址总线。

对齐(alignment)数据项只能存储在地址是数据项大小的整数倍的内存位置上,这样可以加速内存访问。如:访问一个8字节的double的数据时,地址只能是8的整数倍,所以存储一个double的地址只能是24,8008,但不能存储于地址1006因为它不能被8整除,只要保证这个原则,就可以保证一个原子项数据不会跨页或cache块的边界。

引起总线错误的示例:

union 

    char a[10];
    int i;
u;

int *p =(int*)&(u.a[1]);

*p =17;/*p中未对齐的地址将会引起总线错误*/

因为数组和int的联合确保了a是按照int的4字节来对齐的,所以“a+1”肯定不是int来对齐的。

3. SIGSEGV

SIG 是信号名的通用前缀, SEGV 是 segmentation violation 的缩写。

对应的数值为 11

该信号意味着一个进程执行了一个无效的内存引用,或发生段错误

si_addr 为所访问的无效地址。

该信号量中常见的错误码说明:

Code说明
SEGV_MAPERR地址不在 /proc/self/map 映射中
SEGV_ACCERR没有访问权限

SIGSEGV(SEGV_MAPERR) 解决方案 :

SIG 是信号名的通用前缀, SEGV 是 segmentation violation 的缩写

在 POSIX 兼容的平台上,SIGSEGV 是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。SIGSEGV 的符号常量在头文件 signal.h 中定义。因为在不同平台上,信号数字可能变化,因此符号信号名被使用。通常,它是信号11。

对于不正确的内存处理,如当程序企图访问 CPU 无法定址的内存区块时,计算机程序可能抛出 SIGSEGV。操作系统可能使用信号栈向一个处于自然状态的应用程序通告错误,由此,开发者可以使用它来调试程序或处理错误。
在一个程序接收到 SIGSEGV 时的默认动作是异常终止。这个动作也许会结束进程,但是可能生成一个核心文件以帮助调试,或者执行一些其他特定于某些平台的动作。
SIGSEGV可以被捕获。也就是说,应用程序可以请求它们想要的动作,以替代默认发生的动作。这样的动作可以是忽略它、调用一个函数,或恢复默认的动作。在一些情形下,忽略 SIGSEGV 导致未定义行为。
一个应用程序可能处理SIGSEGV的例子是调试器,它可能检查信号栈并通知开发者目前所发生的,以及程序终止的位置。

SIGSEGV通常由操作系统生成,但是有适当权限的用户可以在需要时使用kill系统调用或kill命令(一个用户级程序,或者一个shell内建命令)来向一个进程发送信号。

  • 闪退场景一:recorder deleteRecording 之前 先判断文件是否存在,否则会造成过度释放,解决方法:
if ([[NSFileManager defaultManager] fileExistsAtPath:self.recorder.url.path]) 
        if (![self.recorder deleteRecording])
            NSLog(@"Failed to delete %@", self.recorder.url);

  • 闪退场景二: delegate = nil

XXViewContrller设置为delegate时,当页面发生跳转时,XXViewController的对象会被释放,这是代码走到[_delegate callbackMethod],便出现crash。
解决方法有二:
1.将@property (nonatomic ,assign) id <BLELibDelegate>delegate; assign关键字改为weak
2.在XXViewControllerdelloc方法中添加:xxx.delegate = nil;

4. SIGFPE

SIG是信号名的通用前缀。FPE是floating-point exception(浮点异常)的首字母缩写拼接而成。

对应的数值为 8

该信号一般是算术运算相关问题导致的。

si_addr 为失败的指令。

该信号量中常见的错误码说明:

Code说明
FPE_INTDIV整数除以 0
FPE_INTOVF整数溢出
FPE_FLTDIV浮点数除以 0
FPE_FLTOVF浮点数向下溢出
FPE_FLTRES浮点数结果不精确
FPE_FLTINV无效的浮点运算
FPE_FLTSUB下标超出范围

5. SIGABRT

SIG是信号名的通用前缀。ABRT是abort program的缩写。

对应的数值为 6

该信号意味着异常退出;通常是调用 abort(), raise(), kill(), pthread_kill() 或者被系统进程杀死时出现。

当错误码为 SI_USER 时表示是被其它程序杀死,一般情况是由于ANR被 system_server 杀死;

其他错误码一般是业务自己调用 abort() 等函数退出,此时错误码一般认为无效。

解决方案 :

abort program
SIG是信号名的通用前缀。ABRTabort program的缩写。

当操作系统发现不安全的情况时,它能够对这种情况进行更多的控制,必要的话,它能要求进程进行清理工作。在调试造成此信号的底层错误时,并没有什么妙招。 如 cocos2d 或 UIKit 等框架通常会在特定的前提条件没有满足或一些糟糕的情况出现时调用 C 函数 abort (由它来发送此信号)。

如果是ios系统:
发生在UIApplication WillTerminate 时,是主动退出应用时发生的,所以对用户没什么实际影响。
iOS10访问相册时发生,目前只发生在iOS10+系统,需要修改工程plist文件,加入访问权限提示信息。
补充:iOS 10 has updated privacy policy and implemented new privacy rules. You have to update your Info.plist app with this following fields by authorisation asked.

6. SIGSTKFLT

SIG是信号名的通用前缀,STKFLT是stack fault 的缩写。

对应的数值为 16

按照官方文档说明,该信号量意味着协处理器栈故障。

根据网上的部分问题结论说明,在内存耗尽时,一般 malloc 返回 NULL 且设置 errno 为 ENOMEM,但有些系统可能会使用 SIGSTKFLT 信号代替。

7. SIGPIPE

该信号对应的数值为 13

该信号意味着管道错误,通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止仍继续往管道写,写进程就会收到 SIGPIPE 信号。

8. SIGUSR2

为 12。用户自定义信号2。

9. SIGTERM

SIG是信号名的通用前缀,TERM 是 Termination 的缩写。该信号对应的数值为 15。

与 SIGKILL (signal 9) 类似,不过 SIGKILL 不可捕获,而 SIGTERM 可被捕获。一般常见于 APP 处于后台时,被系统 vold 进程使用 SIGTERM 杀死,该情形基本没有意义,可忽略。

另外,chromium 多进程架构中,经常也会使用 SIGTERM 杀死出现了异常的子进程。

10. SIGSYS

该信号对应的数值为 31,通常是因为无效的 linux 内核系统调用而产生。

android O (android 8.0) 中,部分不安全的系统调用被移除,若代码中仍然使用它们,则会出现 SIGSYS。

11. SIGTRAP

该信号对应的数值为 5。gdb 调试设置断点等操作使用的信号。

Code说明
TRAP_BRKPTTRAP_BRKPT
TRAP_TRACETRAP_TRACE
TRAP_BRANCHTRAP_BRANCH
TRAP_HWBKPTTRAP_HWBKPT

解决方案

SIG是信号名的通用前缀。TRAP陷阱的意思。它并不是一个真正的崩溃信号。它会在处理器执行 trap 指令时发送。LLDB调试器通常会处理此信号,并在指定的断点处停止运行。如果你收到了原因不明的 SIGTRAP,先清除上次的输出,然后重新进行构建通常能解决这个问题。

该异常是由于打算给一个附加的调试器在执行特定的断点来中断进程时触发。你可以在自己的代码中使用__builtin_trap()方法来触发此异常。如果没有被调试器所附加,那么进程将会结束并且生成一份崩溃报告。

如果Swift代码在运行时发现一个意外的情况时,也会以该异常类型结束程序。例如:

为一个非可选(non-optional)类型被赋值nil
一个有问题的强制类型转换

以上是关于Native崩溃说明 so库崩溃SIGBUS(bad memory access) 和 SIGSEGV(Invalid memory reference)的主要内容,如果未能解决你的问题,请参考以下文章

dlopen 崩溃(接收 SIGBUS)

应用随机崩溃与致命信号7(SIGBUS),代码2或致命信号11(SIGSEGV),代码1

记一次收集APP native崩溃信息

JVM 在 Solaris 框中崩溃 有问题的框架:# C [libc.so.1]# [ timer expired, abort... ]

如何理解这次崩溃? : SIGBUS BUS_ADRALN

Android 应用程序崩溃“libjsc.so”缺少堆栈跟踪崩溃:线程:SIGSEGV 0x0000000000000010