android / ndk

The Android Native Development Kit
1.97k stars 255 forks source link

[Bug]: Dynamic library contains extra weak С++ symbol when built with -fvisibility=hidden and clang from 27 NDK #2074

Closed lisa-bella97 closed 1 week ago

lisa-bella97 commented 1 week ago

Description

We find out that a dynamic library built with -fvisibility=hidden flag and using static C++ runtime may contain extra symbols from C++ standard library (with weak visibility) when built with clang from NDK r27b. There are no such problems when using NDK r26d. This new behavior may result in incorrect symbol resolution when loading, for example, two shared libraries with a static C++ runtime. Gist with minimal sample and reproducing instructions: https://gist.github.com/lisa-bella97/961535b74370165b635ac6de11bcb294.

Upstream bug

No response

Commit to cherry-pick

No response

Affected versions

r27

Canary version

No response

Host OS

Linux

Host OS version

Ubuntu 22.04.4 LTS

Affected ABIs

armeabi-v7a, arm64-v8a

DanAlbert commented 1 week ago

If you want to hide symbols in other libraries, you must use a version script. -fvisibility=hidden only hides symbols in your code (it applies at compile time, not link time).

I've actually got a doc in review that explains this more thoroughly, but all my reviewers are busy this week. For now I guess look at https://github.com/android/ndk-samples/pull/1067/files#diff-5571c00dc04cdef054a5b2f2ba33f211faba95aeacb8017e993655bcf62394df and how that's used in the CMakeLists.txt? I'll post the real doc here when it merges. It's a bit too large to copy paste the draft :)

There's also a bit of advice here: https://developer.android.com/ndk/guides/middleware-vendors#using_the_stl (and yes, you can use -Wl,--exclude-libs,libc++_static.a instead of a version script, but it's far less robust).

DanAlbert commented 1 week ago

Alright, ignore the other stuff and just read https://developer.android.com/ndk/guides/symbol-visibility :)

lisa-bella97 commented 1 week ago

@DanAlbert thank you for your answer and useful doc. The question was not so much about the fix, but rather to understand why the destructor ~basic_stringstream() symbol became visible in 27 ndk.

Our hypothesis was that the destructor became header-only. However, after looking at the sstream file in the ndk (located in toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1 directory), we found that the destructor definition was missing in both 27 and 26 ndk. I wonder why the symbol became visible in 27 ndk?

DanAlbert commented 5 days ago

but rather to understand why the destructor ~basic_stringstream() symbol became visible in 27 ndk.

/shrug It's a 3p component that we don't have much control over. You'd have to look through about a year's worth of LLVM commits to find an answer. These sorts of things tend to happen in libc++ now and then.

DanAlbert commented 5 days ago

(if anyone here knows it'd be @rprichard)