动态注册jni函数时的流程

学习过程的记录,以下代码参考自aosp8.1

jni函数绑定主要是有两种方式。静态注册和动态注册的jni函数

静态注册的jni函数有两次绑定,在类加载时会绑定一次,在jni函数第一次被调用时,art会对jni函数的地址进行查询并绑定

动态注册的jni函数也有两次绑定,在类加载时绑定一次,第二次由开发人员的代码主动绑定。

DexClassLoader是安卓的一种类加载器,然后用他的loadClass函数来进行类加载处理,而DexClassLoader中没有看到loadClass函数。通过在父类中寻找。最终发现loadClass在ClassLoad类中。继承关系如下

DexClassLoader–>BaseDexClassLoader–>ClassLoader

然后开始看看loadClass的实现

1
2
3
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}

调用了两个参数的重载。继续看另外一个重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//初次加载时,c肯定是null,必然走的findClass
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}

初次加载时,必然是取不到这个class的。所以初次执行时,必然走的findClass。所以接下来找这个findClass的代码,这个findClass的实现在BaseDexClassLoader中。

1
2
3
4
5
6
7
8
9
10
11
12
13
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException(
"Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}

上面我们可以看到关键的一句

Class c = pathList.findClass(name, suppressedExceptions);

Class c又是调用的pathList.findClass,继续找pathList的findClass,

DexPathList pathList

找到这个pathList的定义后,继续去DexPathList找findClass的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public Class findClass(String name, List<Throwable> suppressed) {
DexPathList.Element[] arr$ = this.dexElements;
int len$ = arr$.length;

for(int i$ = 0; i$ < len$; ++i$) {
DexPathList.Element element = arr$[i$];
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, this.definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}

if (this.dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(this.dexElementsSuppressedExceptions));
}

return null;
}

下面是关键的class来源。

Class clazz = dex.loadClassBinaryName(name, this.definingContext, suppressed);

这里可以看到clazz的来源,然后继续找loadClassBinaryName,在DexFile中

1
2
3
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, this.mCookie, suppressed);
}

然后继续看defineClass的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static Class defineClass(String name, ClassLoader loader, long cookie, List<Throwable> suppressed) {
Class result = null;

try {
result = defineClassNative(name, loader, cookie);
} catch (NoClassDefFoundError var7) {
if (suppressed != null) {
suppressed.add(var7);
}
} catch (ClassNotFoundException var8) {
if (suppressed != null) {
suppressed.add(var8);
}
}

return result;
}

然后继续看defineClassNative的实现

1
private static native Class defineClassNative(String var0, ClassLoader var1, long var2) throws ClassNotFoundException, NoClassDefFoundError;

这里这个defineClassNative是一个native函数了。那么实现的代码就要去找c++代码了。DexFile的对应c++代码在dalvik_system_DexFile.cc中。继续找c++的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static jclass DexFile_defineClassNative(JNIEnv* env,
jclass,
jstring javaName,
jobject javaLoader,
jobject cookie,
jobject dexFile) {
std::vector<const DexFile*> dex_files;
const OatFile* oat_file;
if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {
VLOG(class_linker) << "Failed to find dex_file";
DCHECK(env->ExceptionCheck());
return nullptr;
}

ScopedUtfChars class_name(env, javaName);
if (class_name.c_str() == nullptr) {
VLOG(class_linker) << "Failed to find class_name";
return nullptr;
}
const std::string descriptor(DotToDescriptor(class_name.c_str()));
const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
for (auto& dex_file : dex_files) {
const DexFile::ClassDef* dex_class_def =
OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);
if (dex_class_def != nullptr) {
ScopedObjectAccess soa(env);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
ObjPtr<mirror::DexCache> dex_cache =
class_linker->RegisterDexFile(*dex_file, class_loader.Get());
if (dex_cache == nullptr) {
// OOME or InternalError (dexFile already registered with a different class loader).
soa.Self()->AssertPendingException();
return nullptr;
}
ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),
descriptor.c_str(),
hash,
class_loader,
*dex_file,
*dex_class_def);
// Add the used dex file. This only required for the DexFile.loadClass API since normal
// class loaders already keep their dex files live.
class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),
class_loader.Get());
if (result != nullptr) {
VLOG(class_linker) << "DexFile_defineClassNative returning " << result
<< " for " << class_name.c_str();
return soa.AddLocalReference<jclass>(result);
}
}
}
VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();
return nullptr;
}

