From 33ee0cd44a15b4549942f330ddbf6fde7f98b9a9 Mon Sep 17 00:00:00 2001 From: Charley <471705439@qq.com> Date: Tue, 20 Aug 2019 10:03:14 +0800 Subject: [PATCH] Summary: Fixed: https://github.com/facebook/react-native/issues/25490 Make extraction strategy of .so files smarter. In past versions, SoLoader follows standard rule to extract .so files from zip. However, some phones customize application installer which doesn't follow standard rule. And then, may cause crash because .so files of different ABI is loaded in the application. But now, SoLoader will parse .so files in lib dir to determine which ABI should be extracted. It can ensure that only one type of ABI will be loaded in the application so that SoLoader can be adapted for more models. --- java/com/facebook/soloader/ExoSoSource.java | 2 +- .../soloader/ExtractFromZipSoSource.java | 6 +- java/com/facebook/soloader/MinElf.java | 56 +++++++++++++++++ java/com/facebook/soloader/SysUtil.java | 62 +++++++++++++++++++ 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/java/com/facebook/soloader/ExoSoSource.java b/java/com/facebook/soloader/ExoSoSource.java index 59f4863..c1a7f51 100644 --- a/java/com/facebook/soloader/ExoSoSource.java +++ b/java/com/facebook/soloader/ExoSoSource.java @@ -59,7 +59,7 @@ private final class ExoUnpacker extends Unpacker { Set librariesAbiSet = new LinkedHashSet<>(); - for (String abi : SysUtil.getSupportedAbis()) { + for (String abi : SysUtil.getSortedSupportedAbisByNativeLibrary(mContext)) { File abiDir = new File(exoDir, abi); if (!abiDir.isDirectory()) { continue; diff --git a/java/com/facebook/soloader/ExtractFromZipSoSource.java b/java/com/facebook/soloader/ExtractFromZipSoSource.java index df5fd96..527fe66 100644 --- a/java/com/facebook/soloader/ExtractFromZipSoSource.java +++ b/java/com/facebook/soloader/ExtractFromZipSoSource.java @@ -20,10 +20,14 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -72,7 +76,7 @@ final ZipDso[] ensureDsos() { Set librariesAbiSet = new LinkedHashSet<>(); HashMap providedLibraries = new HashMap<>(); Pattern zipSearchPattern = Pattern.compile(mZipSearchPattern); - String[] supportedAbis = SysUtil.getSupportedAbis(); + String[] supportedAbis = SysUtil.getSortedSupportedAbisByNativeLibrary(mSoSource.mContext); Enumeration entries = mZipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); diff --git a/java/com/facebook/soloader/MinElf.java b/java/com/facebook/soloader/MinElf.java index 3261638..0f1de9a 100644 --- a/java/com/facebook/soloader/MinElf.java +++ b/java/com/facebook/soloader/MinElf.java @@ -31,6 +31,26 @@ */ public final class MinElf { + public static enum ISA { + NOT_SO("not_so"), + X86("x86"), + ARM("armeabi-v7a"), + X86_64("x86_64"), + AARCH64("arm64-v8a"), + OTHERS("others"); + + private final String value; + + ISA(final String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + }; + public static final int ELF_MAGIC = 0x464c457f; public static final int DT_NULL = 0; @@ -42,6 +62,42 @@ public final class MinElf { public static final int PN_XNUM = 0xFFFF; + /** + * elf format see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format + * @param file + * @return isa of file + * @throws IOException + */ + public static ISA getISAFromFile(File file) + throws IOException { + FileInputStream fis = new FileInputStream(file); + FileChannel fc = fis.getChannel(); + ByteBuffer bbBit = ByteBuffer.allocate(8); + ByteBuffer bbISA = ByteBuffer.allocate(16); + bbBit.order(ByteOrder.LITTLE_ENDIAN); + bbISA.order(ByteOrder.LITTLE_ENDIAN); + if (getu32(fc, bbBit, Elf32_Ehdr.e_ident) != ELF_MAGIC) { + return ISA.NOT_SO; + } + boolean is32 = getu8(fc, bbBit, Elf32_Ehdr.e_ident + 0x4) == 1; + if (is32) { + int isa = getu16(fc, bbISA, Elf32_Ehdr.e_machine); + if (isa == 0x03) { + return ISA.X86; + } else if (isa == 0x28) { + return ISA.ARM; + } + } else { + int isa = getu16(fc, bbISA, Elf64_Ehdr.e_machine); + if (isa == 0x3E) { + return ISA.X86_64; + } else if (isa == 0xB7) { + return ISA.AARCH64; + } + } + return ISA.OTHERS; + } + public static String[] extract_DT_NEEDED(File elfFile) throws IOException { FileInputStream is = new FileInputStream(elfFile); try { diff --git a/java/com/facebook/soloader/SysUtil.java b/java/com/facebook/soloader/SysUtil.java index d363c51..39d2e30 100644 --- a/java/com/facebook/soloader/SysUtil.java +++ b/java/com/facebook/soloader/SysUtil.java @@ -25,14 +25,21 @@ import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; +import android.util.Log; + import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; public final class SysUtil { + private static final String TAG = "SysUtil"; + private static final byte APK_SIGNATURE_VERSION = 1; /** @@ -73,6 +80,61 @@ public static String[] getSupportedAbis() { } } + /** + * Return an list of ABIs we supported on this device ordered according to preference. + * @param context + * @return Ordered array of supported ABIs, the ABI nativeLibrary used is top priority. + */ + public static String[] getSortedSupportedAbisByNativeLibrary(Context context) { + // Only use 32-bit abi when API < 21 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return new String[]{Build.CPU_ABI, Build.CPU_ABI2}; + } + String[] supportedAbis = LollipopSysdeps.getSupportedAbis(); + Log.i(TAG, "SupportedAbis is " + Arrays.toString(supportedAbis)); + // If context or nativeLibraryDir is null, return abis unsorted. + if (context == null || context.getApplicationInfo().nativeLibraryDir == null) { + return supportedAbis; + } + File appLibsDir = new File(context.getApplicationInfo().nativeLibraryDir); + if (!appLibsDir.exists() + || !appLibsDir.canRead() + || appLibsDir.listFiles() == null) { + return supportedAbis; + } + String topPriorityABI = null; + for (File appLib : appLibsDir.listFiles()) { + try { + MinElf.ISA isaOfFile = MinElf.getISAFromFile(appLib); + if (isaOfFile != MinElf.ISA.NOT_SO && isaOfFile != MinElf.ISA.OTHERS) { + topPriorityABI = isaOfFile.toString(); + Log.i(TAG, "topPriorityAbi is " + topPriorityABI); + break; + } + } catch (IOException e) { + e.printStackTrace(); + } + } + if (topPriorityABI != null) { + final String finalTopPriorityABI = topPriorityABI; + Arrays.sort(supportedAbis, new Comparator() { + @Override + public int compare(String o1, String o2) { + if (o1.equals(finalTopPriorityABI)) { + return -1; + } else if (o2.equals(finalTopPriorityABI)) { + return 1; + } else { + return 0; + } + } + }); + Log.i(TAG, "SortedSupportedAbis by NativeLibrary is " + Arrays.toString(supportedAbis)); + } + // If we can't judge which abi is used by nativeLibrary, then use result of default method. + return supportedAbis; + } + /** * Pre-allocate disk space for a file if we can do that * on this version of the OS.