System.LoadLibrary学习

调用分析

先看源码

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

1
2
3
4
5
6
7
@CallerSensitive
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}

//将libname加上前缀和后缀,即`lib<libname>.so`
public static native String mapLibraryName(String libname);

System 的 loadLibrary 最终调用的是 Runtime 中的 loadLibrary0 方法。

mapLibraryName 方法主要是给 libname 加上 “lib” 的前缀和 “.so” 的后缀。所以我们加载 so 库时,不用带 “lib” 和 “.so” 的字符串。

调用了/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
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
synchronized void loadLibrary0(ClassLoader loader, String libname) {
// 检测是否存在lib, 前是有对libname进行过处理的.
if (libname.indexOf((int)File.separatorChar) != -1) {
throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
String libraryName = libname;
if (loader != null) {
// 如果classloader不为空,则从classloader中获取so文件路径
String filename = loader.findLibrary(libraryName);
if (filename == null) {
// It's not necessarily true that the ClassLoader used
// System.mapLibraryName, but the default setup does, and it's
// misleading to say we didn't find "libMyLibrary.so" when we
// actually searched for "liblibMyLibrary.so.so".
throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
System.mapLibraryName(libraryName) + "\"");
}
// 调用nativeLoad来进行load这个库
String error = nativeLoad(filename, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
// 否则通过getLibPaths方式获取到so路径
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : getLibPaths()) {
// 将文件夹路径和so文件名组成绝对路径
// 文件夹路径通过getLibPaths()调用initLibPaths()获得
String candidate = directory + filename;
candidates.add(candidate);

if (IoUtils.canOpenReadOnly(candidate)) {
// 调用nativeLoad来进行load这个库
String error = nativeLoad(candidate, loader);
if (error == null) {
return; // We successfully loaded the library. Job done.
}
lastError = error;
}
}

if (lastError != null) {
throw new UnsatisfiedLinkError(lastError);
}
throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}

private volatile String[] mLibPaths = null;

private String[] getLibPaths() {
if (mLibPaths == null) {
synchronized(this) {
if (mLibPaths == null) {
mLibPaths = initLibPaths();
}
}
}
return mLibPaths;
}
// 在多线程环境下,当多个线程同时访问 getLibPaths() 方法时,可能会导致重复初始化 mLibPaths 数组,从而引发不一致的结果或潜在的竞态条件问题。
// 通过使用两个嵌套的if语句和synchronized关键字,可以确保只有一个线程能够执行初始化过程,从而避免了竞态条件。
// 通过这样的双重检查和同步块的方式,可以确保在多线程环境下只有一个线程执行初始化过程,从而保证了线程安全性,同时避免了不必要的初始化开销。

private static String[] initLibPaths() {
String javaLibraryPath = System.getProperty("java.library.path");
if (javaLibraryPath == null) {
return EmptyArray.STRING;
}
String[] paths = javaLibraryPath.split(":");
// Add a '/' to the end of each directory so we don't have to do it every time.
for (int i = 0; i < paths.length; ++i) {
if (!paths[i].endsWith("/")) {
paths[i] += "/";
}
}
return paths;
}

loadLibrary0 在加载 so 时,先判断 loader 是否为 null 。如果不为 null,就通过 loader 去找到 so 的绝对路径,然后再加载。如果为 null,就从 /system/lib64/ 中去找是否存在要加载的 so 。如果都没有找到就抛出异常。

ClassLoader.findLibrary():

ClassLoader集成自BaseDexClassLoader,所以调用的是BaseDexClassLoader.findLibrary()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent, boolean isTrusted) {
super(parent);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);

if (reporter != null) {
reportClassLoaderChain();
}
}

@Override
public String findLibrary(String name) {
return pathList.findLibrary(name);
}

BaseDexClassLoader.findLibrary() 最终由 DexPathList.findLibrary() 实现。而 DexPathList.findLibrary() 是在 nativeLibraryPathElements 中遍历查找 存在的 so。

nativeLibraryPathElements 的值来自 librarySearchPath 和 System.getProperty(“java.library.path”) 。librarySearchPath 主要是 /data/app/<packagename>-xyz==/lib/arm64:/data/app/<packagename>-xyz==/base.apk!/lib/arm64-v8a的路径。System.getProperty(“java.library.path”) 为/system/lib64/

所以 DexPathList 是在这几个目录下查找 so 是否存在。如果存在就返回其绝对路径即可。

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
DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
...
//librarySearchPath="/data/app/<packagename>-xyz==/lib/arm64:/data/app/<packagename>-xyz==/base.apk!/lib/arm64-v8a"
this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true); //"java.library.path"="/system/lib64"
List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);

