Skip to content

Commit

Permalink
[android] Make Expo Android use a custom SoLoader that implements 0.1…
Browse files Browse the repository at this point in the history
….0 and 0.5.1

RN 0.57 and some of its dependencies (Fresco) updated to use SoLoader 0.5.1, which introduced breaking changes compared to 0.1.0. Namely, `SoLoader.loadLibrary` now returns a boolean instead of nothing. This causes old Java bytecode -- including older Expo ABIs -- that was looking for `void SoLoader.loadLibrary` to fail because the method is not defined.

The behavior of SoLoader is seemingly otherwise compatible, so we may be able to just add the old method back. This commit adds a custom SoLoader AAR and overrides the version from Maven so that we use it when building Expo, but unimodules can depend on Maven's SoLoader 0.5.1 in the build.gradle files and work with the latest RN (0.57) and address issues like https://github.com/expo/expo-gl/issues/2.

Test Plan: Built the Android client and loaded the published NavigationPlayground project, which uses SDK 30. Without this patch, the app crashes and adb logcat shows an error with SoLoader missing the void `loadLibrary` method. With this patch, NavigationPlayground (SDK 30) loads.

Also unpacked the APK and examined the DEX files (classes3.dex specifically) and verified that both the old and new SoLoader methods are defined:

```
    #13              : (in Lcom/facebook/soloader/SoLoader;)
      name          : 'loadLibrary'
      type          : '(Ljava/lang/String;)V'
      access        : 0x0009 (PUBLIC STATIC)
      code          -
      registers     : 1
      ins           : 1
      outs          : 1
      insns size    : 4 16-bit code units
      catches       : (none)
      positions     :
        0x0000 line=840
      locals        :
        0x0000 - 0x0004 reg=0 shortName Ljava/lang/String;
    #14              : (in Lcom/facebook/soloader/SoLoader;)
      name          : 'loadLibrary'
      type          : '(Ljava/lang/String;)Z'
      access        : 0x0009 (PUBLIC STATIC)
      code          -
      registers     : 2
      ins           : 1
      outs          : 2
      insns size    : 6 16-bit code units
      catches       : (none)
      positions     :
        0x0000 line=455
      locals        :
        0x0000 - 0x0006 reg=1 shortName Ljava/lang/String;
```

---

There is some sorcery to the AAR so these are some notes. Implementing the new and old SoLoader methods is complicated by the fact that we can't add the old method to the SoLoader.java source and recompile SoLoader. Java does not support return-type dispatch, so `void loadLibrary(String)` and `boolean loadLibrary(String)` fail to compile when together.

However, JVM bytecode does allow for two methods with the same name and parameter types but different return values to co-exist. (In fact, some bytecode obfuscators use this behavior as they make take two differently-named Java methods with different return types and produce JVM bytecode with the same method name and different return types.) By editing the bytecode directly we can support the old and new SoLoader interfaces simultaneously.

To do this, I found the SoLoader AAR from the Gradle cache, extracted it, then extracted classes.jar and found SoLoader.class. In a JVM bytecode editor I added a new method:

```
  public static void loadLibrary(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokestatic  #635                // Method loadLibrary:(Ljava/lang/String;)Z
         4: pop
         5: return
      LineNumberTable:
        line 840: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0 shortName   Ljava/lang/String;
```

The implementation of this method was written by hand, which seems error-prone so I verified it three ways:

- Wrote a small test class with a similar method and inspected its bytecode and ensured it matched the new SoLoader method's bytecode.
- Ran the modified .class file through a decompiler and verified the Java code looked right.
- Wrote a small test class that called into the actual modified .class file and verified I could invoke the underlying `SoLoader.loadLibrary` method i.e. the thin patched method was working.
  • Loading branch information
ide committed Oct 15, 2018
1 parent 26c473d commit 7b5d52e
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 0 deletions.
Binary file not shown.
@@ -0,0 +1 @@
d12b8edd7cdc227c3831091432cd9bc9
@@ -0,0 +1 @@
bd0fcfede5f25e6fabbed2937d4fe5144ee333a4
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.facebook.soloader</groupId>
<artifactId>soloader</artifactId>
<version>0.5.1</version>
<packaging>aar</packaging>
<name>SoLoader</name>
<description>Reliable native code loader for Android</description>
<url>https://github.com/facebook/soloader</url>
<licenses>
<license>
<url>https://github.com/facebook/soloader/blob/master/LICENSE</url>
<name>BSD License</name>
</license>
</licenses>
<developers>
<developer>
<id>facebook</id>
<name>Facebook</name>
</developer>
</developers>
<scm>
<url>https://github.com/facebook/soloader.git</url>
<developerConnection>scm:git:git@github.com:facebook/soloader.git</developerConnection>
<connection>scm:git:https://github.com/facebook/soloader.git</connection>
</scm>
</project>
@@ -0,0 +1 @@
d0a993f02f083dca0dbd9b0ce6eff9d3
@@ -0,0 +1 @@
e2996d0ceff9de93bd47a96d22c9963ddb1fe3de
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>com.facebook.soloader</groupId>
<artifactId>soloader</artifactId>
<versioning>
<release>0.5.1</release>
<versions>
<version>0.5.1</version>
</versions>
<lastUpdated>20181013081228</lastUpdated>
</versioning>
</metadata>
@@ -0,0 +1 @@
f3d379b729c23e13b2209865dd8adf62
@@ -0,0 +1 @@
0d8d998c02cdb687fe33aaf5bb8a82defe404425

0 comments on commit 7b5d52e

Please sign in to comment.