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.