这里这段代码比较多。下面是关键代码

1
ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),                                   descriptor.c_str(),                                          hash,                                               class_loader,                                                *dex_file,                                             *dex_class_def);

继续看DefineClass的实现代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

mirror::Class* ClassLinker::DefineClass(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
StackHandleScope<3> hs(self);
auto klass = hs.NewHandle<mirror::Class>(nullptr);

// Load the class from the dex file.
if (UNLIKELY(!init_done_)) {
// finish up init of hand crafted class_roots_
if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
klass.Assign(GetClassRoot(kJavaLangObject));
} else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
klass.Assign(GetClassRoot(kJavaLangClass));
} else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass.Assign(GetClassRoot(kJavaLangString));
} else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) {
klass.Assign(GetClassRoot(kJavaLangRefReference));
} else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
klass.Assign(GetClassRoot(kJavaLangDexCache));
} else if (strcmp(descriptor, "Ldalvik/system/ClassExt;") == 0) {
klass.Assign(GetClassRoot(kDalvikSystemClassExt));
}
}

if (klass == nullptr) {
// Allocate a class with the status of not ready.
// Interface object should get the right size here. Regular class will
// figure out the right size later and be replaced with one of the right
// size when the class becomes resolved.
klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
}
if (UNLIKELY(klass == nullptr)) {
self->AssertPendingOOMException();
return nullptr;
}
// Get the real dex file. This will return the input if there aren't any callbacks or they do
// nothing.
DexFile const* new_dex_file = nullptr;
DexFile::ClassDef const* new_class_def = nullptr;
// TODO We should ideally figure out some way to move this after we get a lock on the klass so it
// will only be called once.
Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor,
klass,
class_loader,
dex_file,
dex_class_def,
&new_dex_file,
&new_class_def);
// Check to see if an exception happened during runtime callbacks. Return if so.
if (self->IsExceptionPending()) {
return nullptr;
}
ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get());
if (dex_cache == nullptr) {
self->AssertPendingException();
return nullptr;
}
klass->SetDexCache(dex_cache);
SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get());

// Mark the string class by setting its access flag.
if (UNLIKELY(!init_done_)) {
if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass->SetStringClass();
}
}

ObjectLock<mirror::Class> lock(self, klass);
klass->SetClinitThreadId(self->GetTid());
// Make sure we have a valid empty iftable even if there are errors.
klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());

// Add the newly loaded class to the loaded classes table.
ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash);
if (existing != nullptr) {
// We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
return EnsureResolved(self, descriptor, existing);
}

// Load the fields and other things after we are inserted in the table. This is so that we don't
// end up allocating unfree-able linear alloc resources and then lose the race condition. The
// other reason is that the field roots are only visited from the class table. So we need to be
// inserted before we allocate / fill in these fields.
LoadClass(self, *new_dex_file, *new_class_def, klass);
if (self->IsExceptionPending()) {
VLOG(class_linker) << self->GetException()->Dump();
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}

// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
CHECK(klass->IsLoaded());

// At this point the class is loaded. Publish a ClassLoad event.
// Note: this may be a temporary class. It is a listener's responsibility to handle this.
Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(klass);

// Link the class (if necessary)
CHECK(!klass->IsResolved());
// TODO: Use fast jobjects?
auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);

MutableHandle<mirror::Class> h_new_class = hs.NewHandle<mirror::Class>(nullptr);
if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
self->AssertNoPendingException();
CHECK(h_new_class != nullptr) << descriptor;
CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor;

// Instrumentation may have updated entrypoints for all methods of all
// classes. However it could not update methods of this class while we
// were loading it. Now the class is resolved, we can update entrypoints
// as required by instrumentation.
if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
// We must be in the kRunnable state to prevent instrumentation from
// suspending all threads to update entrypoints while we are doing it
// for this class.
DCHECK_EQ(self->GetState(), kRunnable);
Runtime::Current()->GetInstrumentation()->InstallStubsForClass(h_new_class.Get());
}

