android - 解读app c++崩溃信息?
问题描述
我特意写了一个触发空指针崩溃的代码,<br />得到了这个崩溃日志:
libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 27802 (sodino.demo), pid 27802 (sodino.demo)
DEBUG : ABI: 'arm64'
DEBUG : Timestamp: 2021-07-17 11:23:28+0800
DEBUG : pid: 27802, tid: 27802, name: sodino.demo >>> sodino.demo <<<
DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
DEBUG : Cause: null pointer dereference
DEBUG : x0 0000000000000018 x1 000000707e2e4998 x2 0000000000000019 x3 2073796173207070
DEBUG : x4 000000707e2e499e x5 0000007077a70c58 x6 0000000062626161 x7 0000000063636262
DEBUG : x8 0000000000000000 x9 0000000000000000 x10 0000000000000000 x11 0000000000000018
DEBUG : x12 6820737961732070 x13 206f74206f6c6c65 x14 0000000000000010 x15 000000711109f39a
DEBUG : x16 00000071110a4b58 x17 00000071110082bc x18 0000007115812000 x19 000000708ee10800
DEBUG : x20 0000000000000000 x21 000000708ee10800 x22 0000007fcf67f890 x23 0000007114845658
DEBUG : x24 0000000000000004 x25 000000711499c020 x26 000000708ee108b0 x27 0000000000000001
DEBUG : x28 0000007fcf67f620 x29 0000007fcf67f5c0
DEBUG : sp 0000007fcf67f560 lr 000000707e2cc9b8 pc 000000707e2cc9c8
DEBUG :
DEBUG : backtrace:
DEBUG : #00 pc 000000000000c9c8 /data/app/sodino.demo-K1-uQOGzzG20SxiRS9-g1Q==/base.apk!libnative-lib.so (offset 0x1000) (concatenateMyStringWithCppString(char const*)+248) (BuildId: fdb041aba6818c49543f4b4e93c433f6d787c772)
DEBUG : #01 pc 000000000000c878 /data/app/sodino.demo-K1-uQOGzzG20SxiRS9-g1Q==/base.apk!libnative-lib.so (offset 0x1000) (Java_sodino_demo_MainActivity_stringFromJNI+32) (BuildId: fdb041aba6818c49543f4b4e93c433f6d787c772)
日志中间的内容:
什么是 x0
~ x29
?什么是sp
lr
pc
?
第二个问题:
#00 pc 000000000000c9c8
/data/app/sodino.demo-K1-uQOGzzG20SxiRS9-g1Q==/base.apk!
libnative-lib.so (offset 0x1000)
(concatenateMyStringWithCppString(char const*)+248)
(BuildId: fdb041aba6818c49543f4b4e93c433f6d787c772)
(concatenateMyStringWithCppString(char const*)+248)
什么意思+248
?
我以为+248
是代码行号,
但代码不匹配如下:line 11
触发空点崩溃:
核心.h
#ifndef __HelloCpp__Core__
#define __HelloCpp__Core__
#include <iostream>
const char *concatenateMyStringWithCppString(const char *myString);
#endif /* defined(__HelloCpp__Core__) */
核心.cpp
#include <string.h>
#include "Core.h"
const char *CPP_BASE_STRING = "cpp says hello to %s";
const char *concatenateMyStringWithCppString(const char *myString) {
char *concatenatedString = new char[strlen(CPP_BASE_STRING) + strlen(myString)];
sprintf(concatenatedString, CPP_BASE_STRING, myString);
char* p = NULL;
*p = 0; // line 11 crash.
return concatenatedString;
}
本机 lib.cpp
#include <jni.h>
#include <string>
#include "Core.h"
extern "C" JNIEXPORT jstring JNICALL
Java_sodino_demo_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
const char* result = concatenateMyStringWithCppString("aabbcc");
// std::string hello = "Hello from C++";
return env->NewStringUTF(result);
}
解决方案
x0 ~ x29 是什么?什么是sp lr pc?
x0 到 x29 是ARM64 的通用寄存器。
sp
,lr
并且pc
如ARM 链接和帧指针中所述。
sp
用于函数中本地数据的堆栈指针。lr
调用当前例程的函数/方法的返回地址。pc
当前指令。
寄存器是所有商业 CPU 通用的。可以制作仅基于堆栈的架构,但它们通常不能很好地执行。编译器可以识别热值并在构建时将它们分配给寄存器。堆栈通常比寄存器慢 5-100 倍。
(concatenateMyStringWithCppString(char const*)+248)
(BuildId: fdb041aba6818c49543f4b4e93c433f6d787c772)
+248 是什么意思?
这不在代码级别。它是自例程开始以来的指令数concatenateMyStringWithCppString
。事实上,如果跟踪代码不能看到所有例程,则指令可能位于不同的函数中。
正常的过程是得到一个汇编列表,之后是(248/4 -> 62)指令。没有看到汇编程序,
const char *concatenateMyStringWithCppString(const char *myString) {
char *concatenatedString = new char[strlen(CPP_BASE_STRING) + strlen(myString)];
sprintf(concatenatedString, CPP_BASE_STRING, myString);
char* p = NULL;
*p = 0; // line 11 crash.
return concatenatedString;
}
看起来第 11 行将包含大约 62 条指令到例程中,特别是如果strlen()
是内联的。编译器选项、版本等将在生成不同代码时更改偏移量与行号。