this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
...
}

private static List<File> splitPaths(String searchPath, boolean directoriesOnly) {
List<File> result = new ArrayList<>();

if (searchPath != null) {
for (String path : searchPath.split(File.pathSeparator)) {
if (directoriesOnly) {
try {
StructStat sb = Libcore.os.stat(path);
if (!S_ISDIR(sb.st_mode)) {
continue;
}
} catch (ErrnoException ignored) {
continue;
}
}
result.add(new File(path));
}
}

return result;
}

private static NativeLibraryElement[] makePathElements(List<File> files) {
NativeLibraryElement[] elements = new NativeLibraryElement[files.size()];
int elementsPos = 0;
for (File file : files) {
String path = file.getPath();

if (path.contains(zipSeparator)) { //zipSeparator="!/"
String split[] = path.split(zipSeparator, 2);
File zip = new File(split[0]);
String dir = split[1];
elements[elementsPos++] = new NativeLibraryElement(zip, dir);
} else if (file.isDirectory()) {
// We support directories for looking up native libraries.
elements[elementsPos++] = new NativeLibraryElement(file);
}
}
if (elementsPos != elements.length) {
elements = Arrays.copyOf(elements, elementsPos);
}
return elements;
}

public String findLibrary(String libraryName) {
String fileName = System.mapLibraryName(libraryName);

for (NativeLibraryElement element : nativeLibraryPathElements) {
String path = element.findNativeLibrary(fileName);

if (path != null) {
return path;
}
}

return null;
}

static class NativeLibraryElement {
public NativeLibraryElement(File dir) {
this.path = dir;
this.zipDir = null;
}

public NativeLibraryElement(File zip, String zipDir) {
this.path = zip;
this.zipDir = zipDir;

if (zipDir == null) {
throw new IllegalArgumentException();
}
}

public synchronized void maybeInit() {
if (initialized) {
return;
}

if (zipDir == null) {
initialized = true;
return;
}

try {
urlHandler = new ClassPathURLStreamHandler(path.getPath());
} catch (IOException ioe) {
System.logE("Unable to open zip file: " + path, ioe);
urlHandler = null;
}

initialized = true;
}

public String findNativeLibrary(String name) {
maybeInit();

if (zipDir == null) {
String entryPath = new File(path, name).getPath();
if (IoUtils.canOpenReadOnly(entryPath)) {
return entryPath;
}
} else if (urlHandler != null) {
String entryName = zipDir + '/' + name;
if (urlHandler.isEntryStored(entryName)) {
return path.getPath() + zipSeparator + entryName;
}
}

return null;
}
}

找到 so 的绝对路径后就是加载 so 了。so 的加载是通过 nativeLoad 方法实现的。java 层的 nativeLoad 对应的就是 c 层的 Runtime_nativeLoad() 方法。

libcore/ojluni/src/main/native/Runtime.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
#define NATIVE_METHOD(className, functionName, signature) \
{ #functionName, signature, (void*)(className ## _ ## functionName) }


JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader)
{
return JVM_NativeLoad(env, javaFilename, javaLoader);
}

static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
NATIVE_METHOD(Runtime, gc, "()V"),
NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
NATIVE_METHOD(Runtime, nativeLoad,
"(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)"
"Ljava/lang/String;"),
};

void register_java_lang_Runtime(JNIEnv* env) {
jniRegisterNativeMethods(env, "java/lang/Runtime", gMethods, NELEM(gMethods));
}

上面代码可以看出 Runtime_nativeLoad 调用的是 JVM_NativeLoad 方法。而 JVM_NativeLoad 真正实现so 的加载是在 vm->LoadNativeLibrary 方法中。

art/openjdkjvm/OpenjdkJvm.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
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader) {
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == NULL) {
return NULL;
}

std::string error_msg;
{
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
&error_msg);
if (success) {
return nullptr;
}
}

// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
env->ExceptionClear();
return env->NewStringUTF(error_msg.c_str());
}

