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

3

我正在尝试使用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++.so而不是libc++_shared.so。它们之间有什么区别?我阅读了这篇文章,但还是没有解释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'
$

更新

本文解释了libc++.solibc++_shared.so之间的区别。


1
(1) 不清楚您为什么认为正在使用 libc++_shared.so。您有建议吗? (2) 不清楚您为什么认为正在使用 libstdc++。这个符号模式不是 libstdc++ 的特征。__1 也不一定存在于所有的 libc++ 安装中。__1 中的 1_LIBCPP_ABI_VERSION 扩展的内容,但它可以配置为扩展为任何内容。 - n. m.
这些绝对不是来自于libstdc++的符号。在libstdc++中有一个ABI命名空间叫做__cxx11,但它是(1)硬编码的,并且(2)并不适用于所有符号,特别是不适用于allocatorchar_traits - n. m.
那么 libc++.solibc++_shared.so 之间有什么区别呢?为什么 Android ndk 不允许“轻松地”“只是”使用 libc++.so 呢? - Elvis Dukaj
我不知道。 - n. m.
@n.1.8e9-我的份在哪里呢?我在这篇帖子中找到了答案:https://dev59.com/3qbja4cB1Zd3GeqPkLDI - Elvis Dukaj
1个回答

3
通过将-DANDROID_STL=c++_shared传递给CMake调用,您明确要求使用共享运行时而不是默认运行时。
文档所述,规则很简单:
1.如果您的所有本机代码都在一个库中,则使用静态libc++(默认值),以便可以删除未使用的代码并获得最小可能的应用程序包。
2.只要您包含额外的库-无论是因为从其他地方包含预编译库还是包含了恰好包含本机代码的Android AAR文件-您必须切换到共享运行时。
规则的理由很简单:C++运行时有一些全局数据结构,必须初始化一次,且只能存在于内存中一次。如果你不小心加载了两个链接了C++运行时的静态库的库,那么就会有两个冲突的内存分配器。这将导致在释放另一个库分配的内存时崩溃,或者在跨库边界传递C++ STL对象(如std::string)时出现问题。
为了完整起见,在旧版NDK中也包括了libstdc++(GNU C++运行时),但从NDK r18开始不再包括。

我仍然感到困惑,因为在我的板子上,库链接到 libc++.so,但是当我使用 ndk 时,我链接到了 libc++_shared.so。 这个库不在目标中,我也需要部署它... 我不能只链接到 libc++.so 吗?有没有办法? - Elvis Dukaj
1
链接页面上的第二个注释非常明确:“注意:libc++不是系统库。如果您使用libc++_shared.so,则必须将其包含在您的应用程序中。如果您使用Gradle构建应用程序,则会自动处理此问题。”换句话说,无论您的板子上有什么,APK都会打包自己的libc++_shared.so。 - Botje
2
只要您包含了额外的库——无论是因为您从其他地方包含了一个预编译库,还是因为您包含了一个包含本地代码的Android AAR文件——您必须切换到共享运行时。但这并非一定如此。请参见https://dev59.com/967la4cB1Zd3GeqPdnkl#52389560。 - Michael
好的评论。我认为Android文档和我都倾向于简单的建议,以谨慎为重。 - Botje

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接