关于Android NDK libc++ libc++_shared, libstdc++ 的困惑

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

我在尝试使用 Android NDK 23 (23.1.7779620) 构建一个简单的 C++ 库时感到非常困惑。我正在使用 CMake,这是一个非常简单的程序:

# 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;
}

这是构建的命令行:

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

我知道使用 libc++_shared 是因为这个命令:

$ 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]

运行 nm 看来我正在使用来自 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'
$

更新

在此post中解释了libc++.solibc++_shared.so

之间的区别

通过将 -DANDROID_STL=c++_shared 传递给 CMake 调用,您明确要求共享运行时而不是默认运行时。

the documentation中所述,规则很简单:

  1. 如果 所有 您的本机代码都在单个库中,请使用静态 libc++(默认),这样可以删除未使用的代码,并且您拥有尽可能小的应用程序包。
  2. 只要您包含一个额外的库——要么是因为您包含了来自其他地方的预编译库,要么是因为您包含了一个恰好包含本机代码的 Android AAR 文件——您必须切换到共享运行时。

规则的基本原理很简单:C++ 运行时具有某些全局数据结构,必须 初始化一次并且 必须 只存在曾经在记忆中。如果您不小心加载了两个都静态地 link C++ 运行时的库,那么您有(例如)两个冲突的内存分配器。 当您 freedelete 内存由另一个库分配时,或者如果您跨库边界传递 C++ STL 对象(如 std::string),这将导致崩溃。

为了完整起见,在旧版 NDK 中,libstdc++(GNU C++ 运行时)也包含在 NDK 中,但从 NDK r18 开始,情况已不再如此。