/*
* We send CLASS_PREPARE events to the debugger from here. The
* definition of "preparation" is creating the static fields for a
* class and initializing them to the standard default values, but not
* executing any code (that comes later, during "initialization").
*
* We did the static preparation in LinkClass.
*
* The class has been prepared and resolved but possibly not yet verified
* at this point.
*/
Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(klass, h_new_class);

// Notify native debugger of the new class and its layout.
jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get());

return h_new_class.Get();
}

这一段代码比较多,可以从descriptor的使用看出,这个参数是要加载的类描述 。但是关键点也很明显,那就是LoadClass。

LoadClass(self, *new_dex_file, *new_class_def, klass);

继续看LoadClass的实现代码

1
2
3
4
5
6
7
8
9
10
void ClassLinker::LoadClass(Thread* self,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass) {
const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
if (class_data == nullptr) {
return; // no fields or methods - for example a marker interface
}
LoadClassMembers(self, dex_file, class_data, klass);
}

在这里就看到了我们关心的加载函数的处理了,LoadClassMembers也就是加载类函数的地方了。继续往下看实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
void ClassLinker::LoadClassMembers(Thread* self,
const DexFile& dex_file,
const uint8_t* class_data,
Handle<mirror::Class> klass) {
{
// Note: We cannot have thread suspension until the field and method arrays are setup or else
// Class::VisitFieldRoots may miss some fields or methods.
ScopedAssertNoThreadSuspension nts(__FUNCTION__);
// Load static fields.
// We allow duplicate definitions of the same field in a class_data_item
// but ignore the repeated indexes here, b/21868015.
LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader());
ClassDataItemIterator it(dex_file, class_data);
LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self,
allocator,
it.NumStaticFields());
size_t num_sfields = 0;
uint32_t last_field_idx = 0u;
for (; it.HasNextStaticField(); it.Next()) {
uint32_t field_idx = it.GetMemberIndex();
DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier.
if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) {
DCHECK_LT(num_sfields, it.NumStaticFields());
LoadField(it, klass, &sfields->At(num_sfields));
++num_sfields;
last_field_idx = field_idx;
}
}

// Load instance fields.
LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,
allocator,
it.NumInstanceFields());
size_t num_ifields = 0u;
last_field_idx = 0u;
for (; it.HasNextInstanceField(); it.Next()) {
uint32_t field_idx = it.GetMemberIndex();
DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier.
if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) {
DCHECK_LT(num_ifields, it.NumInstanceFields());
LoadField(it, klass, &ifields->At(num_ifields));
++num_ifields;
last_field_idx = field_idx;
}
}

if (UNLIKELY(num_sfields != it.NumStaticFields()) ||
UNLIKELY(num_ifields != it.NumInstanceFields())) {
LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor()
<< " (unique static fields: " << num_sfields << "/" << it.NumStaticFields()
<< ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")";
// NOTE: Not shrinking the over-allocated sfields/ifields, just setting size.
if (sfields != nullptr) {
sfields->SetSize(num_sfields);
}
if (ifields != nullptr) {
ifields->SetSize(num_ifields);
}
}
// Set the field arrays.
klass->SetSFieldsPtr(sfields);
DCHECK_EQ(klass->NumStaticFields(), num_sfields);
klass->SetIFieldsPtr(ifields);
DCHECK_EQ(klass->NumInstanceFields(), num_ifields);
// Load methods.
bool has_oat_class = false;
const OatFile::OatClass oat_class =
(Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler())
? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class)
: OatFile::OatClass::Invalid();
const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr;
klass->SetMethodsPtr(
AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()),
it.NumDirectMethods(),
it.NumVirtualMethods());
size_t class_def_method_index = 0;
uint32_t last_dex_method_index = DexFile::kDexNoIndex;
size_t last_class_def_method_index = 0;
// TODO These should really use the iterators.
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
LoadMethod(dex_file, it, klass, method);
LinkCode(this, method, oat_class_ptr, class_def_method_index);
uint32_t it_method_index = it.GetMemberIndex();
if (last_dex_method_index == it_method_index) {
// duplicate case
method->SetMethodIndex(last_class_def_method_index);
} else {
method->SetMethodIndex(class_def_method_index);
last_dex_method_index = it_method_index;
last_class_def_method_index = class_def_method_index;
}
class_def_method_index++;
}
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
LoadMethod(dex_file, it, klass, method);
DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
LinkCode(this, method, oat_class_ptr, class_def_method_index);
class_def_method_index++;
}
DCHECK(!it.HasNext());
}
// Ensure that the card is marked so that remembered sets pick up native roots.
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get());
self->AllowThreadSuspension();
}

