鏈接器命名空間

動態連結器解決了 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}

設定檔包括:

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

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

目錄部分映射屬性

財產描述例子

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.paths中或permitted.paths下的共享庫可以載入到sphal命名空間。

namespace. name . search.paths

用於搜尋共享庫的以冒號分隔的目錄清單。

如果對dlopen()DT_NEEDED條目的函式呼叫未指定完整路徑,則search.paths中指定的目錄將會加入到請求的函式庫名稱之前。清單開頭指定的目錄具有較高的優先權。

isolatedtrue時,可以載入search.paths目錄之一(不包括子目錄)中的共享庫,而不管permitted.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啟用ASan時, namespace. name . permitted.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()來開啟連結器命名空間中的共用程式庫。

如果visibletrueandroid_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
  • 供應商分區的目標 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對於非 Treble 設備

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-SP 共享庫載入到vndk命名空間。
  • 供應商流程

    • 建立defaultvndksystem命名空間。
    • default命名空間是隔離的。
    • 供應商共享庫被載入到default命名空間。
    • VNDK 和 VNDK-SP 共享庫載入到vndk命名空間。
    • LL-NDK 及其相依性被載入到system命名空間。

連結器命名空間之間的關係如下所示。

VNDK 配置中所述的連結器命名空間圖
圖 1.連結器命名空間隔離(VNDK 配置)

在上圖中, LL-NDKVNDK-SP代表以下共享庫:

  • 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 共用程式庫,以便它們的符號不會與其他框架共用程式庫衝突。連結器命名空間之間的關係如下所示。

VNDK Lite 配置中所述的連結器命名空間圖
圖 2.連結器命名空間隔離(VNDK Lite 配置)

LL-NDKVNDK-SP代表以下共享程式庫:

  • 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 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 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 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 在運行時產生它們。

安卓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分區。