finagolfin / swift-android-sdk

Android SDKs for Swift
Apache License 2.0
151 stars 15 forks source link

UnsatisfiedLinkError: empty/missing DT_HASH/DT_GNU_HASH #67

Closed KittyMac closed 2 years ago

KittyMac commented 2 years ago

I am attempting to use this toolchain to build a barebones SPM as a dynamic library and load it into an Android studio app. I have been able to set things up such that the SPM project builds + tests succeed + .so files are emitted in a Docker container. I then take the .so's out of the container (including all the dependent ones like libdispatch.so, libc++_shared.so etc) and drop them in a simple Android studio project and attempt to load at runtime using System.loadLibrary("Foundation"). I am using the prebuilts ( https://github.com/buttaface/swift-android-sdk/releases/tag/5.6 )

java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH/DT_GNU_HASH in "/data/app/~~5NBdFZVd502EJ3UzvxTnEw==/com.chimerasw.silkroadandroidtest-ARbbTYafh-bWJrnSDReyTw==/base.apk!/lib/arm64-v8a/libdispatch.so" (new hash type from the future?)
        at java.lang.Runtime.loadLibrary0(Runtime.java:1077)
        at java.lang.Runtime.loadLibrary0(Runtime.java:998)
        at java.lang.System.loadLibrary(System.java:1656)

If I try and load just a single dependent .so (say just swiftCore) it loads fine.

I tried swapping out all of the .so with the toolchain built over at https://github.com/readdle/swift-android-toolchain, and System.loadLibrary("Foundation") loads fine.

I suspect its something just with libdispatch, but am unsure where to look further. Any advice on where I can look to see what might be up?

[edit]fwiw, i reproduce with just libdispatch.so and libBlocksRuntime.so and nothing else[/edit]

[edit2]If i use your 5.4 release and swap out the other projects libdispath.so, then libdispatch and libBlockRuntime load successfully. I suspect the step mentioned in the readme where you manually do something to libdispatch may be the reason[/edit2]

finagolfin commented 2 years ago

I patch all the Swift libraries to change their ELF runpath, there's nothing specific to libdispatch. What version of Android are you testing on? My SDK only supports Android 7 or later.

KittyMac commented 2 years ago

Android 12

Here is my minimal reproducible example:

- New Android project, Empty Activity (Bumblebee 2021.1.1 Patch 2)
- Minimim SDK set to API 24 (test device is Android 12)
- Make jniLibs folder
- Make arm64-v8a, armeabi-v7a, x86_64 folders
- Copy the libdispatch.so and libBlocksRuntime.so from your Android SDKs for Swift 5.6 releases to the right folders
- Add System.loadLibrary("dispatch") to MainActivity.kt
- Run app and hit exception

ANDROID_TEST.zip

finagolfin commented 2 years ago

I don't know why it says the hash is empty or missing, I see it for all the Swift libraries with readelf -d lib*so | ag "File:|_HASH". Can you check that for your final apk? You can unzip it with unzip your.apk and then check the shared libraries.

I have not tried using this Swift SDK with a GUI apk yet, only on the command-line, but others say they got it working with some modifications, #53. I have never used Kotlin or Android Studio, would be easier if you let me know what you find.

KittyMac commented 2 years ago

Thanks, that was the hint I needed. It appears that the .so are being stripped during the build process. To have it not do that, I added this to the build.gradle:

packagingOptions {
    doNotStrip "*/arm64-v8a/*.so"
    doNotStrip "*/armeabi-v7a/*.so"
    doNotStrip "*/x86_64/*.so"
}

Once I get minimal code of mine working, I'll reduce that to just not strip the .so's it has trouble with.

KittyMac commented 2 years ago

In conclusion, I only need to flag libdispatch as do not strip. I was able to get some swift code compiling and some kotlin code calling it without any trouble after that. Thanks!

KittyMac commented 2 years ago

new information on this topic:

Stripping the .so's which are generated by swift package manager also exhibit this behaviour, so I had them all disabled in the gradle. Yesterday I tried to reduce the file size of the .so's and I discovered that if I strip them myself PRIOR to running ANY patch elf on them this issue goes away. If they are stripped AFTER the patching then the error exists.

This explains why only libdispatch in the base set of .so provided by this SDK fails in this manner, as libdispatch appears to be the only one NOT stripped already:

libdispatch.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=bdcf1902ee03845ffe8a7c417ef265bd5e528f6e, not stripped

while the others are

libFoundation.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, stripped

which effectively skips the gradle stripping process as they won't be stripped twice.

@buttaface i dunno if you can adjust your build process to strip libdispatch prior to patching it but it might fix this error for the included .so's without needing the gradle workaround.

[edit] here is how i am stripping them:

/root/android-ndk-r23c/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-objcopy --strip-all "/root/lib/x86_64/$SONAME"

[/edit]

finagolfin commented 2 years ago

I'll take a look, thanks for the info.

finagolfin commented 2 years ago

I just looked and file reports all my SDK libraries in the Swift resource directory as not stripped:

> file swift-5.7-android-aarch64-24-sdk/usr/lib/swift/android/lib*
swift-5.7-android-aarch64-24-sdk/usr/lib/swift/android/libBlocksRuntime.so:          ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=656980c1fbd00e22bacc2ddad15f6235fc1a5e9a, not stripped
swift-5.7-android-aarch64-24-sdk/usr/lib/swift/android/libdispatch.so:               ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=29c57cfdce3da3e61b943a7949ade2bd20535bf5, not stripped
swift-5.7-android-aarch64-24-sdk/usr/lib/swift/android/libFoundationNetworking.so:   ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, not stripped
swift-5.7-android-aarch64-24-sdk/usr/lib/swift/android/libFoundation.so:             ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, not stripped
...

I'm not sure why you're seeing anything different with my SDK libraries.

KittyMac commented 2 years ago

Thanks! I will dig deeper and report back.

[edit] (Just leaving notes here in case any of this is useful in the future.)

Correct ofc that the ones in the SDK are not stripped, my bad.

I have also now confirmed that gradle will strip an already stripped .so (which loaded correctly before the second strip), corrupting it in the process.

I can still confirm that if I do the following (have gradle stripping disabled in both scenarios):

  1. build spm to generate .so

  2. patch those .so

  3. strip those .so

  4. see empty/missing DT_HASH/DT_GNU_HASH error at runtime

  5. build spm to generate .so

  6. strip those .so

  7. patch those .so

  8. see them load correctly at runtime.

I suspect there is some incompatibility with stripping the .so after patchelf is run. A quick look at their repo shows a number of past issues like this, so I suspect similar but one which is specific to the android System.loadLibrary(). It might also have to do with what specific type of patchelf is happening, because the above doesn't explain why the other swift core libs load correctly when I manually stripped them (which would be after you've already performed the patchelf to them).

[/edit]