一、VNDK概述
VNDK(Vendor Native Development Kit)是一組專門用於vendor實現其HAL的lib庫,因為自Android 8.0以來,Google引入了Treble架構,希望對vendor和system分區進行解耦處理,期待實現:framwork進程不加載vendor共享庫,vendor進程僅加載vendor共享庫(和部分framework共享庫),而framework進程和vendor進程之間通過HIDL和hwbinder來通信。總結為如下幾點:
- platform和Vendor的構建是相互隔離的;
- platform lib對應 system.img;
- vendor lib對應 vendor.img;
- 大多數情況下,Vendor lib跟系統核心不能相互使用;Vendor lib不允許dlopen私有的系統庫;
- 合作伙伴不允許為自己的產品在VNDK新增lib,只能貢獻到AOSP;
這一切都是為系統庫與Vendor庫之間的解耦合,在Android P上采用該方案,則下一個大版本Android Q更新,可以直接將新的System Q加上老Vendor P,組成新版本Android。
其中VNDK + Framework libs組成system.img, Vendor libs組成vendor.img。
Android P新添加命名空間namespace:
- System命名空間/system/lib/;
- Vendor命名空間有/system/lib/vndk,/system/lib/vndk-sp,/vendor/lib/vndk,/vendor/lib/vndk-sp
二、framework和vendor之間庫的區分
2.1 vendor進程可訪問的部分framework共享庫
根據特性及使用方法的差異,可將framework共享庫大致分為三類:
(1)已知API/ABI穩定的framework共享庫 - 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。
對於這些庫,在Android.bp都包含llndk_library模塊的定義,該定義指定了模塊名稱和符號文件:
llndk_library { name: "libvndksupport", symbol_file: "libvndksupport.map.txt", }
編譯系統會根據符號文件為vendor模塊生成stub共享庫,需滿足條件:未在以_PRIVATE或_PLATFORM結尾的部分中定義,不含#platform-only標記,並且不含#introduce*標記或者該標記與目標匹配。
#libvndksupport.map.txt LIBVNDKSUPPORT { global: android_load_sphal_library; # vndk android_unload_sphal_library; # vndk local: *; };
(2)可以安全復制兩次的framework共享庫 - Eligible-VNDK,需要滿足以下幾個條件:
a.不與framework之間進行IPC通信。
b.與ART虛擬機無關。
c.不讀寫文件格式不穩定的文件或分區。
d.沒有特殊的軟件許可。
e.code所有者不反對vendor使用。
(3)除上述兩種以外的framework共享庫 - FWK-Only,通常具有以下幾個特點:
a.被視為framework內部實現。 b.不得由vendor訪問。 c.不穩定的ABI/API。 d.不會被復制。
2.2 SP-HAL(Same-Pocess HAL)
SP-HAL是一組預先確定的HAL,被加載到framework進程中的vendor共享庫。它由鏈接器命名空間進行隔離,必須僅依賴於LL-NDK和VNDK-SP(部分預定義的Eligible-VNDK庫)。無論是SP-HAL還是VNDK-SP都是由Google來定義的。
SP-HAL有:libGLESv1_CM_${driver}.so、libGLESv2_${driver}.so、libGLESv3_${driver}.so、libEGL_${driver}.so、vulkan.${driver}.so、android.hardware.renderscript@1.0-impl.so、android.hardware.graphics.mapper@2.0-impl.so。
SP-HAL可以訪問的VNDK-SP有:android.hardware.graphics.common@1.0.so、android.hardware.graphics.mapper@2.0.so、android.hardware.renderscript@1.0.so (Renderscript)、libRS_internal.so (Renderscript)、libbase.so、libc++.so、libcutils.so、libhardware.so、libhidlbase.so、libhidltransport.so、libhwbinder.so、libion.so、libutils.so、libz.so。
SP-HAL不可見的VNDK-SP有:libRSCpuRef.so (RS)、libRSDriver.so (RS)、libbacktrace.so、libblas.so (RS)、libbcinfo.so (RS)、liblzma.so、libunwind.so。
包含渲染腳本(Rendor script)的FWK-ONLY庫:libft2.so、libmediandk.so。
2.3 VNDK安全策略
framework進程對應於sepolicy中的coremain,而vendor進程對應於non-coredomain。現將共享庫權限整理如下:
類別 | coredomain訪問 | 非coredomain訪問 | 分區位置 |
---|---|---|---|
LL-NDK | Y | Y | system/lib[64] |
LL-NDK-Private | Y | Y | system/lib[64] |
VNDK-SP/VNDK-SP-Private | Y | Y | system/lib[64] |
VNDK-SP-Ext | Y | Y | vendor/lib[64]/vndk-sp |
VNDK | Y | Y | system/lib[64] |
VNDK-Ext | N | Y | vendor/lib[64] |
FWK-ONLY | Y | N | system/lib[64] |
FWK-ONLY-RS | Y | N | system/lib[64] |
SP-HAL | Y | Y | vendor/lib[64]或vendor/lib[64]/hw |
SP-HAL-Dep | Y | Y | vendor/lib[64]vendor/lib[64]/hw |
VND-ONLY | N | Y | vendor/lib[64] |
為了確保vendor分區中VNDK-SP-Ext、SP-HAL、SP-HAL-Dep庫既可以從coredomain訪問,也可以從非coredomain訪問,需要用same_process_hal_file標簽來標記,如file_contexts中所定義:
/vendor/lib(64)?/hw/libMySpHal\.so u:object_r:same_process_hal_file:s0 |
三、VNDK編譯
3.1 VNDK使能
針對Android 9.0新設備,會強制ro.vndk.version屬性非空。該值用於說明VNDK共享庫版本號,存在/vendor/default.prop中。若在makefile中定義的BOARD_VNDK_VERSION不等於current,則將BOARD_VNDK_VERSION定義的值賦給ro.vndk.version。若不相等,則需要先看makefile中PLATFORM_VERSION_CODENAME是否為REL,如果是則采用PLATFORM_SDK_VERSION的值,否則使用PLATFORM_VERSION_CODENAME對應的內容。
若想從Android 8.0之前版本通過OTA升級至Android 9.0,需要在BoardConfig.mk中加入以下內容:
PRODUCT_TREBLE_LINKER_NAMESPACES_OVERRIDE := false
若想從禁用了VNDK運行時增強功能的Android 8.0以后版本通過OTA升級至Android 9.0,需將PRODUCT_USE_VNDK_OVERRIDE := false添加至BoardConfig.mk中,在編譯時ro.vndk.lite屬性會被設為tru,並會自動添加被添加到/vendor/default.prop文件中。動態鏈接器將加載/system/etc/ld.config.vndk_lite.txt中的鏈接器命名空間配置,以隔離SP-HAL和VNDK。
對於first_pai_product屬性大於27的版本,不能定義ro.vndk.lite屬性,比如Android 9.0的VTS測試結果中將會出現VtsTreblePlatformVersionTest Fail項。
VNDK共享庫將安裝到/system/lib[64]/vndk-${ro.vndk.version}中。
VNDK-SP共享庫將安裝到/system/lib[64]/vndk-sp-${ro.vndk.version}中。
動態鏈接器配置文件將安裝到/system/etc/ld.config.${ro.vndk.version}.txt中。
3.2 VNDK編譯配置
編譯系統包含多種類型的對象,其中包括庫(共享、靜態或標頭)和二進制文件:
core:位於system.img中,由system使用。vendor、vendor_available、vndk或vndk-sp庫不能使用此類庫。
cc_binary { name: "libexample", ... }
vendor-only(proprietary):位於vendor.img中,由vendor使用。
cc_binary { name: "libexample", proprietary: true, ... }
vendor_available:位於vendor.img中,由vendor使用(可能包含core的重復項)。
cc_binary { name: "libexample", vendor_available: true, ... }
vndk:位於system.img中,由vendor使用(vendor_available的子集)。
cc_binary { name: "libexample", vendor_available: true, vndk: { enabled: true, } ... }
vndk-sp:位於system.img中,由system間接使用(core的子集)。
cc_binary { name: "libexample", vendor_available: true, vndk: { enabled: true, support_system_process: true, } ... }
llndk:同時由system和vendor使用。
cc_binary { name: "libexample", ... }
庫的vendor版本在bp文件中用-D__ANDROID_VNDK__來標記編譯。對於vendor模塊,在Android.bp中必須將vendor或proprietary設為true,而在Android.mk中需將LOCAL_VENDOR_MODULE或LOCAL_PROPRIETARY_MODULE設為true。將當被標記為vendor_available:true時,該庫將編譯2次,1次為平台編譯,被安裝到/system/lib[64]中;1次為vendor編譯,被安裝到/vendor/lib[64]、/system/lib[64]/vndk或/system/lib[64]/vndk-sp中)。vendor模塊可以設置vendor_available,但不得設置 vndk.enabled和vndk.support_system_proces,因為它們由Google定義,即GSI中不包含這些vendor模塊。現將三種屬性整理如下表:
vendor_available | vndk enabled | vndk support_same_process | 說明 |
---|---|---|---|
true | false | false | 供應商變體為VND-ONLY。共享庫將安裝到/vendor/lib[64]中。 |
true | false | true | 無效(編譯錯誤) |
true | true | false | 供應商變體為VNDK。共享庫將安裝到/system/lib[64]/vndk-${VER}中。 |
true | true | true | 供應商變體為VNDK-SP。共享庫將安裝到/system/lib[64]/vndk-sp-${VER}中。 |
false | false | false | 沒有供應商變體。此模塊為FWK-ONLY。 |
false | false | true | 無效(編譯錯誤) |
fasle | true | false | 供應商變體為VNDK-Private。共享庫將安裝到/system/lib[64]/vndk-${VER}中。供應商模塊不得直接使用這些變體。 |
false | false | true | 供應商變體為VNDK-SP-Private。共享庫將安裝到/system/lib[64]/vndk-sp-${VER}中。供應商模塊不得直接使用這些變體。 |
如果啟用BOARD_VNDK_VERSION,系統會移除多個默認的全局頭文件搜索路徑,主要有以下幾個目錄:frameworks/av/include、frameworks/native/include、frameworks/native/opengl/include、hardware/libhardware/include、hardware/libhardware_legacy/include、hardware/ril/include、libnativehelper/include、libnativehelper/include_deprecated、system/core/include、system/media/audio/include。若某個模塊依賴於以上目錄中的headers,則必須在Android.bp中指定與header_libs、static_libs和shared_libs的依賴關系。或在Android.mk中的中指定LOCAL_HEADER_LIBRARIES、LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES,否則編譯檢查時會報錯。
3.3 VNDK Definition工具
VNDK Definition tool可以幫助vendor將源碼樹移植到Android 8.0環境。該工具會先掃描system.img和vendor.img中二進制文件,然后解析依賴項。還可以指定system.img與GSI進行對比,以確定擴展后的lib庫。
該工具代碼路徑為:$AOSP/development/vndk/tools/definition-tool/vndk_definition_tool.py。該腳本支持以下參數:
$ python3 ./vndk_definition_tool.py vndk \ --system "${PRODUCT_OUT}/system.img" \ --vendor "${PRODUCT_OUT}/vendor.img" \ --aosp-system "gsi_system_image" \ --load-extra-deps "dlopen.dep" \ --output-format=make
$ python3 ./vndk_definition_tool.py check-dep \ --system "${PRODUCT_OUT}/system.img" \ --vendor "${PRODUCT_OUT}/vendor.img" \ --aosp-system "gsi_system_image" \ --tag-file "eligible-list-v3.0.csv" \ --module-info ${PRODUCT_OUT}/module-info.json \ 1> check_dep.txt \ 2> check_dep_err.txt
$ python3 ./vndk_definition_tool.py deps \ --system "${PRODUCT_OUT}/system/" \ --vendor "${PRODUCT_OUT}/vendor/" \ > deps.txt
現將腳本支持的參數說明整理如下表:
第1個參數 | 說明 |
---|---|
vndk | 計算VNDK庫集 |
deps | 輸出二進制文件依賴信息用於debug |
check-dep | 檢查eligible共享庫依賴 |
第2個參數 | 說明 |
---|---|
–system | 開發項目編譯生成的system.img路徑。 |
–vendor | 開發項目編譯生成的vendor.img路徑。 |
–aosp-system | AOSP的system.img,即由Google釋放的用於VTS測試的GSI文件。 |
–tag-file | 由Google釋放的對framework共享庫進行了分類的表格 |
–output-format | 自動生成Android.mk(適用於vndk ) |
–load-extra-deps | 加載外部模塊依賴文件(vndk 不適用) |
–revert | 輸出依賴詳細情況(適用於deps ) |
–module-info | 開發項目編譯生成的module-info.json。(適用於check-dep ) |
dlopen.dep的內容范例如下:
#libart.so依賴於libart-compier.so |
check_dep_err.txt的內容范例如下:
#libRS_internal.so對libmediandk.so的違規依賴 |
deps.txt的內容范例如下:
#ld-android.so沒有依賴 |
各類庫對應目錄:
vndk-sp – /system/lib[64]/vndk-sp。
vndk-sp-ext – /vendor/lib[64]/vndk-sp。
extra-vendor-libs – /vendor/lib[64]
在device/$(VENDOR)/$(DEVICE_NAME)目錄下創建vndk文件夾,用於存放配置vndk庫的Android.mk,其范例如下:
ifneq ($(filter $(DEVICE_NAME),$(TARGET_DEVICE)),) ##_VNDK_SP_## VNDK_SP_LIBRARIES := \ ##_VNDK_SP_EXT_## VNDK_SP_EXT_LIBRARIES := \ libpng ##_EXTRA_VENDOR_LIBS_## EXTRA_VENDOR_LIBRARIES := \ android.hidl.base@1.0 \ com.qualcomm.qti.ant@1.0 \ com.qualcomm.qti.wifidisplayhal@1.0 #------------------------------------------------------------------------------- # VNDK Modules #------------------------------------------------------------------------------- LOCAL_PATH := $(call my-dir) define define-vndk-lib include $$(CLEAR_VARS) LOCAL_MODULE := $1.$2 LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_PREBUILT_MODULE_FILE := $$(TARGET_OUT_INTERMEDIATE_LIBRARIES)/$1.so LOCAL_STRIP_MODULE := false LOCAL_MULTILIB := first LOCAL_MODULE_TAGS := optional LOCAL_INSTALLED_MODULE_STEM := $1.so LOCAL_MODULE_SUFFIX := .so LOCAL_MODULE_RELATIVE_PATH := $3 LOCAL_VENDOR_MODULE := $4 include $$(BUILD_PREBUILT) ifneq ($$(TARGET_2ND_ARCH),) ifneq ($$(TARGET_TRANSLATE_2ND_ARCH),true) include $$(CLEAR_VARS) LOCAL_MODULE := $1.$2 LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_PREBUILT_MODULE_FILE := $$($$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)/$1.so LOCAL_STRIP_MODULE := false LOCAL_MULTILIB := 32 LOCAL_MODULE_TAGS := optional LOCAL_INSTALLED_MODULE_STEM := $1.so LOCAL_MODULE_SUFFIX := .so LOCAL_MODULE_RELATIVE_PATH := $3 LOCAL_VENDOR_MODULE := $4 include $$(BUILD_PREBUILT) endif # TARGET_TRANSLATE_2ND_ARCH is not true endif # TARGET_2ND_ARCH is not empty endef $(foreach lib,$(VNDK_SP_LIBRARIES),\ $(eval $(call define-vndk-lib,$(lib),vndk-sp-gen,vndk-sp,))) $(foreach lib,$(VNDK_SP_EXT_LIBRARIES),\ $(eval $(call define-vndk-lib,$(lib),vndk-sp-ext-gen,vndk-sp,true))) $(foreach lib,$(EXTRA_VENDOR_LIBRARIES),\ $(eval $(call define-vndk-lib,$(lib),vndk-ext-gen,,true))) #------------------------------------------------------------------------------- # Phony Package #------------------------------------------------------------------------------- include $(CLEAR_VARS) LOCAL_MODULE := $(DEVICE_NAME)-vndk LOCAL_MODULE_TAGS := optional LOCAL_REQUIRED_MODULES := \ $(addsuffix .vndk-sp-gen,$(VNDK_SP_LIBRARIES)) \ $(addsuffix .vndk-sp-ext-gen,$(VNDK_SP_EXT_LIBRARIES)) \ $(addsuffix .vndk-ext-gen,$(EXTRA_VENDOR_LIBRARIES)) include $(BUILD_PHONY_PACKAGE) endif # ifneq ($(filter $(DEVICE_NAME),$(TARGET_DEVICE)),)
然后在項目的makefile中添加下面一條內容,使得vndk/Android.mk參與編譯:
PRODUCT_PACKAGES += $(DEVICE_NAME)-vndk
#查看image類型 file system.img #將ext4轉換為raw類型 out/host/linux-x86/bin/simg2img system.img system.raw.img mkdir system #手動掛載system分區 sudo mount -o loop,ro system.raw.img system
四、VNDK延伸
4.1 VNDK擴展
根據模塊中定義的功能,可將模塊分為DA模塊和DX模塊:
(1)Defining-only-AOSP模塊(DA 模塊)不會定義AOSP副本中未包含的新功能。
a.一個未經修改的AOSP庫就是一個DA模塊。
b.如果vendor使用SIMD指令重寫libexample.so中的函數(不添加新函數),那么修改后的libexample.so將是一個DA模塊。
(2)Defining-Extension模塊(DX模塊)要么會定義新功能,要么沒有AOSP副本。
a.如果vendor向 libexample.so 添加一個test函數以訪問某些內部數據,那么修改后的libexample.so將是一個DX庫,而這個新增函數將是該庫的擴展部分。
b.如果vendor定義了一個名為libexample.so的非AOSP庫,那么libexmple.so將是一個DX庫。
根據模塊所使用的功能,可將模塊分為UA模塊和UX模塊。
(1)Using-only-AOSP(UA模塊)僅會在其實現過程中使用AOSP功能。它們不依賴任何非AOSP擴展功能。
a.一個未經修改且完整無缺的AOSP庫即是一個UA模塊。
b.如果修改后的共享庫libexample.so僅依賴於其他AOSP API,那么它將是一個UA模塊。
(2)Using-Extension 模塊(UX模塊)會在其實現過程中依賴某些非 AOSP 功能。
a.如果修改后的libexample.so依賴另一個名為libexample2.so的非AOSP庫,那么修改后的libexample.so將是一個UX模塊。
b.如果vendor修改后的libexample.so1添加了一個新函數,並且其修改后的libexample2.so使用libexample1.so中的這個新增函數,那么修改后的libexample2.so將是一個UX模塊。
4.2 鏈接器命名空間
鏈接器命名空間機制由動態鏈接器提供,可以隔離不同鏈接器命名空間中的共享庫,以確保具有相同庫名稱和不同符號的庫不會發生沖突。鏈接器命名空間機制可提供相應的靈活性,從而將由一個鏈接器命名空間導出的某些共享庫用於另一個鏈接器命名空間。這些導出的共享庫可能會成為對其他程序公開的應用編程接口,同時在其鏈接器命名空間中隱藏實現細節。
動態鏈接器負責加載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]區段所應用到的目錄的路徑。每個屬性都會將目錄下的可執行文件映射到鏈接器命名空間配置區段。可能會有2個(或多個)屬性具有相同的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]配置中有3個命名空間(default、sphal和vndk)。 |
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.so或libm.so作為請求的庫名稱。如果請求的庫名稱不是libc.so,也不是libm.so,則動態鏈接器會忽略從sphal到default命名空間的回退鏈接。 |
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這表示所有庫名稱都可以遍歷從vndk到sphal命名空間的回退鏈接。 |
namespace.name.isolated | 一個布爾值,用於指示動態鏈接器是否應該檢查共享庫在什么位置。如果isolated為true,則只有某個 search.paths目錄(不包含子目錄)中或permitted.paths目錄(包含子目錄)下的共享庫才能加載。如果isolated為false,則動態鏈接器不會檢查共享庫的路徑。 | namespace.sphal.isolated=true這表示只有search.paths中或permitted.paths下的共享庫才能加載到sphal命名空間。 |
namespace.name.search.paths | 以冒號分隔的目錄列表,用於搜索共享庫。如果函數調用dlopen()或DT_NEEDED條目時未指定完整路徑,則在search.paths中指定的目錄將附加到請求的庫名稱前面。在列表開頭指定的目錄優先級較高。如果isolated為true,則任一search.paths目錄(不包含子目錄)中的共享庫都可以加載,無論permitted.paths屬性如何設置。 | namespace.default.search.paths=/system/${LIB}這表示動態鏈接器會在/system/${LIB}中搜索共享庫。 |
namespace.name.asan.search.paths | 以冒號分隔的目錄列表,用於在啟用Address Sanitizer后搜索共享庫。在啟用 Address Sanitizer后,系統會忽略namespace.name.search.paths。 | namespace.default.asan.search.paths=/data/asan/system/${LIB}:/system/${LIB}這表示在啟用Address Sanitizer后,動態鏈接器會先搜索/data/asan/system/${LIB},然后再搜索 /system/${LIB}。 |
namespace.name.permitted.paths | 以冒號分隔的目錄列表(包含子目錄),當isolated為true時,動態鏈接器可在其中加載共享庫(除了search.paths以外)。permitted.paths的子目錄下的共享庫也可以加載。如果isolated為false,則系統會忽略 permitted.paths 並發出相應警告。 | namespace.default.permitted.paths=/system/${LIB}/hw這表示/system/${LIB}/hw下的共享庫可以加載到隔離的default命名空間。 |
namespace.name.asan.permitted.paths | 以冒號分隔的目錄列表,在啟用Address Sanitizer后,動態鏈接器可在其中加載共享庫。在啟用Address Sanitizer后,系統會忽略namespace.name.permitted.paths。 | namespace.default.asan.permitted.paths=/data/asan/system/${LIB}/hw:/system/${LIB}/hw這表示在啟用Address Sanitizer后,/data/asan/system/${LIB}/hw或/system/${LIB}/hw下的共享庫可以加載到隔離的 default命名空間。 |
namespace.name.visible | 一個布爾值,用於指示程序(不包括libc)是否可以包含帶有android_get_exported_namespace()的鏈接器命名空間句柄,以及通過將此句柄傳遞到android_dlopen_ext()打開鏈接器命名空間中的共享庫。如果visible為true,則android_get_exported_namespace()在命名空間存在時始終返回此句柄。如果visible為false(默認值),則無論命名空間是否存在,android_get_exported_namespace()始終返回NULL。僅當符合以下條件時,共享庫才能加載到此命名空間:(1)具有指向此命名空間的回退鏈接的其他鏈接器命名空間請求這些共享庫;(2)此命名空間中的其他共享庫或可執行文件請求這些共享庫。 | namespace.sphal.visible = true這表示android_get_exported_namespace(“sphal”)可以返回有效的鏈接器命名空間句柄。 |
${android-src}/system/core/rootdir/etc中有3個配置文件。系統會根據BoardConfig.mk中 PRODUCT_TREBLE_LINKER_NAMESPACES、BOARD_VNDK_VERSION和BOARD_VNDK_RUNTIME_DISABLE的值選擇不同的配置:
PRODUCT_TREBLE_LINKER_NAMESPACES | BOARD_VNDK_VERSION | BOARD_VNDK_RUNTIME_DISABLE | 選擇的配置 | VTS要求 |
---|---|---|---|---|
true | current | empty | ld.config.txt | 搭載Android P的設備的必要配置。 |
true | current | true | ld.config.vndk_lite.txt | 搭載Android8.x的設備的必要配置。 |
true | empty | any | ld.config.vndk_lite.txt | 搭載Android 8.x的設備的必要配置。 |
false | any | any | ld.config.legacy.txt | 適用於不支持Treble的設備 |
${AOSP}/system/core/rootdir/etc/ld.config.vndk_lite.txt會隔離SP-HAL和VNDK-SP共享庫。在Android 8.0及更高版本中,當PRODUCT_TREBLE_LINKER_NAMESPACES為true時,該配置必須是動態鏈接器的配置文件。
${AOSP}/system/core/rootdir/etc/ld.config.txt也會隔離SP-HAL和VNDK-SP共享庫。此外,ld.config.txt還會提供全面的動態鏈接器隔離。它可確保系統分區中的模塊不依賴於供應商分區中的共享庫,反之亦然。
在Android8.1中,ld.config.txt是默認配置文件,強烈建議您啟用全面的動態鏈接器隔離。但是,如果在Android 8.1中需要清理的依賴項太多,您可以將BOARD_VNDK_RUNTIME_DISABLE添加到BoardConfig.mk中。如果BOARD_VNDK_RUNTIME_DISABLE為true,則會安裝 ${AOSP}/system/core/rootdir/etc/ld.config.vndk_lite.txt。
ld.config.txt會隔離系統分區和供應商分區之間的共享庫依賴項。下文概述了該配置文件與上一小節中提到的ld.config.txt相比有哪些不同:
framework進程:
a.創建了四個命名空間(default、vndk、sphal和rs)。
b.系統會隔離所有命名空間。
c.將系統共享庫加載到default命名空間中。
d.將SP-HAL加載到sphal命名空間中。
e.將VNDK-SP共享庫加載到vndk命名空間中。
vendor進程
a.創建了三個命名空間(default、vndk和system)。
b.系統會隔離default命名空間。
c.將供應商共享庫加載到default命名空間中。
d.將VNDK和VNDK-SP共享庫加載到vndk命名空間中。
e.將LL-NDK及其依賴項加載到system命名空間中。
鏈接器命名空間之間的關系如下圖所示:
在上圖中,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
下表列出了框架進程的命名空間配置(摘自ld.config.txt中的[system]部分):
命名空間 | 屬性 | 值 |
---|---|---|
default | search.paths | /system/${LIB}, /product/${LIB} |
default | permitted.paths | /system/${LIB}/drm, system/${LIB}/extractors, /system/${LIB}/hw, /product/${LIB}, /system/framework, /system/app, /system/priv-app, /vendor/app, /vendor/priv-app, /oem/app, /odm/priv-app, /oem/app, /product/framework, /product/app, /product/priv-app, /data, /mnt/expand |
default | isolated | true |
sphal | search.paths | /odm/${LIB}, /vendor/${LIB} |
sphal | permitted.paths | /odm/${LIB}, /vendor/${LIB} |
sphal | isolated | true |
sphal | visible | true |
links | default,vndk,rs | |
links | link.default.shared_libs | LL-NDK |
links | link.vndk.shared_libs | VNDK-SP |
links | 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} |
vndk(適用於 VNDK-SP) | permitted.paths | /odm/${LIB}/hw, /odm/${LIB}/egl, /vendor/${LIB}/hw, /vendor/${LIB}/egl, /system/${LIB}/vndk-sp-${VER}/hw |
vndk(適用於 VNDK-SP) | isolated | true |
vndk(適用於 VNDK-SP) | visible | true |
vndk(適用於 VNDK-SP) | links | default、sphal |
vndk(適用於 VNDK-SP) | link.default.shared_libs | LL-NDK |
vndk(適用於 VNDK-SP) | link.default.allow_all_shared_libs | true |
rs(適用於 Renderscript) | search.paths | /odm/${LIB}/vndk-sp, /vendor/${LIB}/vndk-sp, /system/${LIB}/vndk-sp-${VER}, /odm/${LIB}, /vendor/${LIB} |
rs(適用於 Renderscript) | permitted.paths | /odm/${LIB}, /vendor/${LIB}, /data(適用於已編譯的 RS 內核) |
rs(適用於 Renderscript) | isolated | true |
rs(適用於 Renderscript) | visible | true |
rs(適用於 Renderscript) | links | default,vndk |
rs(適用於 Renderscript) | link.default.shared_libs | LL-NDK, libmediandk.so, libft2.so |
rs(適用於 Renderscript) | link.vndk.shared_libs | VNDK-SP |
下表列出了供應商進程的命名空間配置(摘自ld.config.txt中的[vendor]部分):
命名空間 | 屬性 | 值 |
---|---|---|
default | search.paths | /odm/${LIB}, /vendor/${LIB} |
default | permitted.paths | /odm, /vendor |
default | isolated | true |
default | visible | true |
default | links | system、vndk |
default | link.system.shared_libs | LL-NDK |
default | link.vndk.shared_libs | VNDK、VNDK-SP(供應商可用) |
vndk | search.paths | /odm/${LIB}/vndk, /odm/${LIB}/vndk-sp, /vendor/${LIB}/vndk, /vendor/${LIB}/vndk-sp, /system/${LIB}/vndk-${VER}, /system/${LIB}/vndk-sp-${VER} |
vndk | isolated | true |
vndk | links | system、default |
vndk | link.system.shared_libs | LL-NDK |
vndk | link.default.allow_all_shared_libs | true |
system | search.paths | /system/${LIB} |
system | isolated | false |
從 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(不在ld.config.txt 中), libsync.so, libvndksupport.so, libz.so(已移到 ld.config.txt中的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
下表列出了框架進程的命名空間配置(摘自ld.config.vndk_lite.txt中的[system]部分):
命名空間 | 屬性 | 值 |
---|---|---|
default | search.paths | /system/${LIB}, /odm/${LIB}, /vendor/${LIB}, /product/${LIB} |
default | isolated | false |
sphal | search.paths | /odm/${LIB}, /vendor/${LIB} |
sphal | permitted.paths | /odm/${LIB}, /vendor/${LIB} |
sphal | isolated | true |
sphal | visible | true |
sphal | links | default,vndk,rs |
sphal | link.default.shared_libs | LL-NDK |
sphal | link.vndk.shared_libs | VNDK-SP |
sphal | 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} |
vndk(適用於 VNDK-SP) | permitted.paths | /odm/${LIB}/hw, /odm/${LIB}/egl, /vendor/${LIB}/hw, /vendor/${LIB}/egl, /system/${LIB}/vndk-sp-${VER}/hw |
vndk(適用於 VNDK-SP) | solated | true |
vndk(適用於 VNDK-SP) | visible | true |
vndk(適用於 VNDK-SP) | links | default |
vndk(適用於 VNDK-SP) | 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} |
rs(適用於 Renderscript) | permitted.paths | /odm/${LIB}, /vendor/${LIB}, /data(適用於已編譯的 RS 內核) |
rs(適用於 Renderscript) | isolated, | true |
rs(適用於 Renderscript) | visible | true |
rs(適用於 Renderscript) | links | default,vndk |
rs(適用於 Renderscript) | link.default.shared_libs | LL-NDK, libmediandk.so, libft2.so |
rs(適用於 Renderscript) | link.vndk.shared_libs | VNDK-SP |
下表列出了vendor進程的命名空間配置(摘自ld.config.vndk_lite.txt中的[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}(已棄用) |
default | isolated | false |
4.3 VNDK快照
VNDK快照是一組適用於Android版本的VNDK-core和VNDK-SP庫。如果system.img包含vendor.img所需的相應VNDK快照,那么只能升級system分區。VNDK快照設計包括兩項操作:(1)從當前系統映像生成預編譯的VNDK快照;(2)將這些預編譯的庫安裝到更高Android版本的system分區。正式的VNDK快照是在Android編譯服務器上自動編譯而成,並包含在${AOSP}/prebuilts/vndk中。此外,也支持本地編譯VNDK快照。
#編譯特定project lunch aosp_arm64_ab-user make -j vndk dist [BOARD_VNDK_VERSION=current] #編譯所有project cd $AOSP DIST_DIR=%dist_dir% development/vndk/snapshot/build.sh
a.VNDK-core和VNDK-SP共享庫的vendor變體。
i.無需LL-NDK 共享庫,因為這類庫是向后兼容的。
ii.對於64位目標,TARGET_ARCH和TARGET_2ND_ARCH庫都將被編譯並包含在內。
b.VNDK-core、VNDK-SP、LL-NDK和VNDK-private庫的列表位於[vndkcore|vndksp|llndk|vndkprivate].libraries.txt。
c.鏈接器配置文件為ld.config.txt。
d.許可文件。
e.module_paths.txt。記錄所有VNDK庫的模塊路徑;檢查GPL項目是否已在指定Android源代碼樹中發布源代碼時,需要用到這種文件。
對於指定VNDK快照ZIP文件android-vndk-{TARGET_ARCH}.zip,系統會根據ABI位數將VNDK預編譯庫分組到名為arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT} 的單獨目錄中。
VNDK 快照目錄結構:
development/vndk/snapshot/update.py腳本可自動將預編譯的VNDK快照添加到源代碼樹中。此腳本將執行以下任務:
a.在/prebuilts/vndk/v中,使用repo start創建新的git分支。
b.獲取VNDK快照編譯軟件工件並將其解壓縮。
c.運行gen_buildfiles.py以自動生成編譯文件(Android.mk、Android.bp)。
d.運行check_gpl_license.py以驗證根據通用公共許可證(GPL)獲得許可的預編譯庫是否在當前源代碼樹中發布了源代碼。
e.使用git commit提交新的更改。
在指定–local選項的情況下,update.py會從本地$DIST_DIR(而非Android編譯服務器中)提取VNDK快照編譯工件。但由於本地模式僅用於測試,因此該腳本將跳過 GPL 許可檢查和 git commit 步驟。
python update.py <VER> --local
prebuilts/vndk 的目錄結構:
system.img在編譯時使用BOARD_VNDK_VERSION、PRODUCT_EXTRA_VNDK_VERSIONS和ro.vndk.version中的信息安裝VNDK快照庫。可以使用以下選項之一控制從/prebuilts/vndk/v中安裝哪些VNDK快照:
a.BOARD_VNDK_VERSION。使用快照模塊編譯當前vendor模塊,並僅安裝vendor模塊所需的快照模塊。
b.PRODUCT_EXTRA_VNDK_VERSIONS。無論當前vendor模塊有哪些,都安裝VNDK快照模塊。這將安裝PRODUCT_EXTRA_VNDK_VERSIONS中列出的預編譯VNDK快照,而不會在編譯時將其與任何其他模塊相關聯。
BOARD_VNDK_VERSION顯示的是當前vendor模塊需要編譯的VNDK版本。如果BOARD_VNDK_VERSION在/prebuilts/vndk目錄中有可用的 VNDK快照版本,則系統會安裝BOARD_VNDK_VERSION中指明的VNDK快照。如果目錄中的VNDK快照不可用,則會出現編譯錯誤。定義 BOARD_VNDK_VERSION也會啟用要安裝的VNDK模塊。vendor模塊會在編譯時與BOARD_VNDK_VERSION中定義的VNDK快照版本相關聯(此操作不會在system源代碼中編譯當前的VNDK模塊)。從代碼庫中下載完整的源代碼樹時,system源代碼和vendor源代碼均基於相同的Android版本。
PRODUCT_EXTRA_VNDK_VERSIONS列出了要安裝的其他VNDK版本。正常情況下,當前的vendor分區只需一個VNDK快照就足夠了。不過,在某些情況下,您可能需要在一個system.img中提供多個快照。例如,常規系統映像(GSI)具有多個快照,以通過一個system.img支持多個vendor版本。設置PRODUCT_EXTRA_VNDK_VERSIONS后,除了BOARD_VNDK_VERSION中的VNDK版本之外,您還可以安裝VNDK快照模塊。如果 PRODUCT_EXTRA_VNDK_VERSIONS具有特定的版本列表,則編譯系統會在prebuilts/vndk目錄中查找版本列表的預編譯快照。如果編譯系統找到所有列出的快照,便會將這些快照文件安裝到每個out/target/product//system/lib[64]/vndk[-sp]-${VER}中。缺少某些版本會導致出現編譯錯誤。
VNDK模塊將不會在編譯時與vendor模塊相關聯,但在運行時可能會使用該模塊(如果vendor分區中的vendor模塊需要某個已安裝的VNDK版本)。PRODUCT_EXTRA_VNDK_VERSIONS僅在指定了BOARD_VNDK_VERSION的情況下才有效。例如,若要將O MR1 VNDK快照安裝到system.img中,需要運行以下命令:
m -j PRODUCT_EXTRA_VNDK_VERSIONS=27
PLATFORM_VNDK_VERSION在系統源代碼中指定了當前VNDK模塊的VNDK版本。系統會通過以下方式自動設置該值:
a.在版本發布之前,將PLATFORM_VNDK_VERSION設置為PLATFORM_VERSION_CODENAME。
b.在發布時,將PLATFORM_SDK_VERSION復制到PLATFORM_VNDK_VERSION中。
c.發布Android版本后,當前的VNDK庫會被安裝到/system/lib[64]/vndk-$SDK_VER和/system/lib[64]/vndk-sp-$SDK_VER,其中$SDK_VER是存儲在PLATFORM_VNDK_VERSION中的版本。
vendor模塊使用/etc/ld.config.${VER}.txt(其中${VER}是從ro.vndk.version屬性中獲得的)中的命名空間配置來搜索所需的共享庫。命名空間配置中包含帶有版本編號的VNDK目錄,該目錄使用以下語法:
#%VNDK_VER% 在編譯時會被替換為PLATFORM_VNDK_VERSION,這樣一來,system.img便能夠為每個VNDK版本提供多個快照。
/system/${LIB}/vndk-%VNDK_VER%
/system/${LIB}/vndk-sp-%VNDK_VER%
如果將BOARD_VNDK_VERSION設置為current,則PLATFORM_VNDK_VERSION將存儲在ro.vndk.version中;否則BOARD_VNDK_VERSION將存儲在ro.vndk.version中。PLATFORM_VNDK_VERSION在Android版本發布時會被設置為SDK版本;在發布之前,由字母和數字組成的Android代碼名稱會用於PLATFORM_VNDK_VERSION。
下表總結了VNDK版本設置。
供應商版本 | 開發板版本 | SDK版本 | 平台版本 | 版本屬性 | 安裝目錄 |
---|---|---|---|---|---|
當前的VNDK模塊 | current | 之前<CODE_NAME> | <CODE_NAME> | /system/lib[64]/vndk[-sp]-<CODE_NAME> | |
當前的VNDK模塊 | current | 之后 <SDK_ver> | <SDK_ver> | /system/lib[64]/vndk[-sp]-<SDK_ver> | |
預編譯的快照模塊 | <VNDK_ver>(用於快照) | 之前或之后 | <CODE_NAME>或 <SDK_ver> | <VNDK_ver> | /system/lib[64]/vndk[-sp]-<VNDK_ver> |
a.開發板版本(BOARD_VNDK_VERSION):vendor模塊需要編譯的VNDK版本。如果vendor模塊可與當前系統模塊相關聯,則將其設置為 current。
b.平台版本(PLATFORM_VNDK_VERSION):當前系統模塊正在編譯的VNDK版本(僅在BOARD_VNDK_VERSION為當前版本時編譯)。
c.版本屬性(ro.vndk.version):一種屬性,用於指定vendor.img 中的二進制文件和庫需要運行的VNDK版本。該屬性存儲在/vendor/default.prop下的vendor.img中。
參考:https://source.android.google.cn/devices/architecture/vndk