为 app_process 配置正确的 ClassLoader namespace

app_process 中使用 System.loadLibrary 加载 .so 库时,若默认路径找不到库文件,可通过以下代码动态添加 native library 的搜索路径:

1
2
3
4
5
6
7
8
private fun fix() {
val pathList = on(javaClass.classLoader).field("pathList").get<Any>()
val dexElements = on(pathList).field("dexElements").get<Array<Any>>()
val apkPath = on(dexElements[0]).field("path").get<File>().absolutePath
val cpuAbi = Reflect.onClass("android.os.SystemProperties").call("get", "ro.product.cpu.abi")

Reflect.on(pathList).call("addNativePath", listOf("$apkPath!/lib/$cpuAbi"))
}

但这样存在一个问题,System.loadLibrary(A) 之后,如果 A 中调用 dlopen(B) 尝试加载另一个打包进来的 so,依然会报找不到的问题,应该是 addNativePath 不能影响到 classloader namespace 导致的,需要寻找更 Trivial 的方法


通过 这篇文章 了解到,应用启动时,通过 ApplicationLoaders 创建默认的 CLNS,同时传入动态库等信息,如果我们能模仿这个行为,应该就能获得一个正常的 CLNS。

先看看正常的创建流程,这里从 LoadedApk 出发:

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
private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
...

boolean registerAppInfoToArt = false;
if (mDefaultClassLoader == null) {
...

Pair<List<ClassLoader>, List<ClassLoader>> sharedLibraries =
createSharedLibrariesLoaders(mApplicationInfo.sharedLibraryInfos, isBundledApp,
librarySearchPath, libraryPermittedPath);

mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
mApplicationInfo.classLoaderName, sharedLibraries.first, nativeSharedLibraries,
sharedLibraries.second);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);

setThreadPolicy(oldPolicy);
// Setup the class loader paths for profiling.
registerAppInfoToArt = true;
}

...
}

ApplicationLoaders 中调用 ClassLoaderFactory#createClassLoader

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
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
String classLoaderName, List<ClassLoader> sharedLibraries,
List<String> nativeSharedLibraries,
List<ClassLoader> sharedLibrariesLoadedAfterApp) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
* don't use that and can happily (and more efficiently) use the
* bootstrap class loader.
*/
ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();

synchronized (mLoaders) {
if (parent == null) {
parent = baseParent;
}

/*
* If we're one step up from the base class loader, find
* something in our cache. Otherwise, we create a whole
* new ClassLoader for the zip archive.
*/
if (parent == baseParent) {
ClassLoader loader = mLoaders.get(cacheKey);
if (loader != null) {
return loader;
}

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);

ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
nativeSharedLibraries, sharedLibrariesLoadedAfterApp);

Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setLayerPaths");
GraphicsEnvironment.getInstance().setLayerPaths(
classloader, librarySearchPath, libraryPermittedPath);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

if (cacheKey != null) {
mLoaders.put(cacheKey, classloader);
}
return classloader;
}

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
ClassLoader loader = ClassLoaderFactory.createClassLoader(
zip, null, parent, classLoaderName, sharedLibraries,
null /*sharedLibrariesLoadedAfterApp*/);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return loader;
}
}

最终走到 ClassLoaderFactory#createClassloaderNamespace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Creates class loaders.
*
* @hide
*/
public class ClassLoaderFactory {
...

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static native String createClassloaderNamespace(ClassLoader classLoader,
int targetSdkVersion,
String librarySearchPath,
String libraryPermittedPath,
boolean isNamespaceShared,
String dexPath,
String sonameList);
}

列表!

参数 类型 取值
classLoader ClassLoader 字面意思
targetSdkVersion int 字面意思
librarySearchPath String System.getProperty("java.library.path");多个则 joinTo(":")
libraryPermittedPath String null
isNamespacesShared boolean isBundled
dexPath String Apk zip 路径
sonameList String mApplicationInfo.sharedLibraryInfos.map { it.name }.joinTo(":")
1
2
3
4
5
6
7
8
9
Reflect.onClass("com.android.internal.os.ClassLoaderFactory").call(
"createClassLoaderNamespace",
javaClass.classLoader,
Build.VERSION.SDK_INT,
System.getProperty("java.class.path")?.plus("!/lib/${Build.SUPPORTED_ABIS[0]}"),
null,
true,
System.getProperty("java.class.path"),
)

上面为 Android R 的代码,之后的版本多一个参数,完整版参见:AProc


为 app_process 配置正确的 ClassLoader namespace
https://neo.mufanc.xyz/posts/41575/
作者
Mufanc
发布于
2025年4月2日
许可协议