I'm working in an Android app in which one shared library (that I build in Android Studio, and let's call it libA.so
) dynamically loads another shared library provider by a vendor (let's call it libB.so
). I know that I shouldn't use multiple C++ runtime libraries in my app (https://developer.android.com/ndk/guides/cpp-support.html#important_considerations) so we've decided to use c++_shared in both libs.
libB.so
(the one provided by a vendor) is compiled and linked when AOSP is built (the vendor insists in building the library this way, can't do much about it). The makefile for libB.so
is setting the STL flag to c++_shared with this:
LOCAL_NDK_STL_VARIANT := c++_shared
When I look at the NEEDED tag in the libB.so
library, I can see the dependency to libc++.so
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libc++.so] <----
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x000000000000000e (SONAME) Library soname: [libB.so]
When I run readelf -d libc++.so
to check the content of AOSP's libc++.so I get this
Dynamic section at offset 0xe4b40 contains 29 entries:
Tag Type Name/Value
0x0000000000000003 (PLTGOT) 0xe6310
0x0000000000000002 (PLTRELSZ) 22128 (bytes)
0x0000000000000017 (JMPREL) 0x39ff8
0x0000000000000014 (PLTREL) RELA
0x0000000000000007 (RELA) 0x2ce58
0x0000000000000008 (RELASZ) 53664 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffff9 (RELACOUNT) 380
0x0000000000000006 (SYMTAB) 0x238
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000005 (STRTAB) 0xdeb8
0x000000000000000a (STRSZ) 102917 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x270c0
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x000000000000000e (SONAME) Library soname: [libc++.so]
0x000000000000001a (FINI_ARRAY) 0xe08e0
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x0000000000000019 (INIT_ARRAY) 0xe5b38
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW
0x000000006ffffff0 (VERSYM) 0x2bb7c
0x000000006ffffffc (VERDEF) 0x2cddc
0x000000006ffffffd (VERDEFNUM) 1
0x000000006ffffffe (VERNEED) 0x2cdf8
0x000000006fffffff (VERNEEDNUM) 2
0x0000000000000000 (NULL) 0x0
I know the NDK provides also libc++.so
, but when I run the same command in the library distributed in the Android NDK, I get an error
readelf: Error: libc++.so: Failed to read file header
And if I'm not mistaken, that's because in the NDK, libc++.so
is in fact a linker script.
libA.so
(the one that I build with my app and loads libB.so
) ends up with a dependency to libc++_shared.so
Dynamic section at offset 0x4bca50 contains 28 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
0x0000000000000001 (NEEDED) Shared library: [libc++_shared.so] <---
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x000000000000000e (SONAME) Library soname: [libA.so]
I don't think I can (or should) bundle both libc++.so
and libc++_shared.so
in my app.
So, is AOSP's libc++.so
the same as NDK's libc++_shared.so
?
Someone knows why AOSP adds a dynamic dependency to libc++.so
instead of libc++_shared.so
even when LOCAL_NDK_STL_VARIANT := c++_shared
is used? Should I ask my provider to link against libc++_shared.so
instead? Maybe someone has a better recommendation to fix this dependency mismatch.
No, they are not equivalent. libc++.so in AOSP is built against the latest (technically it's the future API level) API level and without libandroid_support. It is built with a different set of flags, and is potentially a different ABI. It is built with a set of architecture flags that allow the compiler to use instructions that are not available on all Android devices. It is also a different revision from the one in the NDK, but that is also true of different NDK versions and is less significant in deciding whether or not they are compatible.
If the vendor is providing libB.so to you for inclusion in your app, they are building it incorrectly. As you've noticed, it is linking against the platform libc++.so, which is not accessible to applications. There is no fix from your side; the vendor needs to provide a proper NDK library. If they want to continue using the AOSP build system they need to set LOCAL_SDK_VERSION
so LOCAL_NDK_STL_VARIANT
isn't ignored.
If the vendor is providing a device with this library installed to the system image (i.e. /system/lib64/libB.so, not included in your application), then the guidance in @alex-cohn's answer applies. This is how official NDK libraries like libandroid.so are built. They link against the system's libc++.so, but only expose a C ABI to applications. As long as the vendor follows these guidelines, this is fine.
As Alex mentions, the document that you quote is slightly more restrictive than the reality of the situation. It is possible to use multiple STLs in the same program (otherwise NDK applications wouldn't work at all, since the platform and the application use different STLs), but it does require very careful management of the surface area of your ABIs and a good deal of caution to avoid ODR violations.
First of all, it's OK to mix different C++ runtimes in one app, as long as they do not interact. What this means, the C++ objects (including exceptions) should not cross the boundaries of their shared library. Therefore, if your libB.so provides an extern "C" public API, you can safely use these functions from components compiled for any C++ runtime, even stlport_static.
If the vendor lib does not export a pure C API, it's a violation of the architecture provisions (see https://source.android.com/devices/architecture/images/vndk_design_android_o.pdf, page 27). In this situation, you may need to build your dependent libA.so as part of AOSP, too. You can do that if you choose to couple your library more tightly to libB.so, e.g. extend some of its classes, throw exceptions across libA/libB boundary, etc.
Note that since Android N, the system linker protects platform libraries (those in /system/lib and /vendor/lib) from being dlopened from user code. You should consider adding libB, or libA, or both, to the whitelist.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With