鏈接器命名空間

動態鏈接器解決了 Treble VNDK 設計中的兩個挑戰:

  • SP-HAL 共享庫及其依賴項(包括 VNDK-SP 庫)被加載到框架進程中。應該有一些機制來防止符號衝突。
  • dlopen()android_dlopen_ext()可以引入一些在構建時不可見的運行時依賴項,並且使用靜態分析很難檢測到。

這兩個挑戰可以通過鏈接器命名空間機制來解決。該機制由動態鏈接器提供。它可以隔離不同鏈接器命名空間中的共享庫,使庫名相同但符號不同的庫不會發生衝突。

另一方面,鏈接器命名空間機制提供了靈活性,使得一些共享庫可以由鏈接器命名空間導出並由另一個鏈接器命名空間使用。這些導出的共享庫可以成為對其他程序公共的應用程序編程接口,同時將它們的實現細節隱藏在它們的鏈接器命名空間中。

例如,/system/lib[ /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}

配置文件包括:

  • 開頭的幾個目錄-節映射屬性供動態鏈接器選擇有效節。
  • 幾個鏈接器命名空間配置部分:
    • 每個部分包含幾個命名空間(圖頂點)和命名空間之間的幾個後備鏈接(圖弧)。
    • 每個命名空間都有自己的隔離、搜索路徑、允許的路徑和可見性設置。

下表詳細描述了每個屬性的含義。

目錄部分映射屬性

財產描述例子

dir. name

[ name ]部分適用的目錄的路徑。

每個屬性將目錄下的可執行文件映射到鏈接器命名空間配置部分。可能有兩個(或多個)具有相同name但指向不同目錄的屬性。

dir.system = /system/bin
dir.system = /system/xbin
dir.vendor = /vendor/bin

這表明[system]部分中指定的配置適用於從/system/bin/system/xbin加載的可執行文件。

[vendor]部分中指定的配置適用於從/vendor/bin加載的可執行文件。

關係屬性

財產描述例子
additional. namespaces

該部分的其他名稱空間(除了default名稱空間)的逗號分隔列表。

additional. namespaces = sphal, vndk

這表明[system]配置中有三個命名空間( defaultsphalvndk )。

namespace. name . links

以逗號分隔的後備命名空間列表。

如果在當前命名空間中找不到共享庫,動態鏈接器會嘗試從備用命名空間加載共享庫。在列表開頭指定的命名空間具有更高的優先級。

namespace. sphal. links = default, vndk

如果共享庫或可執行文件請求無法加載到sphal命名空間的共享庫,則動態鏈接器會嘗試從default命名空間加載共享庫。

然後,如果共享庫也無法從default命名空間加載,動態鏈接器會嘗試從vndk命名空間加載共享庫。

最後,如果所有嘗試都失敗,動態鏈接器會返回錯誤。

namespace. name . link. other . shared_libs

當在name命名空間中找不到這些庫時,可以在other命名空間中搜索的以冒號分隔的共享庫列表。

此屬性不能與namespace. name . link. other . allow_all_shared_libs

namespace. sphal. link. default. shared_libs = libc.so: libm.so

這表明後備鏈接僅接受libc.solibm.so作為請求的庫名稱。如果請求的庫名稱不是libc.solibm.so ,則動態鏈接器會忽略從sphaldefault命名空間的回退鏈接。

namespace. name . link. other . allow_all_shared_libs

一個布爾值,指示當在name命名空間中找不到所有共享庫時,是否可以在other命名空間中搜索所有共享庫。

此屬性不能與namespace. name . link. other . shared_libs

namespace. vndk. link. sphal. allow_all_shared_libs = true

這表明所有庫名稱都可以通過從vndksphal命名空間的回退鏈接。

命名空間屬性

財產描述例子
namespace. name . isolated

一個布爾值,指示動態鏈接器是否應檢查共享庫所在的位置。

如果isolatedtrue ,則只能加載search.paths目錄之一(不包括子目錄)或permitted.paths目錄之一(包括子目錄)的共享庫。

如果isolatedfalse (默認),則動態鏈接器不會檢查共享庫的路徑。

namespace. sphal. isolated = true

這表明只有search.pathspermitted.paths下的共享庫才能加載到sphal命名空間中。

namespace. name . search.paths

用於搜索共享庫的以冒號分隔的目錄列表。

如果對dlopen()DT_NEEDED條目的函數調用未指定完整路徑,則search.paths中指定的目錄將附加到請求的庫名稱之前。列表開頭指定的目錄具有更高的優先級。

isolatedtrue時,無論permitted.paths屬性如何,都可以加載search.paths目錄之一(不包括子目錄)的共享庫。

例如,如果search.paths/system/${LIB}並且permitted.paths為空, /system/${LIB}/libc.so可以加載,但/system/${LIB}/vndk/libutils.so無法加載。

namespace. default. search.paths = /system/${LIB}

這表明動態鏈接器在/system/${LIB}中搜索共享庫。

namespace. name . asan.search.paths

啟用AddressSanitizer (ASan)時用於搜索共享庫的以冒號分隔的目錄列表。

namespace. name . search.paths啟用ASan時會忽略namespace. name . search.paths

namespace. default. asan.search.paths = /data/asan/system/${LIB}: /system/${LIB}

這表明當啟用ASan時,動態鏈接器首先搜索/data/asan/system/${LIB}然後搜索/system/${LIB}

namespace. name . permitted.paths

以冒號分隔的目錄列表(包括子目錄),當isolatedtrue時,動態鏈接器可以在其中加載共享庫(除了search.paths )。

