目前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平台 动态库问题相关推荐

  1. 【Android NDK 开发】NDK 交叉编译 ( Ubuntu 中交叉编译动态库 | Android Studio 中配置使用第三方动态库 )

    文章目录 I . 动态库 与 静态库 II . 编译动态库 III. Android Studio 使用第三方动态库 IV . Android Studio 关键代码 V . 博客资源 I . 动态库 ...

  2. 利用android studio 生成 JNI需要的动态库so文件

    JNI:  Java Native Interface,  提供了java语言和其他语言(例如c和c++)进行相互调用的方式. 本文是用java调用c生成的so模式.其中,编译so文件过程如下: 1) ...

  3. 将java编译成so库_利用android studio 生成 JNI需要的动态库so文件

    JNI:  Java Native Interface,  提供了java语言和其他语言(例如c和c++)进行相互调用的方式. 本文是用java调用c生成的so模式.其中,编译so文件过程如下: 1) ...

  4. linux 生成dll文件,Linux和Windows平台 动态库.so和.dll文件的生成

    Linux动态库的生成 1. 纯cpp文件打包动态库 将所有cpp文件和所需要的头文件放在同一文件夹,然后执行下面命令 gcc -shared - fpic *.c -o xxx.so: g++ -s ...

  5. 【Android 逆向】Android 逆向通用工具开发 ( Windows 平台静态库程序类型 | 编译逆向工具依赖的 Windows 平台静态库程序 )

    文章目录 一.Windows 平台静态库程序类型 二.编译逆向工具依赖的 Windows 平台静态库程序 一.Windows 平台静态库程序类型 在 Android 逆向通用工具 MobileGame ...

  6. Linux和Windows平台 动态库.so和.dll文件的生成

    Linux动态库的生成 1. 纯cpp文件打包动态库 将所有cpp文件和所需要的头文件放在同一文件夹,然后执行下面命令 gcc -shared - fpic *.c -o xxx.so: g++ -s ...

  7. 【Android NDK 开发】Android Studio 使用 CMake 导入动态库 ( 构建脚本路径配置 | 指定动态库查找路径 | 链接动态库 )

    文章目录 I . CMake 引入动态库与静态库区别 II . Android Studio 中 CMake 引入动态库流程 III . 指定动态库查找路径 IV . 链接函数库 V . 完整代码示例 ...

  8. 【Android NDK 开发】Android.mk 配置静态库 ( Android Studio 配置静态库 | 配置动态库与静态库区别 | 动态库与静态库打包对比 )

    文章目录 I . Android Studio 中使用 Android.mk 配置静态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...

  9. 【Android NDK 开发】Android.mk 配置动态库 ( Android Studio 配置动态库 | 动态库加载版本限制 | 本章仅做参考推荐使用 CMake 配置动态库 )

    文章目录 I . Android Studio 中使用 Android.mk 配置动态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...

最新文章

  1. centOS外部浏览器无法访问tomcat8000端口解决办法
  2. Spring的环绕通知
  3. Python_基础知识储备
  4. 多重判定系数怎么求_关于多重共线性
  5. Java之String,StringBuffer,StringBuilder
  6. lamda获取参数集合去空_(转)Java8使用lambda表达式进行集合的遍历
  7. 最小生成树 洛谷P3366【模板】最小生成树 洛谷P2820 局域网
  8. fastclick.js插件使用简单说明
  9. paip.c#图片裁剪
  10. unity 入门学习之(二)脚本学习
  11. ubuntu16.04安装搜狗拼音输入法
  12. Rust异步之Future
  13. Android Studio使用WIFI调试应用
  14. 大学毕业生找不到工作的原因何在?
  15. 推荐三款动态壁纸软件,足够让你的桌面惊艳!
  16. .net 操作达梦数据库
  17. 关于服务端与客户端的数据交互
  18. 什么是k8s(Kubernetes)
  19. CNCC 2018 科技展盛况空前,近 100 家企业汇聚一堂
  20. 网络路径查询traceroute

热门文章

  1. android使用桢布局,Android性能优化UI篇
  2. HashMap的存储过程
  3. 005_JDK的Date类对Comparable接口的实现
  4. 计算机网络中什么叫总衰耗_1、什么是计算机网络?
  5. ps2020的antlib文件在哪_ant design pro 新增页面
  6. knight tour java_The Knight’s tour problem
  7. python语言的单行注释以井号开头_推荐|零基础学习Python基础知识
  8. css3属性box-sizing:border-box 用法解析 击败边框:带border的百分比布局
  9. 明明输出电压小于5V,单片机ADC测量到数值却一直为5V,解决方法
  10. jupyter中中文显示不正常_jupyter 中文乱码设置编码格式 避免控制台输出的解决...