linker学习(一)

System.loadLibrary解析

System.loadLibrary和load的区别

1
2
3
4
5
6
7
8
loadLibrary只需要传入native-lib
load需要传入so的绝对路径

load比loadLibrary少了查找so路径的过程
load只支持应用本地存储路径/data/data/package-name,或者是系统lib目录 /system/lib /vendor/lib
load不能加载sdcard中的so路径
loadLibrary加载的是一开始就打包apk或系统的so文件,load可以加载任意一个时刻的so文件
最终都是调用java.lang.Runtime中的nativeLoad

首先从java层 java/lang/System.java

1
System.loadLibrary("libnative-lib.so");
1
2
3
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}

/libcore/ojluni/src/main/java/java/lang/Runtime.java

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

998 synchronized void loadLibrary0(ClassLoader loader, String libname) {
//传入的so文件名,如果是路径,路径会抛出异常
999 if (libname.indexOf((int)File.separatorChar) != -1) {
1000 throw new UnsatisfiedLinkError(
1001 "Directory separator should not appear in library name: " + libname);
1002 }
1003 String libraryName = libname;
// 如果classLoader不为空,从classloader中获取so文件路径,首先调用BaseClassLoader的findLibray
// BaseClassLoader的findLibrary调用DexPathList的findLibrary,会加上lib前缀和so后缀
// 在BaseClassLoader的构造函数的时候DexPathList的构造函数调用,DexPathList的nativeLibraryDirectories字段保存apk中的so路径和系统 // so存放路径,应用的lib目录会放在第一个位置,so文件会先从应用本身的目录开始查找 不存在的话才会从系统lib路径(/system/lib、/vendor/lib等)
1004 if (loader != null) {
1005 String filename = loader.findLibrary(libraryName);
1006 if (filename == null) {
1007 // It's not necessarily true that the ClassLoader used
1008 // System.mapLibraryName, but the default setup does, and it's
1009 // misleading to say we didn't find "libMyLibrary.so" when we
1010 // actually searched for "liblibMyLibrary.so.so".
1011 throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
1012 System.mapLibraryName(libraryName) + "\"");
1013 }
1014 String error = doLoad(filename, loader); // 主要通过这个来寻找
1015 if (error != null) {
1016 throw new UnsatisfiedLinkError(error);
1017 }
1018 return;
1019 }
1020 // mapLibraryName 就是把 so加上lib + "libraryName" + ".so"
1021 String filename = System.mapLibraryName(libraryName);
1022 List<String> candidates = new ArrayList<String>();
1023 String lastError = null;
// getLibPaths 当ClassLoader为空的时候,获取system指定的vm library Path列表,少了app的lib路径
1024 for (String directory : getLibPaths()) {
1025 String candidate = directory + filename;
1026 candidates.add(candidate);
1027
1028 if (IoUtils.canOpenReadOnly(candidate)) {
1029 String error = doLoad(candidate, loader);
1030 if (error == null) {
1031 return; // We successfully loaded the library. Job done.
1032 }
1033 lastError = error;
1034 }
1035 }
1036
1037 if (lastError != null) {
1038 throw new UnsatisfiedLinkError(lastError);
1039 }
1040 throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
1041 }
doLoad
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
1070    private String doLoad(String name, ClassLoader loader) {
1071 // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
1072 // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.
1073
1074 // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
1075 // libraries with no dependencies just fine, but an app that has multiple libraries that
1076 // depend on each other needed to load them in most-dependent-first order.
1077
1078 // We added API to Android's dynamic linker so we can update the library path used for
1079 // the currently-running process. We pull the desired path out of the ClassLoader here
1080 // and pass it to nativeLoad so that it can call the private dynamic linker API.
1081
1082 // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
1083 // beginning because multiple apks can run in the same process and third party code can
1084 // use its own BaseDexClassLoader.
1085
1086 // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
1087 // dlopen(3) calls made from a .so's JNI_OnLoad to work too.
1088
1089 // So, find out what the native library search path is for the ClassLoader in question...
1090 String librarySearchPath = null;
1091 if (loader != null && loader instanceof BaseDexClassLoader) {
1092 BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
1093 librarySearchPath = dexClassLoader.getLdLibraryPath();
1094 }
1095 // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
1096 // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
1097 // internal natives.
1098 synchronized (this) {
1099 return nativeLoad(name, loader, librarySearchPath);
1100 }
1101 }
1102
1103 // TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
1104 private static native String nativeLoad(String filename, ClassLoader loader,
1105 String librarySearchPath);
doLoad 对应的native方法 JVM_NativeLoad

/art/runtime/openjdkjvm/OpenjdkJvm.cc

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


JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
323 jstring javaFilename,
324 jobject javaLoader,
325 jstring javaLibrarySearchPath) {
// 将jstring的javaFilename 转换为c++的 string filename
326 ScopedUtfChars filename(env, javaFilename);
327 if (filename.c_str() == NULL) {
328 return NULL;
329 }
330
331 std::string error_msg;
332 {
333 art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
//这边是实际执行so加载的操作调用
334 bool success = vm->LoadNativeLibrary(env,
335 filename.c_str(),
336 javaLoader,
337 javaLibrarySearchPath,
338 &error_msg);
339 if (success) {
340 return nullptr;
341 }
342 }
343
344 // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
345 env->ExceptionClear();
346 return env->NewStringUTF(error_msg.c_str());
347}
/art/runtime/java_vm_ext.cc
LoadNativeLibrary
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
162
163
164
165
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
797 const std::string& path,
798 jobject class_loader,
799 jstring library_path,
800 std::string* error_msg) {
801 error_msg->clear();
802
803 // See if we've already loaded this library. If we have, and the class loader
804 // matches, return successfully without doing anything.
805 // TODO: for better results we should canonicalize the pathname (or even compare
806 // inodes). This implementation is fine if everybody is using System.loadLibrary.
807 SharedLibrary* library;
808 Thread* self = Thread::Current();
809 {
810 // TODO: move the locking (and more of this logic) into Libraries.
811 MutexLock mu(self, *Locks::jni_libraries_lock_);
812 library = libraries_->Get(path);
813 }
814 void* class_loader_allocator = nullptr;
815 {
816 ScopedObjectAccess soa(env);
817 // As the incoming class loader is reachable/alive during the call of this function,
818 // it's okay to decode it without worrying about unexpectedly marking it alive.
819 ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);
820
821 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
822 if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
823 loader = nullptr;
824 class_loader = nullptr;
825 }
826
827 class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
828 CHECK(class_loader_allocator != nullptr);
829 }
830 if (library != nullptr) {
831 // Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
832 if (library->GetClassLoaderAllocator() != class_loader_allocator) {
833 // The library will be associated with class_loader. The JNI
834 // spec says we can't load the same library into more than one
835 // class loader.
836 StringAppendF(error_msg, "Shared library \"%s\" already opened by "
837 "ClassLoader %p; can't open in ClassLoader %p",
838 path.c_str(), library->GetClassLoader(), class_loader);
839 LOG(WARNING) << error_msg;
840 return false;
841 }
842 VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
843 << " ClassLoader " << class_loader << "]";
844 if (!library->CheckOnLoadResult()) {
845 StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
846 "to load \"%s\"", path.c_str());
847 return false;
848 }
849 return true;
850 }
851
852 // Open the shared library. Because we're using a full path, the system
853 // doesn't have to search through LD_LIBRARY_PATH. (It may do so to
854 // resolve this library's dependencies though.)
855
856 // Failures here are expected when java.library.path has several entries
857 // and we have to hunt for the lib.
858
859 // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
860 // class unloading. Libraries will only be unloaded when the reference count (incremented by
861 // dlopen) becomes zero from dlclose.
862
863 Locks::mutator_lock_->AssertNotHeld(self);
864 const char* path_str = path.empty() ? nullptr : path.c_str();
865 bool needs_native_bridge = false;
866 void* handle = android::OpenNativeLibrary(env,
867 runtime_->GetTargetSdkVersion(),
868 path_str,
869 class_loader,
870 library_path,
871 &needs_native_bridge,
872 error_msg);
873
874 VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";
875
876 if (handle == nullptr) {
877 VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
878 return false;
879 }
880
881 if (env->ExceptionCheck() == JNI_TRUE) {
882 LOG(ERROR) << "Unexpected exception:";
883 env->ExceptionDescribe();
884 env->ExceptionClear();
885 }
886 // Create a new entry.
887 // TODO: move the locking (and more of this logic) into Libraries.
888 bool created_library = false;
889 {
890 // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
891 std::unique_ptr<SharedLibrary> new_library(
892 new SharedLibrary(env,
893 self,
894 path,
895 handle,
896 needs_native_bridge,
897 class_loader,
898 class_loader_allocator));
899
900 MutexLock mu(self, *Locks::jni_libraries_lock_);
901 library = libraries_->Get(path);
902 if (library == nullptr) { // We won race to get libraries_lock.
903 library = new_library.release();
904 libraries_->Put(path, library);
905 created_library = true;
906 }
907 }
908 if (!created_library) {
909 LOG(INFO) << "WOW: we lost a race to add shared library: "
910 << "\"" << path << "\" ClassLoader=" << class_loader;
911 return library->CheckOnLoadResult();
912 }
913 VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
914
915 bool was_successful = false;
916 void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
917 if (sym == nullptr) {
918 VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
919 was_successful = true;
920 } else {
921 // Call JNI_OnLoad. We have to override the current class
922 // loader, which will always be "null" since the stuff at the
923 // top of the stack is around Runtime.loadLibrary(). (See
924 // the comments in the JNI FindClass function.)
925 ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
926 self->SetClassLoaderOverride(class_loader);
927
928 VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
929 typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
930 JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
931 int version = (*jni_on_load)(this, nullptr);
932
933 if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
934 // Make sure that sigchain owns SIGSEGV.
935 EnsureFrontOfChain(SIGSEGV);
936 }
937
938 self->SetClassLoaderOverride(old_class_loader.get());
939
940 if (version == JNI_ERR) {
941 StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
942 } else if (JavaVMExt::IsBadJniVersion(version)) {
943 StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
944 path.c_str(), version);
945 // It's unwise to call dlclose() here, but we can mark it
946 // as bad and ensure that future load attempts will fail.
947 // We don't know how far JNI_OnLoad got, so there could
948 // be some partially-initialized stuff accessible through
949 // newly-registered native method calls. We could try to
950 // unregister them, but that doesn't seem worthwhile.
951 } else {
952 was_successful = true;
953 }
954 VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
955 << " from JNI_OnLoad in \"" << path << "\"]";
956 }
957
958 library->SetResult(was_successful);
959 return was_successful;
960}
1
2
3
4
5
LoadNativeLibrary主要任务是
1. 先判断是否加载过so,并且判断classLoader要匹配,同一个so不能被不同的classLoader加载
2.没加载过so,调用OpenNativeLibrary来加载,参数path.c_str()传递的是动态库的全路径,之所以还用提供搜索路径,因为包含依赖库
3.加载成功后,创建new SharedLibrary,然后查看这个so有没有JNI_OnLoad方法,有的话就调用
4.查看调用JNI_OnLoad的结果