Search code examples
c++android-ndklibc++

Confustion about Android NDK libc++ libc++_shared, libstdc++


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


Solution

  • 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.