Java 调用Native函数,实际就是 JNI 调用。
我们将关注 Java端如何把参数传递到 Native,Java调用Native函数时,额外的做了哪些事情。
在前面分析Native 调用Java 函数时,直接打断点,就能得到调用 backtrace,那是得益于 GDB 对 Native代码的调试支持,
可以根据包含 symbols的 so库,自动帮忙我们理清 pc 对应的代码以及其所在文件,行号,函数等信息;这极大的帮助了我们分析流程;
而 Java 调用 Native 就不那么方便的调试了;因为:1.GDB 并不是调试 java 代码的工具 2.当 java代码存在native code时,其native code
是存放在 oat 文件中,虽然其是一个 ELF文件,但是其代码和都嵌入新建的 "oatdata" 和 “oatexec” 段,其组织结构并不能被 GDB 所识别;
JNI 方法的执行有两种情况:
1.在 java 中声明的 native 限定的方法在APP安装时,已经被编译了,那么从其他 java 方法调用该方法时,会直接跳转到其native code;
2.声明为 native 的java方法没有被编译,那么会经过 trampoline 以及 art_jni_dlsym_lookup_stub 配合完成调用;
在安装APP,编译dex时,根据compiler-filter决定是否编译 JNI method:
bool CompilerFilter::IsJniCompilationEnabled(Filter filter) {
switch (filter) {
case CompilerFilter::kVerifyNone:
case CompilerFilter::kVerifyAtRuntime: return false;
case CompilerFilter::kVerifyProfile:
case CompilerFilter::kInterpretOnly:
case CompilerFilter::kSpaceProfile:
case CompilerFilter::kSpace:
case CompilerFilter::kBalanced:
case CompilerFilter::kTime:
case CompilerFilter::kSpeedProfile:
case CompilerFilter::kSpeed:
case CompilerFilter::kEverythingProfile:
case CompilerFilter::kEverything: return true;
}
UNREACHABLE();
}
static bool InstructionSetHasGenericJniStub(InstructionSet isa) {
switch (isa) {
case kArm:
case kArm64:
case kThumb2:
case kMips:
case kMips64:
case kX86:
case kX86_64: return true;
default: return false;
}
}
如果compiler-filter是下面的 10种的一个,则必定会编译 JNI method;
如果是 kVerifyNone 或者 kVerifyAtRuntime ,且是所支持的指令集,则不会编译 JNI method;
在这里我们先分析第一种情况,即 Native java Method被编译的情况,此时,如果其他java 函数调用该函数,
会直接跳转到其被编译出来的 native 代码;
举个栗子:
SystemClock.java 的一个 native 函数 : native public static long uptimeMillis();
Thread 1 "miui.yellowpage" hit Breakpoint 1, art::JniMethodStart (self=0x7f86e96a00) at art/runtime/entrypoints/quick/quick_jni_entrypoints.cc:39
39 if (!native_method->IsFastNative()) {
(gdb) bt
#0 art::JniMethodStart (self=0x7f86e96a00) at art/runtime/entrypoints/quick/quick_jni_entrypoints.cc:39
#1 0x00000000751aa338 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) p self->tlsPtr_->managed_stack->top_quick_frame_
$2 = (art::ArtMethod **) 0x7ff85caa80
(gdb) x 0x7ff85caa80
0x7ff85caa80: 0x7147c530
(gdb) art_get_method_name_by_method_id 0x7147c530
android.os.SystemClock.uptimeMillis "()J"
JNI Method的编译是通过 ArtJniCompileMethodInternal 这个函数来为Jni Method生成代码的,我们先了解一下 JNI method的编译流程:
看下运行时 这个函数的Native code,实际上这个 code 就是 uptimeMillis() 这个函数被编译成native code在 oat文件中的存放;
72f2c000-748ae000 r--p 00000000 103:1d 2065 /system/framework/arm64/boot-framework.oat
748ae000-759d9000 r-xp 01982000 103:1d 2065 /system/framework/arm64/boot-framework.oat
759d9000-759da000 r--p 02aad000 103:1d 2065 /system/framework/arm64/boot-framework.oat
759da000-759db000 rw-p 02aae000 103:1d 2065 /system/framewor