Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] __emutls_get_address change in r23b causes compatibility issues for libc++_shared.so #1639

Closed
tanersener opened this issue Dec 31, 2021 · 8 comments
Labels

Comments

@tanersener
Copy link

tanersener commented Dec 31, 2021

The fix in #1551 causes compatibility issues for libraries which are built using r23b. I couldn't find a solution for those problems. So, I'm posting my findings here. I'm still not sure whether it is an application error, an NDK issue or an Android platform problem.

When I build ffmpeg-kit with r23b everything works fine. I don't have any issues about building or running the .so files in any architecture.

However, some packages of ffmpeg-kit (there are 8 different .aar packages) are bundled with libc++_shared.so. If I depend on a second library which also comes with their own version of libc++_shared.so. And if that second libc++_shared.so is not coming from r23b then we have a problem.

Normally, I'm using the following configuration in build.gradle to pick only one of the .so files.

    packagingOptions {
        pickFirst '**/armeabi-v7a/libc++_shared.so'
        pickFirst '**/x86/libc++_shared.so'
        pickFirst '**/arm64-v8a/libc++_shared.so'
        pickFirst '**/x86_64/libc++_shared.so'
    }

This conf drops my libc++_shared.so file, puts the one not coming from r23b and my application fails to start with an undefined symbol error.

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__emutls_get_address" referenced by "/data/app/~~2kB8d3EtNmrQeTorQuxsNw==/com.arthenica.ffmpegkit.reactnative.test-H8VCzoSNcb5BSErN7bf4mw==/base.apk!/lib/x86_64/libffmpegkit.so

In my case, the second library is React Native. React Native is shipped with an old version of libc++_shared.so.

Btw, this is how __emutls_get_address is defined in my libffmpegkit.so library. __emutls_get_address is undefined there.

readelf -s libffmpegkit.so | grep __emutls_get_address

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __emutls_get_address

The flags that I use to build ffmpeg-kit didn't change recently. The only thing in my toolchain that changed is ndk version. So, the question is:

  1. Is there a flag or option I can use to build ffmpeg-kit to prevent this from happening?
  2. If it did happen, is there a workaround I can apply, e.g. tell gradle to pick my libc++_shared.so not the one coming from react-native
  3. Is this an app error or an issue that must be addressed in NDK?
@DanAlbert
Copy link
Member

Hmm. My gut reaction is that this is a packaging bug, but of course it's one that's very easy to encounter. I don't know enough about AGP's packaging DSL to tell you how to spell out the fix though. I would guess that there's some option other than pickFirst that will let you resolve a specific artifact instead.

I don't suppose React Native is available as an AAR? An AAR wouldn't have this problem because prefab packages don't include the stdlib for exactly this kind of reason.

Is there a flag or option I can use to build ffmpeg-kit to prevent this from happening?

I don't think so, and you'd be re-introducing a different bug by doing so (an admittedly rare one that you may not care about).

Is this an app error or an issue that must be addressed in NDK?

