动态链接器解决了 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}
配置文件包含以下内容:
- 位于开头的多个“目录-区段”映射属性,用于让动态链接器选择有效的区段。
-
多个链接器命名空间配置区段:
- 每个区段都包含多个命名空间(图形顶点)以及各命名空间之间的多个回退链接(图形弧)。
- 每个命名空间都具有自己的隔离、搜索路径、允许的路径以及可见性设置。
下表详细介绍了各属性的含义。
“目录-区段”映射属性
属性 | 说明 | 示例 |
---|---|---|
|
指向 每个属性都会将目录下的可执行文件映射到链接器命名空间配置区段。可能会有 2 个(或 2 个以上)属性具有相同的 |
这表示在 在 |
关系属性
属性 | 说明 | 示例 |
---|---|---|
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
- vendor 分区的目标 VNDK 版本
- product 分区的 VNDK 版本
- 已安装的 APEX 模块
链接器配置是通过解析链接器命名空间之间的依赖关系来创建的。例如,如果 APEX 模块上有包括依赖项更新在内的任何更新,就会生成链接器配置,以反映这些变化。如需详细了解如何创建链接器配置,请参阅 ${android-src}/system/linkerconfig
。
链接器命名空间隔离
有 3 类配置文件。系统会根据 BoardConfig.mk
中 PRODUCT_TREBLE_LINKER_NAMESPACES
和 BOARD_VNDK_VERSION
的值在启动时生成相应的配置。
PRODUCT_TREBLE_ LINKER_NAMESPACES |
BOARD_VNDK_ VERSION |
选择的配置 | VTS 要求 |
---|---|---|---|
true |
current |
VNDK |
搭载 Android 9 或更高版本的设备的必要配置 |
空 | VNDK Lite |
搭载 Android 8.x 的设备的必要配置 | |
false |
空 | Legacy |
适用于不支持 Treble 的设备 |
VNDK Lite 配置会隔离 SP-HAL 和 VNDK-SP 共享库。在 Android 8.0 中,当 PRODUCT_TREBLE_LINKER_NAMESPACES
为 true
时,该配置必须是动态链接器的配置文件。
VNDK 配置还会隔离 SP-HAL 和 VNDK-SP 共享库。此外,此配置还会提供全面的动态链接器隔离。它可确保 system 分区中的模块不依赖于 vendor 分区中的共享库,反之亦然。
在 Android 8.1 或更高版本中,VNDK 配置是默认配置,强烈建议您将 BOARD_VNDK_VERSION
设置为 current
,以启用全面的动态链接器隔离。
VNDK 配置
VNDK 配置会隔离 system 分区和 vendor 分区之间的共享库依赖项。下文概述了该配置文件与上一子部分中提到的配置相比有哪些不同:
-
框架进程
- 创建了
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.so
libGLESv1_CM.so
libGLESv2.so
libGLESv3.so
libandroid_net.so
libc.so
libdl.so
liblog.so
libm.so
libnativewindow.so
libneuralnetworks.so
libsync.so
libvndksupport.so
libvulkan.so
-
VNDK-SP
android.hardware.graphics.common@1.0.so
android.hardware.graphics.mapper@2.0.so
android.hardware.renderscript@1.0.so
android.hidl.memory@1.0.so
libRSCpuRef.so
libRSDriver.so
libRS_internal.so
libbase.so
libbcinfo.so
libc++.so
libcutils.so
libhardware.so
libhidlbase.so
libhidlmemory.so
libhidltransport.so
libhwbinder.so
libion.so
libutils.so
libz.so
您可以查看设备的 /linkerconfig/ld.config.txt
,了解更多详情。
VNDK Lite 配置
从 Android 8.0 开始,动态链接器将配置为隔离 SP-HAL 和 VNDK-SP 共享库,以使其符号不会与其他框架共享库发生冲突。链接器命名空间之间的关系如下所示。
LL-NDK 和 VNDK-SP 代表以下共享库:
-
LL-NDK
libEGL.so
libGLESv1_CM.so
libGLESv2.so
libc.so
libdl.so
liblog.so
libm.so
libnativewindow.so
libstdc++.so
(不在配置中)libsync.so
libvndksupport.so
libz.so
(已移到配置中的 VNDK-SP)
-
VNDK-SP
android.hardware.graphics.common@1.0.so
android.hardware.graphics.mapper@2.0.so
android.hardware.renderscript@1.0.so
android.hidl.memory@1.0.so
libbase.so
libc++.so
libcutils.so
libhardware.so
libhidlbase.so
libhidlmemory.so
libhidltransport.so
libhwbinder.so
libion.so
libutils.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.so libft2.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.in
ld.config.txt
对于具有运行时链接器命名空间隔离的设备 ld.config.txt
ld.config.vndk_lite.txt
对于具有 VNDK-SP 链接器命名空间隔离的设备 ld.config.legacy.txt
ld.config.legacy.txt
对于搭载 Android 7.x 或更低版本的旧设备 - 移除了
android.hardware.graphics.allocator@2.0.so
。 - 添加了
product
和odm
分区。