这里的代码比较多。但是整体内容前面部分是准备好jni函数绑定需要用到的field。后面是函数的加载。下面是函数加载部分的关键部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
LoadMethod(dex_file, it, klass, method);
LinkCode(this, method, oat_class_ptr, class_def_method_index);
uint32_t it_method_index = it.GetMemberIndex();
if (last_dex_method_index == it_method_index) {
// duplicate case
method->SetMethodIndex(last_class_def_method_index);
} else {
method->SetMethodIndex(class_def_method_index);
last_dex_method_index = it_method_index;
last_class_def_method_index = class_def_method_index;
}
class_def_method_index++;
}
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
LoadMethod(dex_file, it, klass, method);
DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
LinkCode(this, method, oat_class_ptr, class_def_method_index);
class_def_method_index++;
}

可以大概看出意思,前面是遍历所有普通函数进行加载,后面是遍历所有虚函数进行加载。而加载的方式主要是两段代码,下面列出关键代码

1
2
LoadMethod(dex_file, it, klass, method);
LinkCode(this, method, oat_class_ptr, class_def_method_index);

这两个函数我们都看一下具体实现里面做了些什么,先是LoadMethod的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void ClassLinker::LoadMethod(const DexFile& dex_file,
const ClassDataItemIterator& it,
Handle<mirror::Class> klass,
ArtMethod* dst) {
uint32_t dex_method_idx = it.GetMemberIndex();
const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);

ScopedAssertNoThreadSuspension ants("LoadMethod");
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());

dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_);

uint32_t access_flags = it.GetMethodAccessFlags();

if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
// Set finalizable flag on declaring class.
if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
// Void return type.
if (klass->GetClassLoader() != nullptr) { // All non-boot finalizer methods are flagged.
klass->SetFinalizable();
} else {
std::string temp;
const char* klass_descriptor = klass->GetDescriptor(&temp);
// The Enum class declares a "final" finalize() method to prevent subclasses from
// introducing a finalizer. We don't want to set the finalizable flag for Enum or its
// subclasses, so we exclude it here.
// We also want to avoid setting the flag on Object, where we know that finalize() is
// empty.
if (strcmp(klass_descriptor, "Ljava/lang/Object;") != 0 &&
strcmp(klass_descriptor, "Ljava/lang/Enum;") != 0) {
klass->SetFinalizable();
}
}
}
} else if (method_name[0] == '<') {
// Fix broken access flags for initializers. Bug 11157540.
bool is_init = (strcmp("<init>", method_name) == 0);
bool is_clinit = !is_init && (strcmp("<clinit>", method_name) == 0);
if (UNLIKELY(!is_init && !is_clinit)) {
LOG(WARNING) << "Unexpected '<' at start of method name " << method_name;
} else {
if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
<< klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation();
access_flags |= kAccConstructor;
}
}
}
dst->SetAccessFlags(access_flags);
}

这里大概就是构造函数的加载处理和普通函数的加载处理,然后我们再看看另一个函数LinkCode的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

static void LinkCode(ClassLinker* class_linker,
ArtMethod* method,
const OatFile::OatClass* oat_class,
uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) {
Runtime* const runtime = Runtime::Current();
if (runtime->IsAotCompiler()) {
// The following code only applies to a non-compiler runtime.
return;
}
// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
if (oat_class != nullptr) {
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
oat_method.LinkMethod(method);
}

// Install entry point from interpreter.
const void* quick_code = method->GetEntryPointFromQuickCompiledCode();
bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code);

if (!method->IsInvokable()) {
EnsureThrowsInvocationError(class_linker, method);
return;
}

if (method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
} else if (quick_code == nullptr && method->IsNative()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
} else if (enter_interpreter) {
// Set entry point from compiled code if there's no code or in interpreter only mode.
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
}

if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative();

if (enter_interpreter || quick_code == nullptr) {
// We have a native method here without code. Then it should have either the generic JNI
// trampoline as entrypoint (non-static), or the resolution trampoline (static).
// TODO: this doesn't handle all the cases where trampolines may be installed.
const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
DCHECK(class_linker->IsQuickGenericJniStub(entry_point) ||
class_linker->IsQuickResolutionStub(entry_point));
}
}
}

这里可以看到重点,如果是native的处理

1
2
3
4
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative();
}

这里就是每个类加载时会触发的jni函数绑定,继续看UnregisterNative的实现

1
2
3
4
5
void ArtMethod::UnregisterNative() {
CHECK(IsNative() && !IsFastNative()) << PrettyMethod();
// restore stub to lookup native pointer via dlsym
SetEntryPointFromJni(GetJniDlsymLookupStub());
}

这里我们就知道了。jni函数的静态加载和动态加载的第一次加载绑定都会调用的位置就是SetEntryPointFromJni这里,初次绑定函数存放到了GetJniDlsymLookupStub()这个函数返回的指针。然后看看是存放到了什么地方

1
2
3
static inline const void* GetJniDlsymLookupStub() {
return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub);
}

确定了这是一个指针。这个指针指向了一个函数。初次加载是把函数存放到了哪里,对应的函数是一个汇编函数。静态注册的第一次调用就会直接触发这个函数来获取加载函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    .extern artFindNativeMethod
ENTRY art_jni_dlsym_lookup_stub
push {r0, r1, r2, r3, lr} @ spill regs
.cfi_adjust_cfa_offset 20
.cfi_rel_offset r0, 0
.cfi_rel_offset r1, 4
.cfi_rel_offset r2, 8
.cfi_rel_offset r3, 12
.cfi_rel_offset lr, 16
sub sp, #12 @ pad stack pointer to align frame
.cfi_adjust_cfa_offset 12
blx artFindNativeMethod
mov r12, r0 @ save result in r12
add sp, #12 @ restore stack pointer
.cfi_adjust_cfa_offset -12
cbz r0, 1f @ is method code null?
pop {r0, r1, r2, r3, lr} @ restore regs
.cfi_adjust_cfa_offset -20
.cfi_restore r0
.cfi_restore r1
.cfi_restore r2
.cfi_restore r3
.cfi_restore lr
bx r12 @ if non-null, tail call to method's code
1:
pop {r0, r1, r2, r3, pc} @ restore regs and return to caller to handle exception
END art_jni_dlsym_lookup_stub

看到这里blx调用了artFindNativeMethod。继续再看看这个里面做了什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extern "C" const void* artFindNativeMethod() {
Thread* self = Thread::Current();
#else
extern "C" const void* artFindNativeMethod(Thread* self) {
DCHECK_EQ(self, Thread::Current());
#endif
Locks::mutator_lock_->AssertNotHeld(self); // We come here as Native.
ScopedObjectAccess soa(self);

ArtMethod* method = self->GetCurrentMethod(nullptr);
DCHECK(method != nullptr);

// Lookup symbol address for method, on failure we'll return null with an exception set,
// otherwise we return the address of the method we found.
void* native_code = soa.Vm()->FindCodeForNativeMethod(method);
if (native_code == nullptr) {
self->AssertPendingException();
return nullptr;
}
// Register so that future calls don't come here
return method->RegisterNative(native_code, false);
}

这里就看到了熟悉的RegisterNative了,到这里才进行了真正的函数的绑定。而前面FindCodeForNativeMethod这个函数用来获取绑定函数的符号地址。将查询到的地址交给RegisterNative进行绑定,继续看是如何查询函数符号地址的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) {
CHECK(m->IsNative());
mirror::Class* c = m->GetDeclaringClass();
// If this is a static method, it could be called before the class has been initialized.
CHECK(c->IsInitializing()) << c->GetStatus() << " " << m->PrettyMethod();
std::string detail;
Thread* const self = Thread::Current();
void* native_method = libraries_->FindNativeMethod(self, m, detail);
if (native_method == nullptr) {
// Lookup JNI native methods from native TI Agent libraries. See runtime/ti/agent.h for more
// information. Agent libraries are searched for native methods after all jni libraries.
native_method = FindCodeForNativeMethodInAgents(m);
}
// Throwing can cause libraries_lock to be reacquired.
if (native_method == nullptr) {
LOG(ERROR) << detail;
self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
}
return native_method;
}

