关于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
- 第一个问题是我期望 link 对抗
libc++.so
而不是 libc++_shared.so
。它们之间有什么区别?我读 this article。但仍然没有解释 libc++
和 libc++_shared
之间的区别
- 第二个问题更严重,我好像用的是libstdc++!
- 第三点,我以为 clang 的 c++ 实现在命名空间
std::__1
下,但我找不到类似的东西。
我知道使用 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++.so
和libc++_shared.so
之间的区别
通过将 -DANDROID_STL=c++_shared
传递给 CMake 调用,您明确要求共享运行时而不是默认运行时。
如the documentation中所述,规则很简单:
- 如果 所有 您的本机代码都在单个库中,请使用静态 libc++(默认),这样可以删除未使用的代码,并且您拥有尽可能小的应用程序包。
- 只要您包含一个额外的库——要么是因为您包含了来自其他地方的预编译库,要么是因为您包含了一个恰好包含本机代码的 Android AAR 文件——您必须切换到共享运行时。
规则的基本原理很简单:C++ 运行时具有某些全局数据结构,必须 初始化一次并且 必须 只存在曾经在记忆中。如果您不小心加载了两个都静态地 link C++ 运行时的库,那么您有(例如)两个冲突的内存分配器。
当您 free
或 delete
内存由另一个库分配时,或者如果您跨库边界传递 C++ STL 对象(如 std::string
),这将导致崩溃。
为了完整起见,在旧版 NDK 中,libstdc++(GNU C++ 运行时)也包含在 NDK 中,但从 NDK r18 开始,情况已不再如此。
我在尝试使用 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
- 第一个问题是我期望 link 对抗
libc++.so
而不是libc++_shared.so
。它们之间有什么区别?我读 this article。但仍然没有解释libc++
和libc++_shared
之间的区别
- 第二个问题更严重,我好像用的是libstdc++!
- 第三点,我以为 clang 的 c++ 实现在命名空间
std::__1
下,但我找不到类似的东西。
我知道使用 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++.so
和libc++_shared.so
通过将 -DANDROID_STL=c++_shared
传递给 CMake 调用,您明确要求共享运行时而不是默认运行时。
如the documentation中所述,规则很简单:
- 如果 所有 您的本机代码都在单个库中,请使用静态 libc++(默认),这样可以删除未使用的代码,并且您拥有尽可能小的应用程序包。
- 只要您包含一个额外的库——要么是因为您包含了来自其他地方的预编译库,要么是因为您包含了一个恰好包含本机代码的 Android AAR 文件——您必须切换到共享运行时。
规则的基本原理很简单:C++ 运行时具有某些全局数据结构,必须 初始化一次并且 必须 只存在曾经在记忆中。如果您不小心加载了两个都静态地 link C++ 运行时的库,那么您有(例如)两个冲突的内存分配器。
当您 free
或 delete
内存由另一个库分配时,或者如果您跨库边界传递 C++ STL 对象(如 std::string
),这将导致崩溃。
为了完整起见,在旧版 NDK 中,libstdc++(GNU C++ 运行时)也包含在 NDK 中,但从 NDK r18 开始,情况已不再如此。