动态链接器解决了 Treble VNDK 设计中的两个难题
- SP-HAL 共享库及其依赖项(包括 VNDK-SP 库)会加载到框架进程中。应该有一些机制来防止符号冲突。
dlopen()和android_dlopen_ext()可能会引入一些在编译时不可见且难以使用静态分析检测到的运行时依赖项。
这两个难题可以通过链接器命名空间机制来解决。此机制由动态链接器提供。它可以隔离不同链接器命名空间中的共享库,以便库名称相同但符号不同的库不会冲突。
另一方面,链接器命名空间机制提供了灵活性,使某些共享库可以由一个链接器命名空间导出,并由另一个链接器命名空间使用。这些导出的共享库可以成为其他程序公开的应用程序编程接口,同时将其实现详情隐藏在其链接器命名空间内。
例如,/system/lib[64]/libcutils.so 和 /system/lib[64]/vndk-sp-${VER}/libcutils.so 是两个共享库。这两个库可以具有不同的符号。它们被加载到不同的链接器命名空间中,以便框架模块可以依赖于 /system/lib[64]/libcutils.so,而 SP-HAL 共享库可以依赖于 /system/lib[64]/vndk-sp-${VER}/libcutils.so。
另一方面,/system/lib[64]/libc.so 是一个公共库的示例,它由一个链接器命名空间导出并导入到许多链接器命名空间中。/system/lib[64]/libc.so 的依赖项(例如 libnetd_client.so)会加载到 /system/lib[64]/libc.so 所在的命名空间中。其他命名空间将无法访问这些依赖项。此机制封装了实现详情,同时提供了公共接口。
工作原理
动态链接器负责加载 DT_NEEDED 条目中指定的共享库或 dlopen() 或 android_dlopen_ext() 的参数指定的共享库。在这两种情况下,动态链接器都会查找调用方所在的链接器命名空间,并尝试将依赖项加载到同一链接器命名空间中。如果动态链接器无法将共享库加载到指定的链接器命名空间中,它会向链接的链接器命名空间请求导出的共享库。
配置文件格式
配置文件格式基于 INI 文件格式。典型的配置文件如下所示
dir.system = /system/bin dir.system = /system/xbin dir.vendor = /vendor/bin [system] additional.namespaces = sphal,vndk namespace.default.isolated = true namespace.default.search.paths = /system/${LIB} namespace.default.permitted.paths = /system/${LIB}/hw namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB} namespace.default.asan.permitted.paths = /data/asan/system/${LIB}/hw:/system/${LIB}/hw namespace.sphal.isolated = true namespace.sphal.visible = true namespace.sphal.search.paths = /odm/${LIB}:/vendor/${LIB} namespace.sphal.permitted.paths = /odm/${LIB}:/vendor/${LIB} namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}:/odm/${LIB} namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}:/vendor/${LIB} namespace.sphal.asan.permitted.paths = /data/asan/odm/${LIB}:/odm/${LIB} namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}:/vendor/${LIB} namespace.sphal.links = default,vndk namespace.sphal.link.default.shared_libs = libc.so:libm.so namespace.sphal.link.vndk.shared_libs = libbase.so:libcutils.so namespace.vndk.isolated = true namespace.vndk.search.paths = /system/${LIB}/vndk-sp-29 namespace.vndk.permitted.paths = /system/${LIB}/vndk-sp-29 namespace.vndk.links = default namespace.vndk.link.default.shared_libs = libc.so:libm.so [vendor] namespace.default.isolated = false namespace.default.search.paths = /vendor/${LIB}:/system/${LIB}
配置文件包括
- 开头的多个目录-部分映射属性,供动态链接器选择有效的部分。
- 多个链接器命名空间配置部分
- 每个部分包含多个命名空间(图顶点)和命名空间之间的多个回退链接(图弧)。
- 每个命名空间都有自己的隔离、搜索路径、允许的路径和可见性设置。
下表详细描述了每个属性的含义。
目录-部分映射属性
| 属性 | 描述 | 示例 |
|---|---|---|
|
应用 每个属性将目录下的可执行文件映射到链接器命名空间配置部分。可能有两个(或更多)属性具有相同的 |
这表示
|
关系属性
| 属性 | 描述 | 示例 |
|---|---|---|
additional. |
该部分的附加命名空间(除了 |
这表示 |
namespace. |
回退命名空间的逗号分隔列表。 如果在当前命名空间中找不到共享库,则动态链接器会尝试从回退命名空间加载共享库。列表中开头指定的命名空间具有更高的优先级。 |
如果共享库或可执行文件请求无法加载到 然后,如果也无法从 最后,如果所有尝试都失败,则动态链接器会返回错误。 |
namespace. |
当在 此属性不能与 |
这表示回退链接仅接受 |
namespace. |
一个布尔值,指示当在 此属性不能与 |
这表示所有库名称都可以通过从 |
命名空间属性
| 属性 | 描述 | 示例 |
|---|---|---|
namespace. |
一个布尔值,指示动态链接器是否应检查共享库的驻留位置。 如果 如果 |
这表示只有 |
namespace. |
用于搜索共享库的目录的冒号分隔列表。 如果对 当 例如,如果 |
这表示动态链接器在 |
namespace. |
启用 AddressSanitizer (ASan) 时,用于搜索共享库的目录的冒号分隔列表。 启用 ASan 时,会忽略 |
这表示启用 ASan 后,动态链接器首先搜索 |
namespace. |
当
如果 |
这表示 例如,如果没有 |
namespace. |
启用 ASan 时,动态链接器可以加载共享库的目录的冒号分隔列表。 启用 ASan 时,会忽略 |
这表示启用 ASan 后, |
namespace. |
一个布尔值,指示程序( 如果 如果 |
这表示 |
链接器命名空间创建
在 Android 11 中,链接器配置是在运行时在 /linkerconfig 下创建的,而不是使用 ${android-src}/system/core/rootdir/etc 中的纯文本文件。配置在启动时根据运行时环境生成,运行时环境包括以下项
- 如果设备支持 VNDK
- 供应商分区目标 VNDK 版本
- 产品分区 VNDK 版本
- 已安装的 APEX 模块
链接器配置是通过解析链接器命名空间之间的依赖关系创建的。例如,如果 APEX 模块上有任何更新(包括依赖项更新),则会生成链接器配置以反映这些更改。有关创建链接器配置的更多详情,请参阅 ${android-src}/system/linkerconfig。
链接器命名空间隔离
有三种配置类型。根据 BoardConfig.mk 中 PRODUCT_TREBLE_LINKER_NAMESPACES 和 BOARD_VNDK_VERSION 的值,会在启动时生成相应的配置。
PRODUCT_TREBLE_LINKER_NAMESPACES |
BOARD_VNDK_VERSION |
选定的配置 | VTS 要求 |
|---|---|---|---|
true |
current |
VNDK |
对于搭载 Android 9 或更高版本推出的设备是强制性的 |
| Empty | VNDK Lite |
对于搭载 Android 8.x 版本推出的设备是强制性的 | |
false |
Empty | Legacy |
对于非 Treble 设备 |
VNDK Lite 配置隔离了 SP-HAL 和 VNDK-SP 共享库。在 Android 8.0 中,当 PRODUCT_TREBLE_LINKER_NAMESPACES 为 true 时,这必须是动态链接器的配置文件。
VNDK 配置还隔离了 SP-HAL 和 VNDK-SP 共享库。此外,此配置还提供了完整的动态链接器隔离。它确保系统分区中的模块不会依赖于供应商分区中的共享库,反之亦然。
在 Android 8.1 或更高版本中,VNDK 配置是默认配置,强烈建议通过将 BOARD_VNDK_VERSION 设置为 current 来启用完整的动态链接器隔离。
VNDK 配置
VNDK 配置隔离了系统分区和供应商分区之间的共享库依赖项。与上一小节中提到的配置相比,差异概述如下
-
框架进程
- 创建了
default、vndk、sphal和rs命名空间。 - 所有命名空间都是隔离的。
- 系统共享库加载到
default命名空间中。 - SP-HAL 加载到
sphal命名空间中。 - VNDK-SP 共享库加载到
vndk命名空间中。
- 创建了
-
供应商进程
- 创建了
default、vndk和system命名空间。 default命名空间是隔离的。- 供应商共享库加载到
default命名空间中。 - VNDK 和 VNDK-SP 共享库加载到
vndk命名空间中。 - LL-NDK 及其依赖项加载到
system命名空间中。
- 创建了
链接器命名空间之间的关系如下图所示。
图 1. 链接器命名空间隔离(VNDK 配置)。
在上图中,LL-NDK 和 VNDK-SP 代表以下共享库
-
LL-NDK
libEGL.solibGLESv1_CM.solibGLESv2.solibGLESv3.solibandroid_net.solibc.solibdl.soliblog.solibm.solibnativewindow.solibneuralnetworks.solibsync.solibvndksupport.solibvulkan.so
-
VNDK-SP
android.hardware.graphics.common@1.0.soandroid.hardware.graphics.mapper@2.0.soandroid.hardware.renderscript@1.0.soandroid.hidl.memory@1.0.solibRSCpuRef.solibRSDriver.solibRS_internal.solibbase.solibbcinfo.solibc++.solibcutils.solibhardware.solibhidlbase.solibhidlmemory.solibhidltransport.solibhwbinder.solibion.solibutils.solibz.so
您可以在设备中的 /linkerconfig/ld.config.txt 中找到更多详情。
VNDK Lite 配置
从 Android 8.0 开始,动态链接器配置为隔离 SP-HAL 和 VNDK-SP 共享库,以使其符号不会与其他框架共享库冲突。链接器命名空间之间的关系如下所示。
LL-NDK 和 VNDK-SP 代表以下共享库
-
LL-NDK
libEGL.solibGLESv1_CM.solibGLESv2.solibc.solibdl.soliblog.solibm.solibnativewindow.solibstdc++.so(不在配置中)libsync.solibvndksupport.solibz.so(在配置中移至 VNDK-SP)
-
VNDK-SP
android.hardware.graphics.common@1.0.soandroid.hardware.graphics.mapper@2.0.soandroid.hardware.renderscript@1.0.soandroid.hidl.memory@1.0.solibbase.solibc++.solibcutils.solibhardware.solibhidlbase.solibhidlmemory.solibhidltransport.solibhwbinder.solibion.solibutils.so
下表列出了框架进程的命名空间配置,该配置摘自 VNDK Lite 配置中的 [system] 部分。
| 命名空间 | 属性 | 值 |
|---|---|---|
default |
search.paths |
/system/${LIB}/odm/${LIB}/vendor/${LIB}/product/${LIB}
|
isolated |
false |
|
sphal |
search.paths |
/odm/${LIB}/vendor/${LIB}
|
permitted.paths |
/odm/${LIB}/vendor/${LIB}
|
|
isolated |
true |
|
visible |
true |
|
links |
default,vndk,rs |
|
link.default.shared_libs |
LL-NDK | |
link.vndk.shared_libs |
VNDK-SP | |
link.rs.shared_libs |
libRS_internal.so |
|
vndk(对于 VNDK-SP) |
search.paths |
/odm/${LIB}/vndk-sp/vendor/${LIB}/vndk-sp/system/${LIB}/vndk-sp-${VER}
|
permitted.paths |
/odm/${LIB}/hw/odm/${LIB}/egl/vendor/${LIB}/hw/vendor/${LIB}/egl/system/${LIB}/vndk-sp-${VER}/hw |
|
isolated |
true |
|
visible |
true |
|
links |
default |
|
link.default.shared_libs |
LL-NDK | |
rs(对于 RenderScript) |
search.paths |
/odm/${LIB}/vndk-sp/vendor/${LIB}/vndk-sp/system/${LIB}/vndk-sp-${VER}/odm/${LIB}/vendor/${LIB}
|
permitted.paths |
/odm/${LIB}/vendor/${LIB}/data(对于编译后的 RS 内核) |
|
isolated |
true |
|
visible |
true |
|
links |
default,vndk |
|
link.default.shared_libs |
LL-NDKlibmediandk.solibft2.so
|
|
link.vndk.shared_libs |
VNDK-SP |
下表介绍了供应商进程的命名空间配置,该配置摘自 VNDK Lite 配置中的 [vendor] 部分。
| 命名空间 | 属性 | 值 |
|---|---|---|
default |
search.paths |
/odm/${LIB}/odm/${LIB}/vndk/odm/${LIB}/vndk-sp/vendor/${LIB}/vendor/${LIB}/vndk/vendor/${LIB}/vndk-sp/system/${LIB}/vndk-${VER}/system/${LIB}/vndk-sp-${VER}/system/${LIB}(已弃用)/product/${LIB}(已弃用) |
isolated |
false |
您可以在设备中的 /linkerconfig/ld.config.txt 中找到更多详情。
文档历史记录
Android 11 变更
- 在 Android 11 中,静态
ld.config.*.txt文件已从代码库中移除,LinkerConfig 会在运行时生成它们。
Android 9 变更
- 在 Android 9 中,
vndk链接器命名空间已添加到供应商进程,并且 VNDK 共享库与默认链接器命名空间隔离。 - 将
PRODUCT_FULL_TREBLE替换为更具体的PRODUCT_TREBLE_LINKER_NAMESPACES。 - Android 9 更改了以下动态链接器配置文件的名称。
Android 8.x Android 9 描述 ld.config.txt.inld.config.txt对于具有运行时链接器命名空间隔离的设备 ld.config.txtld.config.vndk_lite.txt对于具有 VNDK-SP 链接器命名空间隔离的设备 ld.config.legacy.txtld.config.legacy.txt对于运行 Android 7.x 或更低版本的旧版设备 - 移除
android.hardware.graphics.allocator@2.0.so。 - 添加了
product和odm分区。