这里又看到了FindNativeMethod,然后继续深入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void* FindNativeMethod(Thread* self, ArtMethod* m, std::string& detail)
REQUIRES(!Locks::jni_libraries_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
std::string jni_short_name(m->JniShortName());
std::string jni_long_name(m->JniLongName());
mirror::ClassLoader* const declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
ScopedObjectAccessUnchecked soa(Thread::Current());
void* const declaring_class_loader_allocator =
Runtime::Current()->GetClassLinker()->GetAllocatorForClassLoader(declaring_class_loader);
CHECK(declaring_class_loader_allocator != nullptr);
// TODO: Avoid calling GetShorty here to prevent dirtying dex pages?
const char* shorty = m->GetShorty();
{
// Go to suspended since dlsym may block for a long time if other threads are using dlopen.
ScopedThreadSuspension sts(self, kNative);
void* native_code = FindNativeMethodInternal(self,
declaring_class_loader_allocator,
shorty,
jni_short_name,
jni_long_name);
if (native_code != nullptr) {
return native_code;
}
}
detail += "No implementation found for ";
detail += m->PrettyMethod();
detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
return nullptr;
}

这里继续找关键的函数FindNativeMethodInternal,然后再深入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void* FindNativeMethodInternal(Thread* self,
void* declaring_class_loader_allocator,
const char* shorty,
const std::string& jni_short_name,
const std::string& jni_long_name)
REQUIRES(!Locks::jni_libraries_lock_)
REQUIRES(!Locks::mutator_lock_) {
MutexLock mu(self, *Locks::jni_libraries_lock_);
for (const auto& lib : libraries_) {
SharedLibrary* const library = lib.second;
// Use the allocator address for class loader equality to avoid unnecessary weak root decode.
if (library->GetClassLoaderAllocator() != declaring_class_loader_allocator) {
// We only search libraries loaded by the appropriate ClassLoader.
continue;
}
// Try the short name then the long name...
const char* arg_shorty = library->NeedsNativeBridge() ? shorty : nullptr;
void* fn = library->FindSymbol(jni_short_name, arg_shorty);
if (fn == nullptr) {
fn = library->FindSymbol(jni_long_name, arg_shorty);
}
if (fn != nullptr) {
VLOG(jni) << "[Found native code for " << jni_long_name
<< " in \"" << library->GetPath() << "\"]";
return fn;
}
}
return nullptr;
}

最后看到了,是通过FindSymbol这个函数来获取绑定函数的符号地址。

静态函数绑定流程到这里就完成了。

