Saturday, June 8, 2019

Verifying 64-bit libraries are being loaded

If you're bundling native libraries inside your Android application, then you've probably already know that starting August 1, 2019, your apps published on Google Play will need to support 64-bit architectures. In this post I'll show how we can explicitly choose the 64-bit ABIs to run, and verify that the 64-bit library was indeed loaded at runtime.

The support 64-bit architectures guide provides a command for selecting which ABI libraries to use during the APK installation process:
# A successful install:
$ adb install --abi armeabi-v7a APK_FILE.apk

This command can be handy when your APK includes several ABI libraries, your CPU able to run multiple ABIs and you want to explicitly specify which ABI to use.

For example, my Pixel XL device supports the following ABIs (both 32 and 64 bits):
# 32-bit ABIs, mapped to Build#SUPPORTED_32_BIT_ABIS
$ adb shell getprop ro.product.cpu.abilist32
# 64-bit ABIs, mapped to Build#SUPPORTED_64_BIT_ABIS
$ adb shell getprop ro.product.cpu.abilist64

Once I've built my APK with arm64-v8a ABI support (along with armeabi-v7a libraries), I can specify which ABI I'd like to use using:
$ adb install --abi arm64-v8a APK_FILE.apk

Those who would like to verify that the 64-bit library was indeed loaded at runtime - can check the dynamic linker's debug logs. Support for printing those logs was added in this commit, and available since Oreo (version 8.0).  Note that this only works for APKs built in debug mode. Device rooting is not required.

To enable the ld debug logs for a particular app, use:
$ adb shell setprop dlerror,dlopen
  • com.example.myapp is your package name.
  • dlerror,dlopen are the filtering options. Any combination of dlerror (enables logging of all dlerrors) / dlopen (traces dlopen and dlclose calls) / dlsym (symbols resolution) is being supported.

Example for such use (package name = info.osom.nativesandbox):
$ adb shell setprop dlerror,dlopen 

Verifying the property was set:
$ adb shell getprop

Now let's run adb logcat and start the app:
D/linker: dlopen(name="/data/app/info.osom.nativesandbox-_1cbjYwj8FtK4-Mjp4m-Sw==/lib/arm64/", flags=0x2, extinfo=[flags=0x200, reserved_addr=0x0, reserved_size=0x7b6ed1a400, relro_fd=1857095400, library_fd=123, library_fd_offset=0x7ff8f34868, library_namespace=classloader-namespace@0x7bf45e0210], caller="/system/lib64/", caller_ns=(default)@0x7bf4775438) ... 
D/linker: ... dlopen calling constructors: realpath="/data/app/info.osom.nativesandbox-_1cbjYwj8FtK4-Mjp4m-Sw==/lib/arm64/", soname="", handle=0x99a17691a1a513e1 
D/linker: ... dlopen successful: realpath="/data/app/info.osom.nativesandbox-_1cbjYwj8FtK4-Mjp4m-Sw==/lib/arm64/", soname="", handle=0x99a17691a1a513e1 
D/linker: dlerror set to "undefined symbol: JNI_OnLoad"

From the above debug logs we can see that dynamic linker loaded the native library from the arm64 folder.

No comments:

Post a Comment