吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2122|回复: 1
收起左侧

[其他转载] NDK开发定位native异常

[复制链接]
LoongC 发表于 2022-4-1 19:57
本帖最后由 LoongC 于 2022-4-1 20:01 编辑

NDK开发定位native异常

NDK 是使用 C/C++ 来进行开发,指针和内存管理是最重要也是最容易出问题的地方,就会遇到诸如内存地址访问错误、使用野指针、内存泄露、堆栈溢出、初始化错误、类型转换错误、数字除0等常见的问题,导致最后都是同一个结果:程序崩溃。当发生 NDK 错误后,logcat 打印的日志根据进行了偏移,无法直接通过日志信息定位错误发生的行数。

首先,当 NDK 程序在发生 Crash 时,它会在路径 /data/tombstones/ 下产生导致程序 Crash 的文件 tombstone_xx。并且 Google 还在 NDK 包中为我们提供了一系列的调试工具,例如 addr2lineobjdumpndk-stack

一、 native 层的崩溃处理机制

在 Unix-like 系统中,所有的崩溃都是编程错误或者硬件错误相关的,系统遇到不可恢复的错误时会触发崩溃机制让程序退出,如除零、段地址错误等。异常发生时,CPU 通过异常中断的方式,触发异常处理流程。不同的处理器,有不同的异常中断类型和中断处理方式。linux 把这些中断处理,统一为信号量,可以注册信号量向量进行处理。信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号。

函数运行在用户态,当遇到系统调用、中断或是异常的情况时,程序会进入内核态。信号涉及到了这两种状态之间的转换。

image.png

接收信号的任务是由内核代{过}{滤}理的,当内核接收到信号后,会将其放到对应进程的信号队列中,同时向进程发送一个中断,使其陷入内核态。注意,此时信号还只是在队列中,对进程来说暂时是不知道有信号到来的。进程陷入内核态后,有两种场景会对信号进行检测:

  • 进程从内核态返回到用户态前进行信号检测
  • 进程在内核态中,从睡眠状态被唤醒的时候进行信号检测

当发现有新信号时,便会进入信号的处理。信号处理函数是运行在用户态的,调用处理函数前,内核会将当前内核栈的内容备份拷贝到用户栈上,并且修改指令寄存器(eip)将其指向信号处理函数。接下来进程返回到用户态中,执行相应的信号处理函数。信号处理函数执行完成后,还需要返回内核态,检查是否还有其它信号未处理。如果所有信号都处理完成,就会将内核栈恢复(从用户栈的备份拷贝回来),同时恢复指令寄存器(eip)将其指向中断前的运行位置,最后回到用户态继续执行进程。至此,一个完整的信号处理流程便结束了,如果同时有多个信号到达,会不断的检测和处理信号。

二、Native崩溃日志格式及内容分析
2022-03-18 13:29:37.872 26666-26666/? A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 26666 (.logandroiddemo), pid 26666 (.logandroiddemo)
2022-03-18 13:29:37.915 26720-26720/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2022-03-18 13:29:37.915 26720-26720/? A/DEBUG: Build fingerprint: 'HONOR/TEL-AN00/HWTEL-H:10/HONORTEL-AN00/3.1.1.203C00:user/release-keys'

2022-03-18 13:29:37.915 26720-26720/? A/DEBUG: Revision: '0'
2022-03-18 13:29:37.915 26720-26720/? A/DEBUG: ABI: 'arm64'
---------------------------------------------------------机器信息----------------------------------------------------------------

2022-03-18 13:29:37.917 26720-26720/? A/DEBUG: SYSVMTYPE: Maple
    APPVMTYPE: Art
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG: Timestamp: 2022-03-18 13:29:37+0800
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG: pid: 26666, tid: 26666, name: .logandroiddemo  >>> com.example.logandroiddemo <<<
---------------------------------------------------------进程,线程,包名----------------------------------------------------------

2022-03-18 13:29:37.918 26720-26720/? A/DEBUG: uid: 10436
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
----------------------------------------------------引发进程崩溃的信号信息-----------------------------------------------------------

2022-03-18 13:29:37.918 26720-26720/? A/DEBUG: Cause: null pointer dereference
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     x0  000000763bf086c0  x1  0000007fdd5d1784  x2  0000000000000006  x3  0000000010d810d8
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     x4  0000000300000003  x5  00000000173c5650  x6  173c5620173c5638  x7  0000000000000000
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     x8  0000000000000000  x9  0000000000000001  x10 0000000000430000  x11 000000763bdf7000
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     x12 00000076c04783c0  x13 1803b8d645ffb1dd  x14 0000000000000006  x15 ffffffffffffffff
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     x16 00000076201b7e70  x17 00000076bfc62980  x18 00000076c2912000  x19 000000763be10800
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     x20 0000000000000000  x21 000000763be10800  x22 0000007fdd5d19f0  x23 00000076c15c44d2
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     x24 0000000000000004  x25 00000076c1b1e020  x26 000000763be108b0  x27 0000000000000001
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     x28 0000007fdd5d1780  x29 0000007fdd5d1750
2022-03-18 13:29:37.918 26720-26720/? A/DEBUG:     sp  0000007fdd5d16e0  lr  000000763b953354  pc  00000076201b7ea0
----------------------------------------------进程崩溃时各寄存器值,随cpu不同而不同----------------------------------------------------

