美文网首页
6.【干货】火爆全网的《超全NDK精品教程》JNI so库奔溃&

6.【干货】火爆全网的《超全NDK精品教程》JNI so库奔溃&

作者: 鹏城十八少 | 来源:发表于2023-03-20 21:30 被阅读0次

    NDK异常信息一般有三个要素:

    ndk--------->墓碑

    第一步: 

    cd /data/tombstones/  

    第二步: 

    /data/tombstones # ls -lht

    第三步: 

    more cdc_8295-221024-197-tombstone_048 

    so库是如何生成的 ????

    cmake生成.so方案

    以上方案都是通通不行的,最终找到是Cmake 3.10.2版本过高,与build版本不一致造成的

    NDK异常信息一般有三个要素:

    信号

    调用栈信息(栈地址,内存地址)

    寄存器信息

    错误信号:11是信号量sigNum,SIGSEGV是信号的名字,SEGV_MAPERR是SIGSEGV下的一种类型。

    寄存器快照:进程收到错误信号时保存下来的寄存器快照,其中PC寄存器存储的就是下个要运行的指令(出错的位置)。

    调用栈:#00是栈顶,#02是栈底,#02调用#01调用#00方法,#00的方法时libspirit.so中的Spirit类下的testCrash方法,出错的地方是testCrash方法内汇编偏移17(不是行号哦!)

    通常的来源有三个:

    1、硬件发生异常,即硬件(通常是CPU)检测到一个错误条件并通知Linux内核,内核处理该异常,给相应的进程发送信号。硬件异常的例子包括执行一条异常的机器语言指令,诸如,被0除,或者引用了无法访问的内存区域。大部分信号如果没有被进程处理,默认的操作就是杀死进程。在本文中,SIGSEGV(段错误),SIGBUS(内存访问错误),SIGFPE(算数异常)属于这种信号。

    2、进程调用的库发现错误,给自己发送中止信号,默认情况下,该信号会终止进程。在本文中,SIGABRT(中止进程)属于这种信号。

    3、用户(手贱)或第三方App(恶意)通过kill-信号 pid的方式给错误进程发送,这时signal中的si_code会小于0。

    SO库奔溃总结方案:

    1.添加日志信息

    2.找到c的源代码生成带符号的so库。

    3.安装bugly,分析具体那一行出了问题

    4.jni异常和c++异常的基本分析和解决办法

    5.从奔溃信息中可以反馈一个问题:c比较安全,比较难破解

    =====================================================

    need-to-insert-img

    在介绍工具之前,先简单讲一下有调试与无调试信息的两个版本 so 。 一个含有 native 代码的 app 项目的典型结构是这样的:

    一般的分析崩溃日志的工具都是利用含有调试信息的 so, 结合崩溃信息,分析崩溃点在源代码中的行号。

    通常一次编译会先生成一个有含有调试信息的 so, 路径通常是在 obj/local/ 各 abi 目录下,其中还有一些中间文件(比如.o文件);再通过对这些含有调试信息的 so 进行一次 strip , 产生对应的无调试信息 so, 放到 libs 目录下各 abi 目录中, 发布产品时,我们都是用这些 strip 后的 so。

    ————————————————

    1.android如何编译出带符号表的.so库

    如果是mk:可以直接生成2种so库

    如果是cmake:debug版本就是带符号的。release就是不带符号的。

    用默认的so库路径

    #生成的so文件目录

    #set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../../../src/main/jniLibs/armebai-v7a)

    把自己生成的注释掉

    bugly:分析有符号的so库

    https://bugly.qq.com/docs/user-guide/symbol-configuration-android/?v=1590754344571#_11

    符号表:

    need-to-insert-img

    VS 编写实现方法生成 dll 动态库

    使用VisualStudio高效开发调试AndroidNDK

    cmake编译so库。然后同时生成apk。

    会发现:会先编译然后生成的apk会把so打包进去。放心好了

    ===========================================================================

    2.so库奔溃具体行数:定位so奔溃的行数

    Android NDK墓碑/崩溃分析

    闪退发生时在logcat中将日志过滤条件选为“No Filters”就可以看到完整的闪退日志,或者叫tombstone(墓碑)文件。

    tombstone(墓碑)是当系统 crash 的时候,会保存一个 tombstone 文件到/data/tombstones目录下(Logcat中也会有相应的信息)

    https://blog.csdn.net/crash163/article/details/51605926

    3.NDK安装包中提供了三个调试工具:addr2line、objdump和ndk-stack,

    google官方提供的ndk-stack工具分析:

    https://developer.android.google.cn/ndk/guides/ndk-stack?hl=zh-cn

    4.bugly的定位原理:ndk-stack使用:

    https://blog.csdn.net/xyang81/article/details/42319789

    Android ndk-stack 定位so库crash位置

    如果你觉得上面的方法太麻烦的话,ndk-stack可以帮你减轻操作步聚,直接定位到代码出错的位置。

    使用adb获取logcat的日志,并通过管道输出给ndk-stack分析,并指定包含符号表的so文件位置。如果程序包含多种CPU架构,需要根据手机的CPU类型,来选择不同的CPU架构目录。以armv7架构为例,执行如下命令:

    1.运行终端。 跳转到你android sdk 目录 因为你的adb 在里面。

    如 cd /Users/name/Android/adt-bundle-mac-x86_64-20131030/sdk/platform-tools 

    2、找了路径正确继续下一步,./adb logcat | 你android ndk-stack所在的路径 -sym /你安卓工程.so文件所在的目录

    如./adb logcat | /Users/name/Android/android-ndk-r8e/ndk-stack -sym /Users/name/test/proj.android/obj/local/armeabi

    3、正确配置后会在终端出现

    - waiting for device -

    adb logcat | $NDK_HOME/ndk-stack -sym $PROJECT/libs/armeabi

    其中,$PROJECT/libs/armeabi是so的路径。

    adb logcat | $NDK_HOME/ndk-stack -sym /Users/mac/AiOpen/AiOpen/app/jni-libs/armeabi

    adb logcat > /Users/mac/ndk/tongue.txt

    $NDK_HOME/ndk-stack -sym /Users/mac/AiOpen/AiOpen/app/jni-libs/armeabi -dump /Users/mac/ndk/tongue.txt

    可以看到具体的行数

    need-to-insert-img

    need-to-insert-img

    总结:

    如果是so库找不到,那么看不到详情

    null指针可以

    5.so库奔溃,日志种类分析:

    https://www.jianshu.com/p/d8ef41edb1bd(不错)

    结合signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0信息,配合崩溃信号列表:

    信号描述

    SIGSEGV内存引用无效。

    SIGBUS访问内存对象的未定义部分。

    SIGFPE算术运算错误,除以零。

    SIGILL非法指令,如执行垃圾或特权指令

    SIGSYS糟糕的系统调用

    SIGXCPU超过CPU时间限制。

    SIGXFSZ文件大小限制。

    1、SIGSEGV 段错误

    SEGV_MAPERR 要访问的地址没有映射到内存空间。 比如上面对空指针的写操作, 当指针被意外复写为一个较小的数值时。

    SEGV_ACCERR 访问的地址没有权限。比如试图对代码段进行写操作。

    2、SIGFPE 浮点错误,一般发生在算术运行出错时。

    FPE_INTDIV 除以0

    FPE_INTOVE 整数溢出

    3、SIGBUS 总线错误

    BUS_ADRALN 地址对齐出错。arm cpu比x86 cpu 要求更严格的对齐机制,所以在 arm cpu 机器中比较常见。

    4、SIGILL 发生这种错误一般是由于某处内存被意外改写了。

    ILL_ILLOPC 非法的指令操作码

    ILL_ILLOPN 非法的指令操作数

    5、当调用堆栈中出现 stack_chk_fail 函数时,一般是由于比如 strcpy 之类的函数调用将栈上的内容覆盖,而引起栈检查失败。

    Testin崩溃分析如何帮开发者发现NDK错误

    以上提到的方法,只适合在开发测试期间,如果你的应用或游戏已经上线,而用户经常反馈说崩溃、闪退,指望用户帮你收集信息定位问题几乎是不可能的。这个时候,我们就需要用其他的手段来捕获崩溃信息。

    目前业界已经有一些公司推出了崩溃信息收集的服务,通过嵌入SDK,在程序发生崩溃时收集堆栈信息,发送到云服务平台,从而帮助开发者定位错误信息。在这方面,国内的Testin和国外的crittercism都可以提供类似服务。

    Testin从1.4版本开始支持NDK的崩溃分析,其最新版本已升级到1.7。当程序发生NDK错误时,其内嵌的SDK会收集程序在用户手机上发生崩溃时的堆栈信息(主要就是上面我们通过logcat日志获取到的函数指针)、设备信息、线程信息等,SDK将这些信息上报至Testin云服务平台,在平台进行唯一性的处理、并可以自定义时段进行详尽的统计分析,从多维度展示程序崩溃的信息和严重程度;最新版本还支持用户自定义场景,方便开发者定位问题所在。

    从用户手机上报的堆栈信息,Testin为NDK崩溃提供了符号化的功能,只要将我们编译过程中产生的包含符号表的so文件上传,就可以自动将函数指针地址定位到函数名称和代码行数。符号化之后,看起来就和我们前面在本地测试的结果是一样的了,一目了然。而且使用这个功能还有一个好处:这些包含符号表的so文件,在每次开发者编译之后都会改变,很有可能我们发布之后就已经变了,因为开发者会修改程序。在这样的情况下,即使我们拿到了崩溃时的堆栈信息,那也无法再进行符号化了。我们可以将这些文件上传到Testin进行符号化的工作,Testin会为我们保存和管理不同版本的so文件,确保信息不会丢失。

    NDK | 说说 so 库从加载到卸载的全过程

    https://juejin.cn/post/6892793299427491854

    .so文件的区别

    armeabi、armeabi-v7a和x86都表示CPU的类型。一般的手机或平板都是用arm的cpu。

    不同的cpu的特性不一样,armeabi就是针对普通的或旧的

    arm v5 cpu,armeabi-v7a是针对有浮点运算或高级扩展功能的arm v7 cpu。

    mips、armeabi、armeabi-v7a和x86到底是什么

    armeabi:默认选项,将创建以基于

    ARM* v5TE 的设备为目标的库。 具有这种目标的浮点运算使用软件浮点运算。 使用此 ABI (二进制接口)

    创建的二进制代码将可以在所有 ARM* 设备上运行。所以armeabi通用性很强。但是速度慢

    armeabi-v7a:创建支持基于 ARM* v7 的设备的库,并将使用硬件 FPU 指令。armeabi-v7a是针对有浮点运算或高级扩展功能的arm v7 cpu。

    x86:支持基于硬件的浮点运算的 

    总结:

    不同的CPU不用的芯片,armeabi是最基本的,有不同的运算指令,如浮点运算,所以需要生成不同的.so文件

    如果项目只包含了 armeabi,那么在所有android设备都可以运行; 如果项目只包含了 armeabi-v7a,除armeabi架构的设备外都可以运行; 

    如果项目只包含了 x86,那么armeabi架构和armeabi-v7a的Android设备是无法运行的;

    如果同时包含了 armeabi, armeabi-v7a和x86,

    所有设备都可以运行,程序在运行的时候去加载不同平台对应的so,这是较为完美的一种解决方案,同时也会导致包变大。

    2. 接下来说明这么做的依据: 

    看上面图分析,armeabi-v7主要不支持ARMv5(1998年诞生)和ARMv6(2001年诞生).目前这两款处理器的手机设备基本不在我公司的适配范围(市场占比太少)。 

    而许多基于 x86 的设备也可运行 armeabi-v7a 和 armeabi NDK 二进制文件。对于这些设备,主要 ABI 将是 x86,辅助 ABI 是 armeabi-v7a。 

    最后总结一点:如果适配版本高于4.1版本,可以直接像我上面这样写,当然,如果armeabi-v7a不是设备主要ABI,那么会在性能上造成一定的影响。 

    --------------------- 

    早期的Android系统几乎只支持ARMv5的CPU架构。你知道现在它支持多少种吗?7种!

    Android系统目前支持以下七种不同的CPU架构:ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起),每一种都关联着一个相应的ABI。

    应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。在Android系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

    4. ABI 兼容性

    目前搜索的资料总结如下:

    armeabi-v7a :armeabi-v7a向下兼容armeabi

    arm64-v8a : 能兼容 armeabi-v7a 和 armeabi

    x86_64 : 兼容 x86

    mips64 : 兼容 mips

    目前主流 APP 所用的 ABI 各是哪些

    数据是 2017/4/8看的市场最新版,采集的几个代表性样本如下:

    仅有 armeabi : 微博,今日头条,淘宝,QQ,微信。

    armeabi 与 armeabi-v7a: UC 浏览器。

    armeabi,armeabi-v7a,armeabi-x86:优酷,哔哩哔哩动画。

    7种类型全有:知乎。

    现在主要是armV7的架构和x86

    我也看了目前流行的游戏 王者荣耀(因为我只安装了它。。。哈哈),发现它仅有 armeabi-v7a

    ABI和CPU的关系

    很多设备都支持多于一种的ABI。例如ARM64和x86设备也可以同时运行armeabi-v7a和armeabi的二进制包。但最好是针对特定平台提供相应平台的二进制包,这种情况下运行时就少了一个模拟层(例如x86设备上模拟arm的虚拟层)

    CPU/ABIarmeabiarmeabi-v7aarm64-v8ax86x86_64mipsmips64

    ARMv5支持

    ARMv7支持支持

    ARMv8支持支持支持

    x86支持支持 支持

    x86_64支持      支持支持

    mips      支持

    mips_64      支持支持

    need-to-insert-img

    通过指令查看cpu的信息,包含cpu的架构

    如何查看so库安装在手机里面存放在了什么位置?和它到底加载哪个

    你应该尽可能的提供专为每个ABI优化过的.so文件,但要么全部支持,要么都不支持:你不应该混合着使用。你应该为每个ABI目录提供对应的.so文件。

    当一个应用安装在设备上,只有该设备支持的CPU架构对应的.so文件会被安装。在x86设备上,libs/x86目录中如果存在.so文件的话,会被安装,如果不存在,则会选择armeabi-v7a中的.so文件,如果也不存在,则选择armeabi目录中的.so文件(因为x86设备也支持armeabi-v7a和armeabi)。

    正确的做法

    当前市面绝大多数是arm的CPU,而且都是V7架构的了,所以可以保留armeabi或者armeabi-v7a即可。

    如果仅保留armeabi-v7a,而有些第三方包未提供v7a的包,则可以将对应armeabi包拷贝到armeabi-v7a。

    如果同时保留armeabi和armeabi-v7a,则需要保证两个目录下的so库文件数相同。

    so库:https://juejin.im/post/5eae6f86e51d454ddb0b3dc6

    3.NDK问题 ,so库类型不对奔溃

    ndk {

    abiFilters"armeabi"

    }

    4.奔溃,因为找不到方法或者so库奔溃,方法找不到原因:要包名一样才行的

     java.lang.UnsatisfiedLinkError: No implementation found for double

    6.NDK版本不对,导致奔溃问题

    完美解决 No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android

    https://blog.csdn.net/qq_24118527/article/details/82867864

    7..地址配置有问题:拼接

    Unable to resolve host "open-api1.51yund.comauth": No address associated with hostname

    ==========================

    https://blog.csdn.net/scruffybear/article/details/129459810

    相关文章

      网友评论

          本文标题:6.【干货】火爆全网的《超全NDK精品教程》JNI so库奔溃&

          本文链接:https://www.haomeiwen.com/subject/fksprdtx.html