背景:
在libandroid_runtime.so添加了一个jni 方法, 在底层消息过来时, 此jni方法会回调java 的callback 方法,将数据吐给java.
问题:
jni回调到java, 但没有执行java的相关代码, 然后线程跳到JN继续执行, log中也没有线程的相关异常, 各个线程也正常运行着, 但相关代码就是没有执行
代码:
code for jni
static void android_os_nng_startListener(JNIEnv* env, jclass clazz){
while(true){
mQueue.waitForItems();
std::vector<Message*> vec = mQueue.flush();
ALOGE(code, "mQueue flush ");
for(Message* msg : vec){
switch(msg->type){
case NNG_MSG_DATA:
{
ALOGE(code, "mQueue flush DATA");
int len = nng_msg_len(msg->msg);
jbyteArray jarray = env->NewByteArray(len);
ALOGE(code, "mQueue flush DATA len: %d" , len);
env->SetByteArrayRegion(jarray, 0, len, static_cast<jbyte*>(nng_msg_body(msg->msg)));
env->CallVoidMethod(gNngOffsets.obj, gNngOffsets.callback, jarray);
ALOGE(code, "mQueue flush DATA len end: %d" , len);
nng_msg_free(msg->msg);
free(msg);
}
break;
case NNG_MSG_STATUS:
ALOGE(code, "mQueue flush status");
env->CallVoidMethod(gNngOffsets.obj, gNngOffsets.reportStatus, msg->nng_pipe_ev, msg->port);
break;
}
}
}
}
code for java
public final void onCallback(byte[] bytes){
Log.d(TAG, "onCallback");
String topic = new String(bytes, 0, 11);
int topicID = Integer.valueOf(topic);
MsgCallBack obj;
lock.lock();
try{
obj = arr.get(topicID);
}finally{
lock.unlock();
}
Log.d(TAG, "onCallback: " + obj.toString());
obj.onCallback(obj.cast(bytes));
}
最后现象为:
4231 4296 E jni_nng : mQueue flush
4231 4296 E jni_nng : mQueue flush DATA
4231 4296 E jni_nng : mQueue flush DATA len: 95
4231 4296 D Nng : onCallback
4231 4296 E jni_nng : mQueue flush
4231 4296 E jni_nng : mQueue flush DATA
4231 4296 E jni_nng : mQueue flush DATA len: 95
4231 4296 D Nng : onCallback
可以看到, jni回调到java, 打印了一行log, 然后并没有往下走, 而是回到jni 等待下一个消息到来.
分析:
为什么java 后续没有执行? 是onCallback方法内有异常, 然后异常没有在log里面体现?
措施: 在onCallback 中一行行添加log
结果: 还是只打印在onCallback 入口log
是bytes有问题?
措施: 在onCallback中添加log,打印 bytes的信息
结果: 还是只打印onCallback入口处log
难道是没有替换成功?
措施: 排查替换jar包, 在次替换, 修改入口类log....
结果: 确认替换成功了, 就是只能打印入口出log, 或无log输出.
是jni构建的bytes有问题,在java中无法正确的访问?
措施: 用类似的代码, 写一个demo app, 在板子上试试
结果: 经过验证没有问题, 可以正常运行.
jdb 调试一下试试:
措施: jdb attach 到4231 进程, 在 onCallback 方法内打上断点
结果: 程序运行到onCallback处, 直接和调试工具断开, 并且程序crash.
没有头绪, 进行整体一个try catch 试试:
修改:
public final void onCallback(byte[] bytes){
try{
......
}catch(Exception e){
e.printStackTrace();
}
}
结果:
的确存在异常,相关log 也能打印出来了:
4796 4843 E jni_nng : mQueue flush
4796 4843 E jni_nng : mQueue flush DATA
4796 4843 E jni_nng : mQueue flush DATA len: 95
4796 4843 D Nng : onCallback
4796 4843 D Nng : onCallback: com.android.car.hal.HalIPCPowerClient$IPCPowerInfoCallback@5a2fcd9
4796 4843 W System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[])' on a null object reference
4796 4843 W System.err: at android.os.MsgCallBack.cast(MsgCallBack.java:60)
4796 4843 W System.err: at android.os.Nng.onCallback(Nng.java:261)
4796 4843 W System.err: at android.os.Nng.startListener(Native Method)
4796 4843 W System.err: at android.os.Nng.access$000(Nng.java:37)
4796 4843 W System.err: at android.os.Nng$1.run(Nng.java:92)
可以看到 onCallback 中是存在一个null指针的, 修改此 null 指针, 问题修复, 一切正常.
遗留问题:
1.进程发生 excetpion 的原理是什么?
2.为什么trycatch之前此异常没有抛出, 依然正常往下运行了呢?
excetpion 原理:
- 写一个demo app, 抛出一个exception
public final void onCallback(byte[] bytes){
throw new NullPointerException("test");
}
2.查看对应的dex文件:
.method public final onCallback([B)V
.registers 4
00000000 new-instance v0, NullPointerException
00000004 const-string v1, "test"
00000008 invoke-direct NullPointerException-><init>(String)V, v0, v1
0000000E throw v0
.end method
可以看到是throw 关键字抛出的异常
throw关键字的实现为:
art/runtime/entrypoints/quick/quick_throw_entrypoints.cc
// Called by generated code to throw a NPE exception.
extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
// We come from an explicit check in the generated code. This path is triggered
// only if the object is indeed null.
ThrowNullPointerExceptionFromDexPC(/* check_address= */ false, 0U);
self->QuickDeliverException();
}
// Installed by a signal handler to throw a NPE exception.
extern "C" NO_RETURN void artThrowNullPointerExceptionFromSignal(uintptr_t addr, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ThrowNullPointerExceptionFromDexPC(/* check_address= */ true, addr);
self->QuickDeliverException();
}
由此可见throw 抛出的异常会,在QuickDeliverException 内处理
art/runtime/thread.cc
void Thread::QuickDeliverException() {
//获取线程异常
ObjPtr<mirror::Throwable> exception = GetException();
//查找对应的trycatch 模块,并设置线程内的异常不可见.
ClearException();
QuickExceptionHandler exception_handler(this, false); //创建 exception handler
exception_handler.FindCatch(exception); // 查找 是否存在对应的try catch, (编译try catch会在字节码中,生成一个异常表格, 此表格会加载到art, 以此为根据查找)
//根据设置的 HandlerQuickFramePc 跳转到对应的trycatch 模块
exception_handler.DoLongJump();
}
遍历调用栈, 查找符合的catch模块
art/runtime/quick_exception_handler.cc
void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) {
instrumentation::InstrumentationStackPopper popper(self_);
MutableHandle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
do {
// Walk the stack to find catch handler.
CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this, /*skip_frames=*/already_popped);
visitor.WalkStack(true); //遍历整个调用栈 查找 catchHandler 或者 遍历到栈底结束
popped_to_top = popper.PopFramesTo(instrumentation_frames_to_pop, exception_ref);
} while (!popped_to_top);
}
遍历函数调用栈
art/runtime/stack.cc
void StackVisitor::WalkStack(bool include_transitions) {
for(***){
bool should_continue = VisitFrame(); //遍历当前栈, 找到catchHandler 或者 当前Frame指向nattive, 返回false, 否则都返回true
if (UNLIKELY(!should_continue)) {
return;
}
QuickMethodFrameInfo frame_info = GetCurrentQuickFrameInfo();
if (context_ != nullptr) {
context_->FillCalleeSaves(reinterpret_cast<uint8_t*>(cur_quick_frame_), frame_info);
}
//计算下一个栈的pc
size_t frame_size = frame_info.FrameSizeInBytes();
size_t return_pc_offset = frame_size - sizeof(void*);
uint8_t* return_pc_addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + return_pc_offset;
uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr);
cur_quick_frame_pc_ = return_pc;
}
}
分析当前栈的情况
art/runtime/quick_exception_handler.cc
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = GetMethod();
exception_handler_->SetHandlerFrameDepth(GetFrameDepth());
if (method == nullptr) {
//如果遍历到了栈底, 将HandlerQuickFramePc 设置为当前栈的pc
exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
exception_handler_->SetHandlerMethodHeader(GetCurrentOatQuickMethodHeader());
uint32_t next_dex_pc;
ArtMethod* next_art_method;
bool has_next = GetNextMethodAndDexPc(&next_art_method, &next_dex_pc);
// Report the method that did the down call as the handler.
exception_handler_->SetHandlerDexPc(next_dex_pc);
exception_handler_->SetHandlerMethod(next_art_method);
return false; // End stack walk.
}
return HandleTryItems(method);
}
private:
bool HandleTryItems(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
uint32_t dex_pc = dex::kDexNoIndex;
if (!method->IsNative()) {
dex_pc = GetDexPc();
}
if (dex_pc != dex::kDexNoIndex) {
bool clear_exception = false;
StackHandleScope<1> hs(GetThread());
Handle<mirror::Class> to_find(hs.NewHandle((*exception_)->GetClass()));
//进行匹配查找,看是否有对应的 catch handler
uint32_t found_dex_pc = method->FindCatchBlock(to_find, dex_pc, &clear_exception);
exception_handler_->SetClearException(clear_exception);
if (found_dex_pc != dex::kDexNoIndex) {
//如果存在对应的catch handler, 这设置到 HandlerQuickFramePc 中
exception_handler_->SetHandlerMethod(method);
exception_handler_->SetHandlerDexPc(found_dex_pc);
exception_handler_->SetHandlerQuickFramePc(
GetCurrentOatQuickMethodHeader()->ToNativeQuickPc(
method, found_dex_pc, /* is_for_catch_handler= */ true));
exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
exception_handler_->SetHandlerMethodHeader(GetCurrentOatQuickMethodHeader());
return false;
} else if (UNLIKELY(GetThread()->HasDebuggerShadowFrames())) {
// We are going to unwind this frame. Did we prepare a shadow frame for debugging?
size_t frame_id = GetFrameId();
ShadowFrame* frame = GetThread()->FindDebuggerShadowFrame(frame_id);
if (frame != nullptr) {
// We will not execute this shadow frame so we can safely deallocate it.
GetThread()->RemoveDebuggerShadowFrameMapping(frame_id);
ShadowFrame::DeleteDeoptimizedFrame(frame);
}
}
}
return true; // Continue stack walk.
}
非throw 产生的异常
先由art接收到中断的signal信号,然后将信号交由对应handler处理,之后就流程类似了
//art/runtime/arch/arm64/fault_handler_arm64.cc
bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
sc->sp -= sizeof(uintptr_t);
*reinterpret_cast<uintptr_t*>(sc->sp) = sc->pc + 4;
sc->regs[30] = reinterpret_cast<uintptr_t>(info->si_addr);
sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
return true;
}
ENTRY art_quick_throw_null_pointer_exception_from_signal
.cfi_def_cfa_offset __SIZEOF_POINTER__
.cfi_rel_offset lr, 0
INCREASE_FRAME (FRAME_SIZE_SAVE_EVERYTHING - __SIZEOF_POINTER__)
SAVE_REG x29, (FRAME_SIZE_SAVE_EVERYTHING - 2 * __SIZEOF_POINTER__) // LR already saved.
SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR
mov x0, lr // pass the fault address stored in LR by the fault handler.
mov x1, xSELF // pass Thread::Current.
bl artThrowNullPointerExceptionFromSignal // (arg, Thread*).
brk 0
END art_quick_throw_null_pointer_exception_from_signal
extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ThrowNullPointerExceptionFromDexPC(/* check_address= */ false, 0U);
self->QuickDeliverException();
}
结合exception 的处理流程分析
本次的问题, 是jni call java发生的问题,
art在遍历调用栈时,遍历到jni 栈就结束了, HandlerQuickFramePc 就指回了jni方法,
所以就陷入了个循环

