跳到主要内容

C/C++标准库机制概述

HarmonyOS NDK提供业界标准库libc标准库c++标准库,本文用于介绍C/C++标准库在HarmonyOS中的机制,开发者了解这些机制有助于在NDK开发过程中避免相关问题。

C++兼容性

在HarmonyOS系统中,系统库和应用Native库均使用C++标准库(参考libc++版本)。系统库依赖的C++标准库随镜像版本升级,应用Native库依赖的C++标准库随编译使用的SDK版本升级。由于两部分依赖的C++标准库会跨多个大版本,可能导致ABI兼容性问题。为解决此问题,HarmonyOS对系统库和应用Native库依赖的C++标准库进行了区分。

  • 系统库:使用libc++.so,随系统镜像发布。
  • 应用Native库:使用libc++_shared.so,随应用发布。

两个库使用不同的C++命名空间。libc++.so使用__h,libc++_shared.so使用__n1。

系统和应用的C++标准库不能混用。Native API接口只能是C接口,用于隔离C++运行环境。如果HAR包中的libc++_shared.so版本不同于应用,可能导致不兼容问题。解决方法是使用相同SDK版本更新HAR包。

已知C++兼容性问题:

应用启动或dlopen时,hilog报错symbol not found, s=__emutls_get_address。原因是API9及之前版本的libc++_shared.so无此符号,而API11之后版本的libc++_shared.so有此符号。解决方法是更新应用或HAR包的SDK版本。

musl libc动态链接器

动态库加载命名空间隔离

动态库加载命名空间(namespace,下面统称为ns)是动态链接器设计的一个概念(区别于C++语言中的命名空间),其设计的主要目的是为了在进程中做native库资源访问的管控,以达到安全隔离的目的。例如系统native库允许加载系统目录(/system/lib64;/vendor/lib64等)下的native库,但是普通应用native库仅允许加载普通应用native库和ndk库,而不允许直接加载系统native库。

动态链接器在加载编译依赖(DT_NEEDED)中指定的共享库或调用dlopen加载指定的共享库时,都需要关联到具体的 ns。

HarmonyOS中动态库加载namespace配置的情况

  • default ns:动态链接器启动时默认创建的ns,它可以搜索/system/lib{abi};/vendor/lib{abi}等系统目录路径下的so。
  • ndk ns:动态链接器启动时默认创建的ns,它可以搜索/system/lib{abi}/ndk目录下的so,主要是暴露了NDK接口的so。
  • app ns: 应用启动时创建的ns,它的搜索路径一般是应用的安装路径(可能为沙箱路径),即可加载应用的so。

当前的命名空间机制主要限制了应用native库和系统native库之间的调用,规则如图所示:

  1. default ns和ndk ns可以互相访问全部so,不能访问app ns的so。
  2. app ns能访问ndk ns的全部so,不能访问default ns的so。

rpath机制

rpath(run-time path)是在运行时指定共享库搜索路径的机制。该机制允许在可执行文件或共享库中嵌入一个用于在运行时指定库的搜索路径的信息。

由于命名空间隔离机制,应用仅允许加载对应安装目录的native库路径下(例如arm64平台上为libs/arm64)的应用native库,当应用程序涉及加载多个native库时,创建多个加载路径会导致无法加载新目录下的native库。这种情况可以通过rpath机制编译时指定搜索路径。

例如,应用安装目录lib/arm64下的libhello.so依赖新创建路径lib/arm64/module下的libworld.so,那么在应用的CMakeLists.txt里设置上rpath编译选项后编译,使用readelf查看libhello.so的rpath配置如图所示,$ORIGIN为libhello.so所在路径,运行时即可正常加载module目录下的libworld.so。

SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
SET(CMAKE_INSTALL_RPATH "\${ORIGIN}/module")

支持dlclose

支持使用dlclose真正卸载动态库的能力。

支持symbol-version机制

