4

Since Android 7.0 it's not possible anymore to link against a non-ndk shared library (see NDK Apps Linking to Platform Libraries).

One possible workaround consists in including the library in the apk (See Update your app).

The library that you are trying to link against may depend on other non-ndk libraries. In that case you should include those libraries too.

In my case I've been developing an application which makes use of OpenCL. On ARM devices the library with the correct symbols is libGLES_mali.so. The application works fine on devices with Android < 7.0 but it crashes on devices with Android >= 7.0. The error that I can read in logcat is:

java.lang.UnsatisfiedLinkError: dlopen failed: library "android.hardware.graphics.common@1.0.so" not found

Using the command

readelf -d libGLES_mali.so | grep NEEDED

I can read the name of the libraries libGLES_mali.so depend on and predictably android.hardware.graphics.common@1.0.so is among them:

0x0000000000000001 (NEEDED)             Shared library: [android.hardware.graphics.common@1.0.so]
 0x0000000000000001 (NEEDED)             Shared library: [liblog.so]
 0x0000000000000001 (NEEDED)             Shared library: [libnativewindow.so]
 0x0000000000000001 (NEEDED)             Shared library: [libz.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc++.so]
 0x0000000000000001 (NEEDED)             Shared library: [libutils.so]
 0x0000000000000001 (NEEDED)             Shared library: [libcutils.so]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]

I've tried including the aforementioned library in the apk but I get the same error. The weird thing is that the library is part of the VNDK-SP (see SP-HAL) and as such it is my understanding that private libraries may depend on it freely.

Any suggestion?

EDIT 31/01/2019: Tested devices running on Android >= 7.0 were all Huawei. Could it possibly be a vendor related problem?

Rodolfo Rocco
  • 71
  • 1
  • 8
  • 1
    "Since Android 7.0 it's not possible anymore to link against a non-ndk shared library". That's not exactly true. The device vendor can still leave it open (as Samsung/LG/Sony/Moto do on most of their devices - but Google Pixel doesn't). There are examples of OCL app on the store that use shared OCL runtime and work fine on latest Android. One example (disclaimer I worked on this): https://play.google.com/store/apps/details?id=com.advancedkernels.betect&hl=en_US – Hashman Jan 31 '19 at 03:06
  • 1
    What specific Android >=7.0 device are you testing with? If its anything Huawei, I've had issues with their's too. – Hashman Jan 31 '19 at 03:22
  • Indeed it is Huawei Mate 20 Lite. Did you ever manage to solve those issues? – Rodolfo Rocco Jan 31 '19 at 08:49
  • 1
    No. I don’t know what’s the deal with their stack. I read online they might use ocl for some camera features. So my best guess is they’ve reserved the ocl runtime or something. I filed a ticket with them, but never heard back. Its a shame. I would test with a Samsung device for now, as they are very stable on OCL. – Hashman Jan 31 '19 at 17:25
  • 1
    Most probably, `android.hardware.graphics.common@1.0.so` is loaded your process address space before your app tries to load `libGLES_mali.so`, and therefore the copy you packaged with your app could not load. To verify my suspicion, try to load `android.hardware.graphics.common@1.0.so` explicitly, as early as you can. You can try even to rename the file, and see what happens when you try to load it. – Alex Cohn Feb 01 '19 at 07:40
  • 1
    @AlexCohn you were right! I did as you suggested and now the application works! more details in the answer I give below. – Rodolfo Rocco Feb 01 '19 at 09:15

2 Answers2

3

Alex Cohn comment was right. In order to solve the problem I did the following:

1) Renamed android.hardware.graphics.common@1.0.so in libfoo.so

2) Added libfoo.so in CMakeLists.txt like this:

add_library( foo
         SHARED
         IMPORTED )
set_target_properties( foo
         PROPERTIES IMPORTED_LOCATION
         ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libfoo.so )

3) Target-linked MyLibrary, which contains the OpenCL calls, against libfoo.so (and, of course, libGLES_mali.so)