art/runtime/java_vm_ext.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
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
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
std::string* error_msg) {
...

// Open the shared library. Because we're using a full path, the system
// doesn't have to search through LD_LIBRARY_PATH. (It may do so to
// resolve this library's dependencies though.)

// Failures here are expected when java.library.path has several entries
// and we have to hunt for the lib.

// Below we dlopen but there is no paired dlclose, this would be necessary if we supported
// class unloading. Libraries will only be unloaded when the reference count (incremented by
// dlopen) becomes zero from dlclose.

// Retrieve the library path from the classloader, if necessary.
ScopedLocalRef<jstring> library_path(env, GetLibrarySearchPath(env, class_loader));

Locks::mutator_lock_->AssertNotHeld(self);
const char* path_str = path.empty() ? nullptr : path.c_str();
bool needs_native_bridge = false;
void* handle = android::OpenNativeLibrary(env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
library_path.get(),
&needs_native_bridge,
error_msg);

VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";

if (handle == nullptr) {
VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
return false;
}

if (env->ExceptionCheck() == JNI_TRUE) {
LOG(ERROR) << "Unexpected exception:";
env->ExceptionDescribe();
env->ExceptionClear();
}
// Create a new entry.
// TODO: move the locking (and more of this logic) into Libraries.
bool created_library = false;
{
// Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
std::unique_ptr<SharedLibrary> new_library(
new SharedLibrary(env,
self,
path,
handle,
needs_native_bridge,
class_loader,
class_loader_allocator));

MutexLock mu(self, *Locks::jni_libraries_lock_);
library = libraries_->Get(path);
if (library == nullptr) { // We won race to get libraries_lock.
library = new_library.release();
libraries_->Put(path, library);
created_library = true;
}
}
if (!created_library) {
LOG(INFO) << "WOW: we lost a race to add shared library: "
<< "\"" << path << "\" ClassLoader=" << class_loader;
return library->CheckOnLoadResult();
}
VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";

bool was_successful = false;
void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
was_successful = true;
} else {
// Call JNI_OnLoad. We have to override the current class
// loader, which will always be "null" since the stuff at the
// top of the stack is around Runtime.loadLibrary(). (See
// the comments in the JNI FindClass function.)
ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
self->SetClassLoaderOverride(class_loader);

VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
int version = (*jni_on_load)(this, nullptr);

if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
// Make sure that sigchain owns SIGSEGV.
EnsureFrontOfChain(SIGSEGV);
}

self->SetClassLoaderOverride(old_class_loader.get());

if (version == JNI_ERR) {
StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
} else if (JavaVMExt::IsBadJniVersion(version)) {
StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
path.c_str(), version);
// It's unwise to call dlclose() here, but we can mark it
// as bad and ensure that future load attempts will fail.
// We don't know how far JNI_OnLoad got, so there could
// be some partially-initialized stuff accessible through
// newly-registered native method calls. We could try to
// unregister them, but that doesn't seem worthwhile.
} else {
was_successful = true;
}
VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
<< " from JNI_OnLoad in \"" << path << "\"]";
}

library->SetResult(was_successful);
return was_successful;
}


class SharedLibrary {
void SetNeedsNativeBridge(bool needs) {
needs_native_bridge_ = needs;
}

bool NeedsNativeBridge() const {
return needs_native_bridge_;
}

// No mutator lock since dlsym may block for a while if another thread is doing dlopen.
void* FindSymbol(const std::string& symbol_name, const char* shorty = nullptr)
REQUIRES(!Locks::mutator_lock_) {
return NeedsNativeBridge()
? FindSymbolWithNativeBridge(symbol_name.c_str(), shorty)
: FindSymbolWithoutNativeBridge(symbol_name.c_str());
}

// No mutator lock since dlsym may block for a while if another thread is doing dlopen.
void* FindSymbolWithoutNativeBridge(const std::string& symbol_name)
REQUIRES(!Locks::mutator_lock_) {
CHECK(!NeedsNativeBridge());

return dlsym(handle_, symbol_name.c_str());
}

void* FindSymbolWithNativeBridge(const std::string& symbol_name, const char* shorty)
REQUIRES(!Locks::mutator_lock_) {
CHECK(NeedsNativeBridge());

uint32_t len = 0;
return android::NativeBridgeGetTrampoline(handle_, symbol_name.c_str(), shorty, len);
}
}
  1. 检查该动态库是否已加载;
  2. 通过dlopen打开动态共享库;
  3. 创建SharedLibrary共享库,并添加到libraries_列表;
  4. 通过dlsym获取JNI_OnLoad符号所对应的方法, 并调用该方法.

总结

1
2
3
4
5
6
7
8
System.loadLibrary()
└─Runtime.loadLibrary0()
└─Runtime_nativeLoad()
└─JVM_NativeLoad()
└─LoadNativeLibrary()
├─fdlopen()
├─fdlsym()
└─fJNI_OnLoad()

相关文章:

https://gityuan.com/2017/03/26/load_library/

https://blog.csdn.net/QQxiaoqiang1573/article/details/101781380