symbol-version是libc在动态链接-符号重定位阶段的符号检索机制,支持不同版本的符号重定位,也可以帮助解决重复符号的问题。可参考LD Version Scripts (GNU Gnulib)

网络接口select支持fd fortify检测

宏定义FD_SET和FD_CLR增加了对fd有效值的检查。如果传入的fd不在区间[0, 1024)中,将触发abort crash。

宏定义FD_ISSET增加了对fd有效值的检查,如果传入的fd不在区间[0, 1024)中会返回false。

全球化支持

自API12起,newlocale及setlocale接口支持将locale设置C、C.UTF-8、en_US、en_US.UTF-8、zh_CN及zh_CN.UTF-8。新增在zh_CN及zh_CN.UTF-8的locale设置下对strtod_l、wcstod_l和localeconv的支持。注意strtod_l及wcstod_l不支持对十六进制及十六进制小数的转换。

fdsan功能

fdsan使用指导可以帮助检测文件的重复关闭和关闭后使用问题。

信号使用

为避免与系统保留信号冲突,开发者在使用信号时需遵循以下规则:

  • 信号编号 1~34:为系统内部保留信号,禁止使用;
  • 信号编号 35~45: 截止到目前 API 19,这些信号已被系统内部模块(如内存、DFX、运行时、系统服务等)占用,为避免与系统行为冲突并导致不可预期的问题,请勿使用该范围内的信号。
  • SIGRTMIN和__libc_current_sigrtmin的值是35, 表示可供应用程序使用的实时信号起始编号(应用实际只能使用46及以上的信号)。

鸿蒙内部信号使用统计如下:

编号名称备注编号名称备注
1SIGHUP控制终端挂起24SIGXCPU超出 CPU 时间限制
2SIGINT中断25SIGXFSZ文件超出大小限制
3SIGQUIT键盘退出26SIGVTALRM虚拟定时器
4SIGILL非法指令27SIGPROFprofiling 计时器到期
5SIGTRAP调试断点28SIGWINCH终端窗口大小变化
6SIGABRT中止信号29SIGIOI/O 可用通知
7SIGBUS总线错误30SIGPWR电源故障
8SIGFPE算术异常31SIGSYS非法系统调用
9SIGKILL强制终止32SIGTIMER定时器定时信号
10SIGUSR1用户自定义信号 133SIGCANCEL线程取消信号
11SIGSEGV无效内存访问34SIGSYNCCALL同步调用信号
12SIGUSR2用户自定义信号 235MUSL_SIGNAL_NATIVE_REMOTE (SIGRTMIN + 0)系统自留
13SIGPIPE管道损坏36MUSL_SIGNAL_HOOK (SIGRTMIN + 1)系统自留
14SIGALRM定时器信号37MUSL_SIGNAL_UNHOOK (SIGRTMIN + 2)系统自留
15SIGTERM程序终止请求38MUSL_SIGNAL_NATIVE_LOCAL (SIGRTMIN + 3)系统自留
16SIGSTKFLT协处理器栈错误39MUSL_SIGNAL_JSHEAP (SIGRTMIN + 4)系统自留
17SIGCHLD子进程退出/停止40MUSL_SIGNAL_JSHEAP_PRIV (SIGRTMIN + 5)系统自留
18SIGCONT继续执行41MUSL_SIGNAL_SAMPLE_STACK (SIGRTMIN + 6)系统自留
19SIGSTOP强制停止42MUSL_SIGNAL_LEAK_STACK (SIGRTMIN + 7)系统自留
20SIGTSTP停止在终端输入43MUSL_SIGNAL_RECYCLE_JEMALLOC (SIGRTMIN + 8)系统自留
21SIGTTIN后台读终端44MUSL_SIGNAL_MEMCHECK (SIGRTMIN + 9)系统自留
22SIGTTOU后台写终端45MUSL_SIGNAL_FDTRACK (SIGRTMIN + 10)系统自留
23SIGURG套接字有紧急数据---