Skip to content

Commit

Permalink
Summary:
Browse files Browse the repository at this point in the history
Fixed: facebook/react-native#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.
  • Loading branch information
codyi96 committed Aug 20, 2019
1 parent a672eff commit 33ee0cd
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 2 deletions.
2 changes: 1 addition & 1 deletion java/com/facebook/soloader/ExoSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private final class ExoUnpacker extends Unpacker {

Set<String> librariesAbiSet = new LinkedHashSet<>();

for (String abi : SysUtil.getSupportedAbis()) {
for (String abi : SysUtil.getSortedSupportedAbisByNativeLibrary(mContext)) {
File abiDir = new File(exoDir, abi);
if (!abiDir.isDirectory()) {
continue;
Expand Down
6 changes: 5 additions & 1 deletion java/com/facebook/soloader/ExtractFromZipSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -72,7 +76,7 @@ final ZipDso[] ensureDsos() {
Set<String> librariesAbiSet = new LinkedHashSet<>();
HashMap<String, ZipDso> providedLibraries = new HashMap<>();
Pattern zipSearchPattern = Pattern.compile(mZipSearchPattern);
String[] supportedAbis = SysUtil.getSupportedAbis();
String[] supportedAbis = SysUtil.getSortedSupportedAbisByNativeLibrary(mSoSource.mContext);
Enumeration<? extends ZipEntry> entries = mZipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
Expand Down
56 changes: 56 additions & 0 deletions java/com/facebook/soloader/MinElf.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down
62 changes: 62 additions & 0 deletions java/com/facebook/soloader/SysUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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<String>() {
@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.
Expand Down

0 comments on commit 33ee0cd

Please sign in to comment.