3

I am getting very confused trying to build a simple C++ library using Android NDK 23 (23.1.7779620). I am using CMake and this is a very simple program:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(mf) 
add_library(mf lib.cpp)

// lib.hpp
#pragma once
#include <string>
std::string foo(std::string);

// lib.cpp
#include "lib.hpp"
std::string foo(std::string str) {
  return std::string{"test"} + str;
}

This is the command line to build:

cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DANDROID_STL=c++_shared -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-29  -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake .. 
cmake --build . -v
  • The first problem is that I was expecting to link against libc++.so and not libc++_shared.so. What is the difference between them? I read this article. But still is not explained the difference betweend libc++ and libc++_shared
  • The second problem is even worst, it seems I am using libstdc++!
  • The 3rd point, I was thinking that the c++ implementation of clang was under the namespace std::__1 but I cannot find anything like that.

I know libc++_shared is used because of this command:

$ readelf -d libmf.so

Dynamic section at offset 0x76e0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libm.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc++_shared.so]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]
 0x000000000000000e (SONAME)             Library soname: [libmf.so]

Running nm it seem I am using symbols from libstdc++:

$ nm -gDC libmf.so | grep '__ndk1'
0000000000003af0 T foo(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >)
                 U std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >::append(char const*, unsigned long)
$ nm -gDC libmf.so | grep '__1'
$

Update

In this post is explained the difference between libc++.so and libc++_shared.so

Elvis Dukaj
  • 7,142
  • 12
  • 43
  • 85
  • 1
    (1) It isn't clear why you think `libc++_shared.so` is being used. Do you have build logs that suggest it? (2) It isn't clear why you think you are using `libstdc++`. This symbol pattern is not characteristic of `libstdc++`. `__1` is not necessarily present in all installations of `libc++` either. `1` in `__1` is `what _LIBCPP_ABI_VERSION` expands to, but it can be configured to expand to anything. – n. m. could be an AI Nov 30 '21 at 09:24
  • These are definitely not symbols from libstdc++. There is an ABI namespace named `__cxx11` in `libstdc++`, but it is (1) hardcoded and (2) does not apply to all symbols, in particular not to `allocator` or `char_traits`. – n. m. could be an AI Nov 30 '21 at 09:35
  • So what is the difference between `libc++.so` and `libc++_shared.so`, Why Android ndk doesn't allow "easily" to "just" use `libc++.so`? – Elvis Dukaj Nov 30 '21 at 09:51
  • This I don't know. – n. m. could be an AI Nov 30 '21 at 09:56
  • @n.1.8e9-where's-my-sharem. I've found the reply in this post: https://stackoverflow.com/questions/47244362/is-aosps-libc-so-the-same-as-ndks-libc-shared-so – Elvis Dukaj Nov 30 '21 at 13:10

1 Answers1

3

By passing -DANDROID_STL=c++_shared to the CMake invocation you explicitly asked for the shared runtime as opposed to the default runtime.

As explained in the documentation, the rules are simple:

  1. if all your native code is in a single library, use the static libc++ (the default) such that unused code can be removed and you have the smallest possible application package.
  2. As soon as you include an extra library – either because you include a precompiled library from somewhere else or you include an Android AAR file that happens to include native code – you must switch to the shared runtime.

The rationale for the rules is simple: the C++ runtime has certain global data structures that must be initialized once and must only exist once in memory. If you were accidentally to load two libraries that both link the C++ runtime statically, you have (for instance) two conflicting memory allocators. This will result in crashes when you free or delete memory allocated by the other library, or if you pass a C++ STL object like std::string across library boundaries.

For completeness, in older NDKs libstdc++ (the GNU C++ runtime) was also included in the NDK, but as of NDK r18 that is no longer the case.

Botje
  • 26,269
  • 3
  • 31
  • 41
  • I am still confused because on my board the library are linking against `libc++.so` but when I am using the ndk I am linking against `libc++_shared.so`. This library is not in the target and I need to deploy it as well... Cannot I just link against `libc++.so`? Is there a way? – Elvis Dukaj Nov 30 '21 at 10:18
  • 1
    The second note on the linked page is very explicit: "Note: libc++ is not a system library. If you use libc++_shared.so, it must be included in your app. If you're building your application with Gradle this is handled automatically." In other words: it doesn't matter what is on your board, the APK will package its own libc++_shared.so – Botje Nov 30 '21 at 10:21
  • 2
    _"As soon as you include an extra library – either because you include a precompiled library from somewhere else or you include an Android AAR file that happens to include native code – you must switch to the shared runtime"_. Not necessarily. See https://stackoverflow.com/a/52389560/1524450 – Michael Dec 01 '21 at 13:33
  • Good remark. I think the Android documentation and I are biased to simple advice that errs on the side of caution. – Botje Dec 01 '21 at 13:41