Abi관련 변수들
Build.CPU_ABI
Build.SUPPORTED_ABIS
ro.product.cpu.abilist64
ro.product.cpu.abilist32
=========================
64bit, 32bit library의 경우
BaseDexClassLoader 에서 참조하는 native library는 null인데, 이는 default library path를 사용하는 것이고...
코드를 쫓아가다보면...
'java.library.path"
에서 library directory를 가져오는데(/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java) 이 값은 default로 LD_LIBRARY_PATH 환경변수값을 사용한다.(java_lang_System.cpp)
android_get_LD_LIBRARY_PATH() 함수를 찾아서 가보면, kDefaultLdPaths 에서 값을 읽어오는데, 아래와 같이 정의되어 있다.
91static const char* const kDefaultLdPaths[] = {
92#if defined(__LP64__)
93 "/vendor/lib64",
94 "/system/lib64",
95#else
96 "/vendor/lib",
97 "/system/lib",
98#endif
99 NULL
100};
101
그리고
system library의 경우
system/lib/
system/lib64/
vendor/lib/
vendor/lib64
========================
system library의 경우는 위와 같고... APK에 포함된 library의 경우는... aapt를 확인해야 하는데,
/frameworks/base/tools/aapt/Command.cpp
를 보면,
1981 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1982 if (dir != NULL) {
1983 if (dir->getFileCount() > 0) {
1984 SortedVector<String8> architectures;
1985 for (size_t i=0; i<dir->getFileCount(); i++) {
1986 architectures.add(ResTable::normalizeForOutput(
1987 dir->getFileName(i).string()));
1988 }
코드가 보이는데... 즉 "lib/<archtecture-name>"의 형태로 directory가 구성되었다는 것을 알 수 있다.
====================================
APP이 뜰때, 64bit인지 32bit인지 확인해서 zygote64를 base로 뜰건지 zygote32를 base로 뜰건지 알아야 하는데,
Process.java
에서
openZygoteSocketIfNeeded(String abi)
처럼 abi가 넘어가게 된다.
이 abi값은 proces start에 argument로 넘어오는데,
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
3192 Process.ProcessStartResult startResult = Process.start(entryPoint,
3193 app.processName, uid, uid, gids, debugFlags, mountExternal,
3194 app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
3195 app.info.dataDir, entryPointArgs);
3177 String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
3178 if (requiredAbi == null) {
3179 requiredAbi = Build.SUPPORTED_ABIS[0];
3180 }
3181
3182 String instructionSet = null;
3183 if (app.info.primaryCpuAbi != null) {
3184 instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
3185 }
에서 requiredAbi argument가 있고... default abi를 사용하긴 하나, 결국 package가 지원하는 abi를 알아야 한다.
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
5726 final int copyRet;
5727 if (isAsec) {
5728 copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
5729 } else {
5730 copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
5731 nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
5732 }
5733
5734 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
5735 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
5736 "Error unpackaging native libs for app, errorCode=" + copyRet);
5737 }
5738
5739 if (copyRet >= 0) {
5740 pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
5741 } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
5742 pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
5743 } else if (needsRenderScriptOverride) {
5744 pkg.applicationInfo.primaryCpuAbi = abiList[0];
5745 }
findSupportedAbi()
com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz,
=> 413static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray) {
결국 "/lib/<abi-name>"이란 규칙을 이용해서 apk가 지원하는 ABI를 찾는다.
그런데, PackageManagerService를 보면, package가 '.apk' file인 경우 - Monolithic apk - 와 directory이 경우 - Cluster apk - 두 가지로 나뉘어 지는 것을 알 수 있다.
두 경우 모두 cpu Abi를 결정하기 위해서 위의 방법을 사용하는데, 두 경우 모두
<package file/dir>/lib/<cpu abi>
형태의 구성을 가정하고 있다.
따라서, apk build 자체가 위의 구성으로 나오는 package build와 실제 so는 system에 두고 link만들 거는 system apk 모두를 cover하고 있다.
ex.
Monolithic
Gallery.apk
|-AndroidManifest.xml
|-...
|-lib/armeabi-v7a/xxx.so
|...
Cluster (PDK build시 "/system/app/<package name>" directory)
Gallery/ ('/system/app/Gallery')
|-Gallery.apk
|-x86_64/Gallery.odex
|-lib/x86_64/xxx.so
두 경우 모두 "<Gallery apk/dir>/lib/<cpu abi>"형태로 구성되고 이를 보고 판단한다.
아래 코드는 추가적으로 참고하자..
5686 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
5687 if (isAsec) {
5688 abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
5689 } else {
5690 abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
5691 nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
5692 useIsaSpecificSubdirs);
5693 }
5694 }
5695
5696 maybeThrowExceptionForMultiArchCopy(
5697 "Error unpackaging 64 bit native libs for multiarch app.", abi64);
5698
5699 if (abi64 >= 0) {
5700
뭐 결국은, apk의 "/lib/<abi-name>"이 platform이 지원하는 64bit abi에 포함되면, 64bit apk로 판단하는 방식이다...
즉, APK가 어떤 abi를 지원하는지는 manifest file 등등 이런 곳에 따로 기록되어 있는것이 아니고, native library의 "/lib/<abi-name>"규칙에 의존해서 찾는다.
Platform build시 jni library들은 default로 모두 'system/lib'쪽에 들어가게 되어 있는데 - APK에 lib이 포함되지 않는다. - 그럼, 해당 APK의 ABI어떻게 알 수 있는가?
(최초 booting시 해당 APK를 AOT(Ahead Of Time) compile을 위해서는 어떤 target ABI로 compile할지가 정해져야 한다.)
일단 생각해보면, 어차피 VM(DEX)에만 의존하는 APK라면 특별히 ABI에 영향을 받지 않으므로 그냥 platform default ABI로 인식하고, AOT compile을 수행하면 된다. 그렇지만, 사용하는 native library들이 있고, 이 library를 load해야하는 APK라면, ABI에 따라서 어떤 zygote(32bit 혹은 64bit)에서 fork될 것인가를 결정해야 하므로, ABI를 system이 알아야할 필요가 있다.
이 경우는, 'LOCAL_MULTILIB := 32' 같은 방식으로 second abi를 사용하도록 정의하고 빌드하면, 'system/app/<package>'쪽에 보면
*** x86_64 용 TARGET ABI로 빌드하고, 'LOCAL_MULTILIB := 32'를 사용하는 Gallery2를 예로 들면...
Gallery2
- Gallery2.apk
- lib/x86/<xxx>.so -> /system/lib/<xxx>.so (symbolic link)
- x86/Gallery2.odex
즉, 제대로 32bit 용으로 빌드되어 있다. (그렇지만, Gallery2.apk내부에는 'lib/<abi>' 형태의 directory가 없다.
그렇지만, 만약, 32bit용으로 DEX가 pre-optimization되어 있지 않다면, 어떻게 Gallery2가 32bit용 package인지 system이 알고 32bit용으로 dex를 optimization - ART의 경우 AOT compile - 시킬수 있을까?
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
6485 } else {
6486 // Cluster install
6487 info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
6488 info.nativeLibraryRootRequiresIsa = true;
6489
6490 info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
6491 getPrimaryInstructionSet(info)).getAbsolutePath();
6492
6493 if (info.secondaryCpuAbi != null) {
6494 info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
6495 VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
6496 }
6497 }
6498 }
scanPackageDirtyLI() -> 여기서 installer를 이용해서 app data directory / lib directory 등을 만든다.
nativeFindSupportedAbi
=> copyNativeBinariesForSupportedAbi : 여기서 abi를 가지고 instrunction set directory로 copy한다. 이때 nativeLibraryRootDir 로 복사하는데,
이 nativeLibraryRootDir 로 될 수 있는게
<bundled app> 은
final boolean bundledApp = isSystemApp(info) && !isUpdatedSystemApp(info);
[[ Monolithic ]]
Monolithic && bundled
"/system/lib[64]/<apkname>"
Monolithic 이고 Non-bundled
"/data/app-lib/" 이다
[[ Cluster ]]
info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
getPrimaryInstructionSet(info)).getAbsolutePath();
if (info.secondaryCpuAbi != null) {
info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
}
즉 Cluster 의 경우 NativeLibraryDir은 abi가 아니라 instruction set 이름으로 찾아들어간다.
VMRuntime.java
38 static {
39 ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm");
40 ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm");
41 ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips");
42 ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64");
43 ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86");
44 ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64");
45 ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64");
46 }
VMRuntime.getInstructionSet(info.secondaryCpuAbi)) 에서 arch이름을 찾는 규칙이 위와 같다.
즉 cpuabi 는 instruction set 이름을 찾기 위해서 사용되지 그 자체는 package manager service에서 사용되지 않는다.
재미있는 것은 build system에서는
"cpu abi" -> "arch"
오 가타은 이름을 사용하고 있고, package manager service에서는
"cpu abi" -> "instruction set"
을 사용한다.
즉, 같은 이름을 build system과 package manager service에서, 'arch', 'instruction set'이라는 이름으로 서로 다르게 사용하고 있다.
실제 emulator를 실행시킨 후 위의 - 첫번째 booting에서 apk가 install된다. - package의 data directory를 가 보면.
$ ls -l /data/data/<package-name>
lib -> /data/app-lib/<package-name>
ex.
root@generic_x86_64:/data/data/com.android.gallery # ls -l
lrwxrwxrwx install install 2014-11-10 14:13 lib -> /data/app-lib/com.android.gallery
그리고 아래의 code를 보자.
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
6477 } else {
6478 final String apkName = deriveCodePathName(codePath);
6479 info.nativeLibraryRootDir = new File(mAppLib32InstallDir, apkName)
6480 .getAbsolutePath();
6481 }
위의 코드에서 'mAppLib32InstallDir'이 바로 '/data/app-lib' directory이다.
이렇게 자신의 library를 찾아가게 된다.