动态函数绑定是通过jni中的RegisterNatives来进行绑定.然后找到art/runtime/jni_internal.cc下的RegisterNatives

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
static jint RegisterNatives(JNIEnv* env,
jclass java_class,
const JNINativeMethod* methods,
jint method_count) {
if (UNLIKELY(method_count < 0)) {
JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
method_count);
return JNI_ERR; // Not reached except in unit tests.
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
ScopedObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
if (UNLIKELY(method_count == 0)) {
LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for "
<< c->PrettyDescriptor();
return JNI_OK;
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
for (jint i = 0; i < method_count; ++i) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
const void* fnPtr = methods[i].fnPtr;
if (UNLIKELY(name == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i);
return JNI_ERR;
} else if (UNLIKELY(sig == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i);
return JNI_ERR;
} else if (UNLIKELY(fnPtr == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i);
return JNI_ERR;
}
bool is_fast = false;
// Notes about fast JNI calls:
//
// On a normal JNI call, the calling thread usually transitions
// from the kRunnable state to the kNative state. But if the
// called native function needs to access any Java object, it
// will have to transition back to the kRunnable state.
//
// There is a cost to this double transition. For a JNI call
// that should be quick, this cost may dominate the call cost.
//
// On a fast JNI call, the calling thread avoids this double
// transition by not transitioning from kRunnable to kNative and
// stays in the kRunnable state.
//
// There are risks to using a fast JNI call because it can delay
// a response to a thread suspension request which is typically
// used for a GC root scanning, etc. If a fast JNI call takes a
// long time, it could cause longer thread suspension latency
// and GC pauses.
//
// Thus, fast JNI should be used with care. It should be used
// for a JNI call that takes a short amount of time (eg. no
// long-running loop) and does not block (eg. no locks, I/O,
// etc.)
//
// A '!' prefix in the signature in the JNINativeMethod
// indicates that it's a fast JNI call and the runtime omits the
// thread state transition from kRunnable to kNative at the
// entry.
if (*sig == '!') {
is_fast = true;
++sig;
}

// Note: the right order is to try to find the method locally
// first, either as a direct or a virtual method. Then move to
// the parent.
ArtMethod* m = nullptr;
bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled();
for (ObjPtr<mirror::Class> current_class = c.Get();
current_class != nullptr;
current_class = current_class->GetSuperClass()) {
// Search first only comparing methods which are native.
m = FindMethod<true>(current_class.Ptr(), name, sig);
if (m != nullptr) {
break;
}

// Search again comparing to all methods, to find non-native methods that match.
m = FindMethod<false>(current_class.Ptr(), name, sig);
if (m != nullptr) {
break;
}

if (warn_on_going_to_parent) {
LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. "
<< "This is slow, consider changing your RegisterNatives calls.";
warn_on_going_to_parent = false;
}
}

if (m == nullptr) {
c->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
LOG(ERROR)
<< "Failed to register native method "
<< c->PrettyDescriptor() << "." << name << sig << " in "
<< c->GetDexCache()->GetLocation()->ToModifiedUtf8();
ThrowNoSuchMethodError(soa, c.Get(), name, sig, "static or non-static");
return JNI_ERR;
} else if (!m->IsNative()) {
LOG(ERROR)
<< "Failed to register non-native method "
<< c->PrettyDescriptor() << "." << name << sig
<< " as native";
ThrowNoSuchMethodError(soa, c.Get(), name, sig, "native");
return JNI_ERR;
}

VLOG(jni) << "[Registering JNI native method " << m->PrettyMethod() << "]";

if (UNLIKELY(is_fast)) {
// There are a few reasons to switch:
// 1) We don't support !bang JNI anymore, it will turn to a hard error later.
// 2) @FastNative is actually faster. At least 1.5x faster than !bang JNI.
// and switching is super easy, remove ! in C code, add annotation in .java code.
// 3) Good chance of hitting DCHECK failures in ScopedFastNativeObjectAccess
// since that checks for presence of @FastNative and not for ! in the descriptor.
LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod();
is_fast = false;
// TODO: make this a hard register error in the future.
}

const void* final_function_ptr = m->RegisterNative(fnPtr, is_fast);
UNUSED(final_function_ptr);
}
return JNI_OK;
}

这里代码有点多。关键函数是最后的m->RegisterNative进行绑定,这个m对象是一个ArtMethod类,继续往里面看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
CHECK(IsNative()) << PrettyMethod();
CHECK(!IsFastNative()) << PrettyMethod();
CHECK(native_method != nullptr) << PrettyMethod();
if (is_fast) {
AddAccessFlags(kAccFastNative);
}
void* new_native_method = nullptr;
Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
native_method,
/*out*/&new_native_method);
SetEntryPointFromJni(new_native_method);
return new_native_method;
}

这个时候发现,其实动态注册最后还是调用了SetEntryPointFromJni这个函数来进行绑定的。

这篇太长了。frida来hook的部分下一篇再记录吧。