2022-03-18 13:29:38.043 26720-26720/? A/DEBUG: backtrace:
2022-03-18 13:29:38.043 26720-26720/? A/DEBUG:       #00 pc 0000000000006ea0  /data/app/com.example.logandroiddemo-VanHrh72D7snIXV6jJNkPg==/base.apk!libnative-lib.so (offset 0x2b5000) (Java_com_example_logandroiddemo_MainActivity_stringFromJNI+48) (BuildId: a5621f59e0623fa37bf37f2a2990f9cffc687334)
2022-03-18 13:29:38.043 26720-26720/? A/DEBUG:       #01 pc 000000000014c350  /apex/com.android.runtime/lib64/libart.so (art_quick_generic_jni_trampoline+144) (BuildId: c34524a56ff4fcd8ce5f2ffe56e5a86b)
2022-03-18 13:29:38.043 26720-26720/? A/DEBUG:       #02 pc 0000000000143334  /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: c34524a56ff4fcd8ce5f2ffe56e5a86b)
2022-03-18 13:29:38.043 26720-26720/? A/DEBUG:       #03 pc 00000000001521a4  /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+252) (BuildId: c34524a56ff4fcd8ce5f2ffe56e5a86b)
2022-03-18 13:29:38.043 26720-26720/? A/DEBUG:       #04 pc 00000000002edc9c  /apex/com.android.runtime/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+384) (BuildId: c34524a56ff4fcd8ce5f2ffe56e5a86b)
2022-03-18 13:29:38.043 26720-26720/? A/DEBUG:       #05 pc 00000000002e8f6c  /apex/com.android.runtime/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+912) (BuildId: c34524a56ff4fcd8ce5f2ffe56e5a86b)..........截取部分内容
------------------------------------------------崩溃时函数调用堆栈信息,主要分析点-----------------------------------------------------
三、常见崩溃类型及原因
SIGSEGV 段错误
SEGV_MAPERR 要访问的地址没有映射到内存空间。 比如上面对空指针的写操作, 当指针被意外复写为一个较小的数值时
SEGV_ACCERR 访问的地址没有权限。比如试图对代码段进行写操作。
SIGFPE 浮点错误 一般发生在算术运行出错时
FPE_INTDIV 除以0
FPE_INTOVE 整数溢出
SIGBUS 总线错误
BUS_ADRALN 地址对齐出错。arm cpu比x86 cpu 要求更严格的对齐机制,所以在 arm cpu 机器中比较常见。
SIGILL 错误 发生这种错误一般是由于某处内存被意外改写了
ILL_ILLOPC 非法的指令操作码
ILL_ILLOPN 非法的指令操作数
四、调试工具

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

优先使用带符号表的lib.so,无符号表的so库使用常规调试工具时无法定位到类名和错误行数需要使用IDA PRO分析汇编指令。

发布时要把带符号表的so进行备份或者上传,方便分析定位native崩溃

带符号表.so文件路径

image.png

不带符号表.so文件路径

image.png

1.         addr2line

找到安装的NDK路径下 打开cmd输入下面命令(arm-linux-androideabi-addr2line.exe -help 查看所有使用方法

arm-linux-androideabi-addr2line.exe -e $PROJECT_PATH\obj\local\arm64-v8a\xxx.so 偏移信息

例如:

C:\Users\user\AppData\Local\Android\Sdk\ndk\16.1.4479499\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin>
aarch64-linux-android-addr2line.exe -e 
D:\fanyunlong_testDemo\LogAndroidDemo\app\build\intermediates\cmake\debug\obj\arm64-v8a\libnative-lib.so 0000000000006ea0

找到arm64-v8a下的libnative-lib.so

#00 pc 0000000000006ea0  /data/app/com.example.logandroiddemo-VanHrh72D7snIXV6jJNkPg==/base.apk!libnative-lib.so (offset 0x2b5000) 

偏移信息为:0000000000006ea0

定位结果:

LogAndroidDemo/app/src/main/cpp/native-lib.cpp:15
10        extern "C" JNIEXPORT jstring JNICALL
11        Java_com_example_logandroiddemo_MainActivity_stringFromJNI(
12        JNIEnv* env,
13        jobject /* this */) {
14           int *p = 0;
15            *p = 1;
16            std::string hello = "Hello from C++";
17
18            return env->NewStringUTF(hello.c_str());
19        }
2.         ndk-stack
选项 说明
-sym 指定debug版本库的位置
-dump 指定含有crash信息的文件
adb logcat | $NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi-v7a

您也可以使用 -dump 选项将 logcat 指定为输入文件。例如:

adb logcat > /tmp/foo.txt
$NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi-v7a -dump foo.txt

该工具会在开始解析 logcat 输出时查找第一行星号。例如:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

免费评分

参与人数 1吾爱币 +1 收起 理由
Tonyha7 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

as4202 发表于 2022-4-1 23:31
学习了,谢谢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 07:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表