后续分析
那是不是,jni 都是如此?
写demo验证,
new Thread(){
@Override
public void run() {
test(MainActivity.this "test".getBytes());
}
}.start();
public final void onCallback(byte[] bytes){
throw new NullPointerException("test");
}
static jint test(JNIEnv *env, jobject thiz, jobject obj, jbyteArray data) {
env= nullptr;
int len = env->GetArrayLength(data);
jbyte* arr = new jbyte[len+1];
memset(arr, 0, len+1);
jbyteArray jarray = env->NewByteArray(len);
env->SetByteArrayRegion(jarray, 0, len , arr);
env->CallVoidMethod(obj, gNngOffsets.callback, jarray); //第一次
env->CallVoidMethod(obj, gNngOffsets.callback, jarray); //第二次
free(arr);
return result;
}
问题现象:
第一次回调onCallback 并没有 发生问题
第二次回调onCallback发生问题 ,将第一次的NullPointerException 打印了出来,
堆栈为:
03-22 17:10:11.054 8535 8592 F zygote64: java_vm_ext.cc:534] native: #00 pc 00000000003cff38 /system/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*)+208)
03-22 17:10:11.054 8535 8592 F zygote64: java_vm_ext.cc:534] native: #01 pc 00000000004a2980 /system/lib64/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+348)
03-22 17:10:11.054 8535 8592 F zygote64: java_vm_ext.cc:534] native: #02 pc 00000000002ffffc /system/lib64/libart.so (art::JavaVMExt::JniAbort(char const*, char const*)+1052)
03-22 17:10:11.054 8535 8592 F zygote64: java_vm_ext.cc:534] native: #03 pc 000000000030040c /system/lib64/libart.so (art::JavaVMExt::JniAbortV(char const*, char const*, std::__va_list)+116)
03-22 17:10:11.054 8535 8592 F zygote64: java_vm_ext.cc:534] native: #04 pc 000000000010f0ec /system/lib64/libart.so (art::ScopedCheck::AbortF(char const*, ...)+152)
03-22 17:10:11.054 8535 8592 F zygote64: java_vm_ext.cc:534] native: #05 pc 000000000010ec30 /system/lib64/libart.so (art::ScopedCheck::CheckThread(_JNIEnv*)+516)
03-22 17:10:11.054 8535 8592 F zygote64: java_vm_ext.cc:534] native: #06 pc 000000000010d0c4 /system/lib64/libart.so (art::ScopedCheck::Check(art::ScopedObjectAccess&, bool, char const*, art::JniValueType*)+648)
03-22 17:10:11.054 8535 8592 F zygote64: java_vm_ext.cc:534] native: #07 pc 00000000001125ec /system/lib64/libart.so (art::CheckJNI::CheckCallArgs(art::ScopedObjectAccess&, art::ScopedCheck&, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, art::InvokeType, art::VarArgs const*)+132)
03-22 17:10:11.055 8535 8592 F zygote64: java_vm_ext.cc:534] native: #08 pc 0000000000111468 /system/lib64/libart.so (art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, std::__va_list, art::Primitive::Type, art::InvokeType)+668)
03-22 17:10:11.055 8535 8592 F zygote64: java_vm_ext.cc:534] native: #09 pc 00000000000ffd34 /system/lib64/libart.so (art::CheckJNI::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+92)
03-22 17:10:11.055 8535 8592 F zygote64: java_vm_ext.cc:534] native: #10 pc 00000000000158e0 /data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==/lib/arm64/libnative-lib.so (_JNIEnv::CallVoidMethod(_jobject*, _jmethodID*, ...)+208)
03-22 17:10:11.055 8535 8592 F zygote64: java_vm_ext.cc:534] native: #11 pc 00000000000156c0 /data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==/lib/arm64/libnative-lib.so (???)
03-22 17:10:11.055 8535 8592 F zygote64: java_vm_ext.cc:534] native: #12 pc 00000000000006c8 /data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==/oat/arm64/base.odex (Java_com_example_nng_MainActivity_add__Lcom_example_nng_MainActivity_2II_3B+216)
03-22 17:10:11.055 8535 8592 F zygote64: java_vm_ext.cc:534] at com.example.nng.MainActivity.add(Native method)
03-22 17:10:11.055 8535 8592 F zygote64: java_vm_ext.cc:534] at com.example.nng.MainActivity$2.run(MainActivity.java:167)
03-22 17:10:11.055 8535 8592 F zygote64: java_vm_ext.cc:534]
分析
第一次回调, jump 到了jni, 异常没有被抛出来, 存在于当前Thread
第二次回调, checkjni, check 当前Thread, 发现异常,主动abort 当前进程
疑问:
那为什么 demo 会报问题, libandroid_runtime.so不会呢, objdump libandroid_runtime.so库
00000000000f42f4 <_ZN7_JNIEnv14CallVoidMethodEP8_jobjectP10_jmethodIDz>:
f42f4: d10483ff sub sp, sp, #0x120
f42f8: a9104ffc stp x28, x19, [sp,#256]
f42fc: a9117bfd stp x29, x30, [sp,#272]
f4300: 910443fd add x29, sp, #0x110
f4304: ad031fe6 stp q6, q7, [sp,#96]
f4308: ad0217e4 stp q4, q5, [sp,#64]
f430c: ad010fe2 stp q2, q3, [sp,#32]
f4310: ad0007e0 stp q0, q1, [sp]
f4314: a90a1fe6 stp x6, x7, [sp,#160]
f4318: a90917e4 stp x4, x5, [sp,#144]
f431c: f90047e3 str x3, [sp,#136]
f4320: d53bd053 mrs x19, tpidr_el0
f4324: f940166b ldr x11, [x19,#40]
f4328: 928004e8 mov x8, #0xffffffffffffffd8 // #-40
f432c: 910003e9 mov x9, sp
f4330: 910223ea add x10, sp, #0x88
f4334: f2dff008 movk x8, #0xff80, lsl #32
f4338: 910043ac add x12, x29, #0x10
f433c: 91020129 add x9, x9, #0x80
f4340: 9100a14a add x10, x10, #0x28
f4344: f81e83ab stur x11, [x29,#-24]
f4348: a93da3a9 stp x9, x8, [x29,#-40]
f434c: a93cabac stp x12, x10, [x29,#-56]
f4350: f9400008 ldr x8, [x0]
f4354: d100e3a9 sub x9, x29, #0x38
f4358: ad400121 ldp q1, q0, [x9]
f435c: d10183a3 sub x3, x29, #0x60
f4360: f940f908 ldr x8, [x8,#496]
f4364: ad3d03a1 stp q1, q0, [x29,#-96]
f4368: d63f0100 blr x8
f436c: f9401668 ldr x8, [x19,#40]
f4370: f85e83a9 ldur x9, [x29,#-24]
f4374: eb09011f cmp x8, x9
f4378: 540000a1 b.ne f438c <_ZN7_JNIEnv14CallVoidMethodEP8_jobjectP10_jmethodIDz+0x98>
f437c: a9517bfd ldp x29, x30, [sp,#272]
f4380: a9504ffc ldp x28, x19, [sp,#256]
f4384: 910483ff add sp, sp, #0x120
f4388: d65f03c0 ret
f438c: 94049bad bl 21b240 <__stack_chk_fail@plt>
看出是什么,但感觉不是走的checkjni流程走的是jni_internal 流程, 在art/runtime/jni/jni_env_ext.cc 中 添加log:
const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) {
const JNINativeInterface* override = JNIEnvExt::table_override_;
if (override != nullptr) {
return override;
}
LOG(ERROR)<<"check jni is " << check_jni;
return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface();
}
抓取log:
03-16 04:14:11.190 4492 4500 E CarPowerServic: check jni is 0
03-16 04:14:11.191 4492 4501 E CarPowerServic: check jni is 0
03-16 04:14:11.192 4492 4502 E CarPowerServic: check jni is 0
03-16 04:14:11.193 4492 4503 E CarPowerServic: check jni is 0
03-16 04:14:11.195 4492 4504 E CarPowerServic: check jni is 0
03-16 04:14:11.196 4492 4505 E CarPowerServic: check jni is 0
03-16 04:14:11.199 4492 4506 E CarPowerServic: check jni is 0
03-16 04:14:11.199 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.200 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.206 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.223 4492 4511 E CarPowerServic: check jni is 0
03-16 04:14:11.230 4492 4513 E CarPowerServic: check jni is 0
03-16 04:14:11.409 4492 4519 E CarPowerServic: check jni is 0
03-16 04:14:11.435 4492 4520 E CarPowerServic: check jni is 0
03-16 04:14:11.437 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.455 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.456 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.480 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.480 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.481 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.488 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.491 1535 1535 E com.android.ca: check jni is 0
03-16 04:14:11.697 4492 4492 E CarPowerServic: check jni is 0
03-16 04:14:11.704 4492 4492 E CarPowerServic: check jni is 0
果然发生问题的相关进程和线程check_jni 是关闭.
那为什么 check_jni 一个是打开的一个是关闭的呢?
梳理check_jni的设置流程:
第一次设置check_jni的位置
private final boolean startProcessLocked(......) {
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
......
}
.....
/frameworks/base/core/java/android/os/Process.java
ProcessStartResult-->star(... runtimeFlags ...)
ZygoteProcess--->start(... runtimeFlags ...)
-->startViaZygote(...runtimeFlags....)
ZygoteConnection--->processOneCommand
Zygote--->forkAndSpecialize() -->nativeForkAndSpecialize
com_android_internal_os_Zygote-->com_android_internal_os_Zygote_nativeForkAndSpecialize() ----> ForkAndSpecializeCommon()
Zygote-->callPostForkChildHooks() --->
ZygoteHooks -->postForkChild()---> nativePostForkChild()
dalvik_system_ZygoteHooks.cc
ZygoteHooks_nativePostForkChild(){
EnableDebugFeatures(runtime_flags);
}
static uint32_t EnableDebugFeatures(uint32_t runtime_flags) {
Runtime* const runtime = Runtime::Current();
if ((runtime_flags & DEBUG_ENABLE_CHECKJNI) != 0) {
JavaVMExt* vm = runtime->GetJavaVM();
if (!vm->IsCheckJniEnabled()) {
vm->SetCheckJniEnabled(true);
Thread::Current()->GetJniEnv()->SetCheckJniEnabled(true);
} else {
LOG(INFO) << "Not late-enabling -Xcheck:jni (already on)";
}
runtime_flags &= ~DEBUG_ENABLE_CHECKJNI;
}
第二次设置check_jni的位置
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote){
bool checkJni = false;
property_get("dalvik.vm.checkjni", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
checkJni = true;
} else if (strcmp(propBuf, "false") != 0) {
property_get("ro.kernel.android.checkjni", propBuf, "");
if (propBuf[0] == '1') {
checkJni = true;
}
}
ALOGV("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
if (checkJni) {
addOption("-Xcheck:jni");
}
}
从以上流程可以看出 和 check_jni有关系的是:
- ro.kernel.android.checkjni
- dalvik.vm.checkjni
- ApplicationInfo.FLAG_DEBUGGABLE
这三个元素,
adb 中查看property:
- ro.kernel.android.checkjni
- dalvik.vm.checkjni
实际山都没有配置, 查看第三个元素:
我的app 携带的 flag
Packages:
Package [com.android.car] (be22749):
userId=1000
sharedUser=SharedUserSetting{e2f33e5 android.uid.system/1000}
pkg=Package{4070eba com.android.car}
codePath=/system/priv-app/CarService
resourcePath=/system/priv-app/CarService
legacyNativeLibraryDir=/system/priv-app/CarService/lib
primaryCpuAbi=null
secondaryCpuAbi=null
versionCode=29 minSdk=29 targetSdk=29
versionName=10
splits=[base]
apkSigningVersion=3
applicationInfo=ApplicationInfo{b5a216b com.android.car}
flags=[ SYSTEM HAS_CODE PERSISTENT ALLOW_CLEAR_USER_DATA ]
........
demo app 携带的flag
Packages:
Package [com.example.nng] (69c4123):
userId=10014
pkg=Package{fea81a3 com.example.nng}
codePath=/data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==
resourcePath=/data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==
legacyNativeLibraryDir=/data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==/lib
primaryCpuAbi=arm64-v8a
secondaryCpuAbi=null
versionCode=1 minSdk=16 targetSdk=29
versionName=1.0
splits=[base]
apkSigningVersion=2
applicationInfo=ApplicationInfo{11883f4 com.example.nng}
flags=[ DEBUGGABLE HAS_CODE ALLOW_CLEAR_USER_DATA TEST_ONLY ALLOW_BACKUP ]
果然 demo app 是携带了DEBUGGABLE 的flag, 所以jni 能识别到异常
在简单验证一下, 将app改为debug 签名此flag不在存在
推荐博客:
https://blog.csdn.net/hl09083253cy/article/details/78590666

5240

被折叠的 条评论
为什么被折叠?