target_link_libraries (MyLibrary GLES_mali foo)

4) Loaded libfoo.so as soon as possible. To do so I created a static method in my MainActivity which I call as soon as the application enters onCreate().

private static void loadLibrary() {
    System.loadLibrary("foo");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    loadLibrary();
    ...
}  

At this point the application crashes complaining that it couldn't find some libraries. Using the readelf command:

./readelf -d /Users/rodolforocco/AndroidProjects/OvermindClient/app/libs/arm64-v8a/android-27/libfoo.so | grep NEEDED

I was able to see that these were indeed the libraries that libfoo.so depended on. These libraries also depended on other libraries that could not be located. I copied them all from the folder /system/lib64/ in my device to the folder ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/, where libfoo.so was.

5) Finally, as before, I loaded MyLibrary when I needed it.

The application doesn't crash anymore and works as intended. Thanks a lot!

Rodolfo Rocco
  • 71
  • 1
  • 8
  • 1
    OK, I had to give it a try on a Huawei Honor 8 with Android 7.0. And yeah it works. But this isn't really deployable, is it? I mean, a completely different set of runtimes where needed than the ones you listed. Huawei really needs to fix this (like everyone else). – Hashman Feb 03 '19 at 01:40
  • I believe you can skip packaging all dependencies in the APK. Can't you copy the libs to your app files folder when the app is installed? – Alex Cohn Feb 03 '19 at 10:48
  • @AlexCohn I tried following your suggestion but if I copy the dependencies of android.hardware.graphics.common@1.0.so from the /system/lib64/ folder to getContext().getApplicationInfo().nativeLibraryDir the application crashes like before, complaining that it couldn't find android.hardware.graphics.common@1.0.so. Moreover, the application crashes even if I bundle the dependencies in the apk but I copy them anyway. It seems like I really can't do anything before loading android.hardware.graphics.common@1.0.so. – Rodolfo Rocco Feb 04 '19 at 09:12
  • **nativeLibraryDir** is read only, but I believe you can copy the libraries to **dataDir**. – Alex Cohn Feb 04 '19 at 09:34
  • No luck, same problem as before even when using dataDir. As I noticed the app crashed even if I do include the libraries in the apk but I try copying them nonetheless. The crash happens after I copy the dependencies and when I try loading the library. – Rodolfo Rocco Feb 04 '19 at 10:05
1

I have had a similar problem with libOpenCL.so library needing libcutils.so and I solved it in a slightly different way:

Instead of linking and including libOpenCL.so in the .apk, I chose to only link my c++ code to it and then use libOpenCL.so the library locally installed on the phone (it's the same file - No need to have it in 2 different locations).

This was much easier for me because by doing this I didn't have to rename and load libcutils as soon as my app started which (I had SW design constraints preventing me from doing this).

Here are the details:

  1. I created a lib dir in a location that Android Studio does not include when packaging the .apk file (ex: src/cpp/lib)
  2. I downloaded the 32-bit and 64-bit libOpenCL.so from my phone using "adb pull ..." and saved them to src/cpp/lib/armeabi-v7a and src/cpp/lib/arm64-v8a respectively (I guess you did a similar thing - see Does Android support OpenCL? for more details)
  3. I added the following 2 lines to my CMakeKists.txt:
add_library(OpenCL SHARED IMPORTED)
set_target_properties(OpenCL PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/cpp/lib/${ANDROID_ABI}/libOpenCL.so)

It's a bit sad but it seems to me that it's almost impossible to make an OpenCL app that works on all Android phones (the libs are called differently and are at different locations on different phones)... Please, let me know if you know of a way to do this :)

kurtzmarc
  • 3,110
  • 1
  • 24
  • 40
fakr00n
  • 103
  • 6
  • This happened to me when I mistakenly put the libOpenCL.so shared library in the src/main/jniLibs/.../ folder. Doing that adds them to the APK which we don't want. Instead we want to put them in a neutral folder in the root of the project. – kurtzmarc May 27 '20 at 19:09