也可以加載位於permitted.paths子目錄下的共享庫。例如,如果permitted.paths/system/${LIB} ,則/system/${LIB}/libc.so/system/${LIB}/vndk/libutils.so都可以加載。

如果isolatedfalse ,則permitted.paths將被忽略並發出警告。

namespace. default. permitted.paths = /system/${LIB}/hw

這表明/system/${LIB}/hw下的共享庫可以加載到隔離的default命名空間中。

例如,如果沒有permitted.pathslibaudiohal.so就無法將/system/${LIB}/hw/audio.a2dp.default.so加載到default命名空間中。

namespace. name . asan.permitted.paths

啟用ASan時動態鏈接器可以加載共享庫的目錄的冒號分隔列表。

namespace. name . permitted.paths啟用namespace. name . permitted.paths時將忽略allowed.paths

namespace. default. asan.permitted.paths = /data/asan/system/${LIB}/hw: /system/${LIB}/hw

這表明當啟用ASan時, /data/asan/system/${LIB}/hw/system/${LIB}/hw下的共享庫可以加載到隔離的default命名空間。

namespace. name . visible

一個布爾值,指示程序(除了libc )是否可以使用android_get_exported_namespace()獲取鏈接器命名空間句柄,並通過將句柄傳遞給android_dlopen_ext()來打開鏈接器命名空間中的共享庫。

如果visibletrue ,則android_get_exported_namespace()如果命名空間存在,則始終返回句柄。

如果visiblefalse (默認),則android_get_exported_namespace()始終返回NULL ,而不管命名空間是否存在。只有在以下情況下,共享庫才能加載到此命名空間中:(1) 另一個鏈接器命名空間請求它們,該鏈接器命名空間具有指向此命名空間的回退鏈接,或者 (2) 其他共享庫或此命名空間中的可執行文件請求它們。

namespace. sphal. visible = true

這表明android_get_exported_namespace("sphal")可以返回一個有效的鏈接器命名空間句柄。

鏈接器命名空間創建

在 Android 11 中,鏈接器配置在運行時在/linkerconfig下創建,而不是使用${android-src}/system/core/rootdir/etc中的純文本文件。配置是在啟動時根據運行環境生成的,包括以下幾項:

  • 如果設備支持 VNDK
  • Vendor 分區的目標 VNDK 版本
  • 產品分區的 VNDK 版本
  • 安裝的 APEX 模塊

鏈接器配置是通過解析鏈接器命名空間之間的依賴關係來創建的。例如,如果 APEX 模塊上有任何包含依賴項更新的更新,則會生成反映這些更改的鏈接器配置。創建鏈接器配置的更多細節可以在${android-src}/system/linkerconfig中找到。

鏈接器命名空間隔離

共有三種配置類型。根據BoardConfig.mkPRODUCT_TREBLE_LINKER_NAMESPACESBOARD_VNDK_VERSION的值,在啟動時會生成相應的配置。

PRODUCT_TREBLE_
LINKER_NAMESPACES
BOARD_VNDK_
VERSION
選定的配置VTS 要求
true current VNDK對於搭載 Android 9 或更高版本的設備必須
空的VNDK Lite對搭載 Android 8.x 的設備強制要求
false空的Legacy對於非高音設備

VNDK Lite 配置隔離了 SP-HAL 和 VNDK-SP 共享庫。在 Android 8.0 中,當PRODUCT_TREBLE_LINKER_NAMESPACEStrue時,這必須是動態鏈接器的配置文件。

VNDK 配置還隔離了 SP-HAL 和 VNDK-SP 共享庫。此外,此配置提供了完整的動態鏈接器隔離。它確保系統分區中的模塊不會依賴於供應商分區中的共享庫,反之亦然。

在 Android 8.1 或更高版本中,VNDK 配置是默認配置,強烈建議通過將BOARD_VNDK_VERSION設置為current來啟用完全動態鏈接器隔離。

VNDK 配置

VNDK 配置隔離了系統分區和供應商分區之間的共享庫依賴關係。與上一小節中提到的配置相比,差異概述如下:

  • 框架流程

    • defaultvndksphalrs命名空間被創建。
    • 所有命名空間都是隔離的。
    • 系統共享庫被加載到default命名空間中。
    • SP-HAL 被加載到sphal命名空間中。
    • 加載到vndk命名空間中的 VNDK-SP 共享庫。
  • 供應商流程

    • 創建defaultvndksystem命名空間。
    • default命名空間是隔離的。
    • 供應商共享庫被加載到default命名空間中。
    • VNDK 和 VNDK-SP 共享庫被加載到vndk命名空間中。
    • LL-NDK 及其依賴項被加載到system命名空間中。

鏈接器命名空間之間的關係如下所示。

VNDK 配置中描述的鏈接器命名空間圖
圖 1.鏈接器命名空間隔離(VNDK 配置)

在上圖中, LL-NDKVNDK-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 精簡版配置

從 Android 8.0 開始,動態鏈接器被配置為隔離 SP-HAL 和 VNDK-SP 共享庫,這樣它們的符號就不會與其他框架共享庫發生衝突。鏈接器命名空間之間的關係如下所示。

VNDK Lite 配置中描述的鏈接器命名空間圖
圖 2.鏈接器命名空間隔離(VNDK Lite 配置)

LL-NDKVNDK-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-NDK
libmediandk.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 更改了以下動態鏈接器配置文件的名稱。
    安卓 8.x安卓 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
  • 添加了productodm分區。