I'm not sure what we could do in the NDK here. Had this been discovered before r23b shipped, we probably would have avoided including the fix in that release. While it's not unlikely for things to break when you build against newer artifacts than are actually shipped, it's really not something we'd like to have happen within a major release :( A revert of the fix in r23c wouldn't help either, it'd just make the problem happen in the other direction (artifacts built with r23b wouldn't work with the r23c libc++).

CC @rprichard @smeenai in case they have better ideas.

Leaving open for now but I suspect my reasoning is right and anything we do to "fix" this actually just adds a second incompatibility.

@tanersener
Copy link
Author

I don't suppose React Native is available as an AAR? An AAR wouldn't have this problem because prefab packages don't include the stdlib for exactly this kind of reason

Actually, it comes with an .aar. But it doesn't have a prefab package inside. Btw, how can a prefab package solve this issue? I thought, a prefab package is simply C/C++ headers for the native libraries under the jni folder and the metadata that shows which headers belong to which .so. I guess it does more than that. Well, I can ask ReactNative to add a prefab package if that will help.

@DanAlbert
Copy link
Member

Btw, how can a prefab package solve this issue?

Prefab packages don't bundle the stdlib; they get it from your NDK. If you need a specific version of the NDK for your app, you pick it in AGP and it gets used no matter what your dependencies used (aside from some ABI compatibility checking).

@DanAlbert
Copy link
Member

DanAlbert commented Jan 5, 2022

Coincidentally found the answer in a different bug: https://issuetracker.google.com/issues/141758241#comment23. I think using pickFrom with your project should resolve the packaging?

@tanersener
Copy link
Author

Prefab packages don't bundle the stdlib; they get it from your NDK. If you need a specific version of the NDK for your app, you pick it in AGP and it gets used no matter what your dependencies used (aside from some ABI compatibility checking).

Does this mean that I will not have the cannot locate symbol "__emutls_get_address" error if I follow the following steps?

  1. Build a prefab package with r23b for ffmpeg-kit and put in ffmpeg-kit.aar
  2. Create an app where I have ffmpeg-kit.aar (with prefab) and react-native.aar (without prefab) as dependencies
  3. Then set ndkVersion to r23b in AGP

Coincidentally found the answer in a different bug: https://issuetracker.google.com/issues/141758241#comment23. I think using pickFrom with your project should resolve the packaging?

Well, I don't remember I've seen the pickFrom option before. Had a look at AGP 4.1, AGP 4.2 and AGP 7.0 docs but I don't see it there too. I guess it was a suggestion to address the issue. I'll be happy to use it they decide to implement it.

@DanAlbert
Copy link
Member

Build a prefab package with r23b for ffmpeg-kit and put in ffmpeg-kit.aar

It shouldn't matter what NDK is used to build your dependencies as long as its output is ABI compatible (which means everything since r11 is valid, not that I'd recommend using anything that old 😄). If all your dependencies are prefab AARs they don't bundle libc++ and your libc++ will be used regardless of what they built with.

and react-native.aar (without prefab) as dependencies

If that's the package that's giving you the out of date copy of libc++ (seems so), if this is still a non-prefab import, this won't fix anything. I should probably mention that I have no clue how react native works so maybe this suggestion is completely off base.

From what I can tell the problem here is that react native is forcing an older (incompatibly older) libc++ into your APK. libffmpegkit is built correctly, it just requires a newer libc++. I don't think any changes to libffmpegkit will help you. I think the fixes available to you (aside from react native rebuilding with r23c) are:

  1. libc++ stops exporting this symbol
  2. AGP uses libc++ from your NDK and not from your dependencies

1 isn't really a solution. It might make the problem asymptomatic in some cases, but there's nothing unique about libc++ here that's causing the problem. If you had any other r23c+ built dependency that used thread locals, the problem would still be present. Hiding it in everything would solve that, but that was the cause of #1551. If TLS variables in a shared library need to be usable outside that library, __emutls_get_address must not be hidden in either library because otherwise there are multiple definitions of the function, and therefore multiple addresses for the thread local variable.

2 seems ideal, but I came to the same conclusion you did: there's currently no way to describe this requirement to AGP. I'm going to ask around and see if I can get a better answer on that. The advice we (both AGP and NDK teams) have been giving is that file conflicts like these cannot be automatically resolved and the developer must make the decision of which file gets packaged, so I'm pretty surprised to find that it's not possible to actually do that.

I'm pretty frustrated by this bug too, fwiw. There don't seem to many good options for a fix local to apps or the NDK. We'll keep thinking about it.

@DanAlbert
Copy link
Member

https://issuetracker.google.com/214395989 is the API described by 2. We obviously didn't think up any fixes that we could ship from our side :\

@tanersener
Copy link
Author

Looks good. Thanks for your help on this Dan! I really appreciate it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants