Android——APK 在32bit/64bit平台 动态库问题
目前64bit android系统也慢慢的多了,看到也有apk声称支持64bit system,然后就往里面打包搞了个arm64-v8a 目录,放了个64bit的so,但是apk代码里面却不按规范去load so ,导致一系列 file not found 异常~
撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/47101815本文来自 【jscese】的博客!
apk lib目录:
先看下apk中的lib打包的目录:
依次代表不同类型的cpu
PMS安装路径:
pms install 流程比较繁杂,只关注so相关的scanPackageDirtyLI函数中:
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {...//invoke installer to do the actual installation //作为外部apk 创建data目录相关项//\frameworks\native\cmds\installd\commands.c install()中创建int ret = **createDataDirsLI**(pkgName, pkg.applicationInfo.uid,pkg.applicationInfo.seinfo);...if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {...setBundledAppAbisAndRoots(pkg, pkgSetting);...setNativeLibraryPaths(pkg); }else{setNativeLibraryPaths(pkg);...if (isAsec) {copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);} else {copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,nativeLibraryRoot, abiList, useIsaSpecificSubdirs);}setNativeLibraryPaths(pkg);if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);final int[] userIds = sUserManager.getUserIds();synchronized (mInstallLock) {// Create a native library symlink only if we have native libraries// and if the native libraries are 32 bit libraries. We do not provide// this symlink for 64 bit libraries.if (pkg.applicationInfo.primaryCpuAbi != null &&**!VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)**) {final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;for (int userId : userIds) {
if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,"Failed linking native library dir (user=" + userId + ")");}}}}}
}
- 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
看下system app 的安装配置函数 setBundledAppAbisAndRoots:
/*** Calculate the abis and roots for a bundled app. These can uniquely* be determined from the contents of the system partition, i.e whether* it contains 64 or 32 bit shared libraries etc. We do not validate any* of this information, and instead assume that the system was built* sensibly.*/private void setBundledAppAbisAndRoots(PackageParser.Package pkg,PackageSetting pkgSetting) {final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());// If "/system/lib64/apkname" exists, assume that is the per-package// native library directory to use; otherwise use "/system/lib/apkname".final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);setBundledAppAbi(pkg, apkRoot, apkName);// pkgSetting might be null during rescan following uninstall of updates// to a bundled app, so accommodate that possibility. The settings in// that case will be established later from the parsed package.//// If the settings aren't null, sync them up with what we've just derived.// note that apkRoot isn't stored in the package settings.if (pkgSetting != null) {pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;}}
- 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
主要是在setBundledAppAbi中:
/*** Deduces the ABI of a bundled app and sets the relevant fields on the* parsed pkg object.** @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem}* under which system libraries are installed.* @param apkName the name of the installed package.*/private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {final File codeFile = new File(pkg.codePath);
...if (has64BitLibs && !has32BitLibs) {// The package has 64 bit libs, but not 32 bit libs. Its primary// ABI should be 64 bit. We can safely assume here that the bundled// native libraries correspond to the most preferred ABI in the list.pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];pkg.applicationInfo.secondaryCpuAbi = null;} else if (has32BitLibs && !has64BitLibs) {// The package has 32 bit libs but not 64 bit libs. Its primary// ABI should be 32 bit.pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];pkg.applicationInfo.secondaryCpuAbi = null;} else if (has32BitLibs && has64BitLibs) {// The application has both 64 and 32 bit bundled libraries. We check// here that the app declares multiArch support, and warn if it doesn't.//// We will be lenient here and record both ABIs. The primary will be the// ABI that's higher on the list, i.e, a device that's configured to prefer// 64 bit apps will see a 64 bit primary ABI,if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {Slog.e(TAG, "Package: " + pkg + " has multiple bundled libs, but is not multiarch.");}if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];} else {pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];}} else {pkg.applicationInfo.primaryCpuAbi = null;pkg.applicationInfo.secondaryCpuAbi = null;}}
- 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
根据file 查找 确定primaryCpuAbi secondaryCpuAbi 变量值,这个也就决定了 这个 apk 由64bit 还是32bit 的zygote去fork 还有nativelibrary 查找的path
其中nativelibrary的几个主要函数 setNativeLibraryPaths:
/*** Derive and set the location of native libraries for the given package,* which varies depending on where and how the package was installed.*/private void setNativeLibraryPaths(PackageParser.Package pkg) {final ApplicationInfo info = pkg.applicationInfo;final String codePath = pkg.codePath;final File codeFile = new File(codePath);final boolean bundledApp = isSystemApp(info) && !isUpdatedSystemApp(info);final boolean asecApp = isForwardLocked(info) || isExternal(info);info.nativeLibraryRootDir = null;info.nativeLibraryRootRequiresIsa = false;info.nativeLibraryDir = null;info.secondaryNativeLibraryDir = null;if (isApkFile(codeFile)) {// Monolithic installif (bundledApp) {// If "/system/lib64/apkname" exists, assume that is the per-package// native library directory to use; otherwise use "/system/lib/apkname".final String apkRoot = calculateBundledApkRoot(info.sourceDir);final boolean is64Bit = VMRuntime.is64BitInstructionSet(getPrimaryInstructionSet(info));// This is a bundled system app so choose the path based on the ABI.// if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this// is just the default path.final String apkName = deriveCodePathName(codePath);final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,apkName).getAbsolutePath();if (info.secondaryCpuAbi != null) {final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),secondaryLibDir, apkName).getAbsolutePath();}} else if (asecApp) {info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME).getAbsolutePath();} else {final String apkName = deriveCodePathName(codePath);info.nativeLibraryRootDir = new File(mAppLib32InstallDir, apkName).getAbsolutePath();}info.nativeLibraryRootRequiresIsa = false;info.nativeLibraryDir = info.nativeLibraryRootDir;} else {// Cluster installinfo.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();info.nativeLibraryRootRequiresIsa = true;info.nativeLibraryDir = new File(info.nativeLibraryRootDir,getPrimaryInstructionSet(info)).getAbsolutePath();if (info.secondaryCpuAbi != null) {info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();}}}
- 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
根据pkg application info 来确定nativelibrarydir 依赖info中的 info.primaryCpuAbi
private static String getPrimaryInstructionSet(ApplicationInfo info) {if (info.primaryCpuAbi == null) {return getPreferredInstructionSet();}return VMRuntime.getInstructionSet(info.primaryCpuAbi);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
非system apk 会调用 NativeLibraryHelper 的 findSupportedAbi 去解析 .apk 文件,根据系统suportabilist 去查找 lib目录下的打包子目录 找到匹配的abi
向文章开头的那个 lib目录 ,在64bit 机器上suportabilist为:
public static final String[] SUPPORTED_ABIS = getStringList("ro.product.cpu.abilist", ",");
- 1
root@:/ # getprop ro.product.cpu.abilist
arm64-v8a,armeabi-v7a,armeabi
- 1
- 2
会匹配arm64-v8a 赋值给 info.primaryCpuAbi
copyNativeBinariesForSupportedAbi 会去copy 前面匹配的lib 目录到本地
最后设置NativeLibraryPaths ,
如果匹配的是64bit的,也就是arm64-v8a 那么就不为/data/data/../lib 建立软链接,这是与32bit 不同的地方
system.loadlibrary
作为动态库加载的标准接口,直接看实现:
public static void loadLibrary(String libName) {Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());}
- 1
- 2
- 3
到Runtime.java中:
/** Searches for and loads the given shared library using the given ClassLoader.*/void loadLibrary(String libraryName, ClassLoader loader) {if (loader != null) {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) + "\"");}String error = doLoad(filename, loader);if (error != null) {throw new UnsatisfiedLinkError(error);}return;}String filename = System.mapLibraryName(libraryName);List<String> candidates = new ArrayList<String>();String lastError = null;for (String directory : mLibPaths) {String candidate = directory + filename;candidates.add(candidate);if (IoUtils.canOpenReadOnly(candidate)) {String error = doLoad(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);}
- 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
这里的 ClassLoader loader 实际上会在 apk启动的时候 初始化好一些相关的 子类 父类 还有参数
大体记录一下 启动时 初始流程 :
ActivityThread.java - handleBindApplication final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,appContext.getClassLoader(), false, true, false);ContextImpl.java -getClassLoader()
LoadedApk.java -getClassLoader() : mLibDir = aInfo.nativeLibraryDir;
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,mBaseClassLoader);
ApplicationLoaders.java -getClassLoader(...)PathClassLoader pathClassloader = new PathClassLoader(zip, libPath, parent); //这里的libPath 就是上面传下来的aInfo.nativeLibraryDirpublic class PathClassLoader extends BaseDexClassLoader
public class BaseDexClassLoader extends ClassLoader
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
loader.findLibrary(libraryName);
/*** Finds the named native code library on any of the library* directories pointed at by this instance. This will find the* one in the earliest listed directory, ignoring any that are not* readable regular files.** @return the complete path to the library or {@code null} if no* library was found*/public String findLibrary(String libraryName) {String fileName = System.mapLibraryName(libraryName);for (File directory : nativeLibraryDirectories) {String path = new File(directory, fileName).getPath();if (IoUtils.canOpenReadOnly(path)) {
return path;}}
return null;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这里的nativeLibraryDirectories 即为前面一系列 构造时 设置了值 其中就有 aInfo.nativeLibraryDir
后面的逻辑就不去叙述了, 根据名字在这个目录下去找 ,然后调用到本地JNI 最终调用 dlopen 加载打开so,必须是相同位数, 而这个关乎当前进程是属于64bit 还是 32bit,这个会在zygote fork时区分, 同样也是由PMS解析时得到的 info.primaryCpuAbi
AMS 请求zygote fork app process选择
只关心 相关代码 startProcessLocked函数:
private final void startProcessLocked(ProcessRecord app, String hostingType,String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;if (requiredAbi == null) {requiredAbi = Build.SUPPORTED_ABIS[0];}String instructionSet = null;if (app.info.primaryCpuAbi != null) {instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);}app.gids = gids;app.requiredAbi = requiredAbi;app.instructionSet = instructionSet;// Start the process. It will either succeed and return a result containing// the PID of the new process, or else throw a RuntimeException.boolean isActivityProcess = (entryPoint == null);if (entryPoint == null) entryPoint = "android.app.ActivityThread";checkTime(startTime, "startProcess: asking zygote to start proc");Process.ProcessStartResult startResult = Process.start(entryPoint,app.processName, uid, uid, gids, debugFlags, mountExternal,app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,app.info.dataDir, entryPointArgs);checkTime(startTime, "startProcess: returned from zygote!");...
}
- 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
Process中真正的socket 请求实现:
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
- 1
openZygoteSocketIfNeeded 会根据传下来的abi 去选择 通信的socket
而在64bit 机器上,启动时会 启动 两个 zygote service ,用于接收 64 32 的apk 请求:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygoteclass mainsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart mediaonrestart restart netdservice zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondaryclass mainsocket zygote_secondary stream 660 root systemonrestart restart zygote
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
可以看到两个 zygote 进程,基本一致 ,区别在于 64bit 32bit ,注册socket不同
关于这两个 zygote 进程启动时的socket 注册 就不多说了
暂时先记录这么多吧~
Android——APK 在32bit/64bit平台 动态库问题相关推荐
- 【Android NDK 开发】NDK 交叉编译 ( Ubuntu 中交叉编译动态库 | Android Studio 中配置使用第三方动态库 )
文章目录 I . 动态库 与 静态库 II . 编译动态库 III. Android Studio 使用第三方动态库 IV . Android Studio 关键代码 V . 博客资源 I . 动态库 ...
- 利用android studio 生成 JNI需要的动态库so文件
JNI: Java Native Interface, 提供了java语言和其他语言(例如c和c++)进行相互调用的方式. 本文是用java调用c生成的so模式.其中,编译so文件过程如下: 1) ...
- 将java编译成so库_利用android studio 生成 JNI需要的动态库so文件
JNI: Java Native Interface, 提供了java语言和其他语言(例如c和c++)进行相互调用的方式. 本文是用java调用c生成的so模式.其中,编译so文件过程如下: 1) ...
- linux 生成dll文件,Linux和Windows平台 动态库.so和.dll文件的生成
Linux动态库的生成 1. 纯cpp文件打包动态库 将所有cpp文件和所需要的头文件放在同一文件夹,然后执行下面命令 gcc -shared - fpic *.c -o xxx.so: g++ -s ...
- 【Android 逆向】Android 逆向通用工具开发 ( Windows 平台静态库程序类型 | 编译逆向工具依赖的 Windows 平台静态库程序 )
文章目录 一.Windows 平台静态库程序类型 二.编译逆向工具依赖的 Windows 平台静态库程序 一.Windows 平台静态库程序类型 在 Android 逆向通用工具 MobileGame ...
- Linux和Windows平台 动态库.so和.dll文件的生成
Linux动态库的生成 1. 纯cpp文件打包动态库 将所有cpp文件和所需要的头文件放在同一文件夹,然后执行下面命令 gcc -shared - fpic *.c -o xxx.so: g++ -s ...
- 【Android NDK 开发】Android Studio 使用 CMake 导入动态库 ( 构建脚本路径配置 | 指定动态库查找路径 | 链接动态库 )
文章目录 I . CMake 引入动态库与静态库区别 II . Android Studio 中 CMake 引入动态库流程 III . 指定动态库查找路径 IV . 链接函数库 V . 完整代码示例 ...
- 【Android NDK 开发】Android.mk 配置静态库 ( Android Studio 配置静态库 | 配置动态库与静态库区别 | 动态库与静态库打包对比 )
文章目录 I . Android Studio 中使用 Android.mk 配置静态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...
- 【Android NDK 开发】Android.mk 配置动态库 ( Android Studio 配置动态库 | 动态库加载版本限制 | 本章仅做参考推荐使用 CMake 配置动态库 )
文章目录 I . Android Studio 中使用 Android.mk 配置动态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...
最新文章
- centOS外部浏览器无法访问tomcat8000端口解决办法
- Spring的环绕通知
- Python_基础知识储备
- 多重判定系数怎么求_关于多重共线性
- Java之String,StringBuffer,StringBuilder
- lamda获取参数集合去空_(转)Java8使用lambda表达式进行集合的遍历
- 最小生成树 洛谷P3366【模板】最小生成树 洛谷P2820 局域网
- fastclick.js插件使用简单说明
- paip.c#图片裁剪
- unity 入门学习之(二)脚本学习
- ubuntu16.04安装搜狗拼音输入法
- Rust异步之Future
- Android Studio使用WIFI调试应用
- 大学毕业生找不到工作的原因何在?
- 推荐三款动态壁纸软件,足够让你的桌面惊艳!
- .net 操作达梦数据库
- 关于服务端与客户端的数据交互
- 什么是k8s(Kubernetes)
- CNCC 2018 科技展盛况空前,近 100 家企业汇聚一堂
- 网络路径查询traceroute
热门文章
- android使用桢布局,Android性能优化UI篇
- HashMap的存储过程
- 005_JDK的Date类对Comparable接口的实现
- 计算机网络中什么叫总衰耗_1、什么是计算机网络?
- ps2020的antlib文件在哪_ant design pro 新增页面
- knight tour java_The Knight’s tour problem
- python语言的单行注释以井号开头_推荐|零基础学习Python基础知识
- css3属性box-sizing:border-box 用法解析 击败边框:带border的百分比布局
- 明明输出电压小于5V,单片机ADC测量到数值却一直为5V,解决方法
- jupyter中中文显示不正常_jupyter 中文乱码设置编码格式 避免控制台输出的解决...