detected problems with app native libraries

detected problems with app native libraries


當targetSdkVersion>=23且使用debug簽名時,在6.0+的Android設備上運行App會輸出以下錯誤Log:

E/linker: /data/app/packagename/lib/arm/libxxx.so: has text relocations
W/System.err: java.lang.UnsatisfiedLinkError: dlopen failed: /data/app/packagename/lib/arm/libxxx.so: has text relocations

注意:當targetSdkVersion>=23,在低於6.0的設備上運行是正常的,至於原因,本文最後會談到。

當targetSdkVersion<23且使用debug簽名時,在6.0+的Android設備上雖然運行正常,但會輸出以下警告Log:

W/linker: “/data/app/packagename/lib/arm/libxxx.so”: has W+E (writable and executable) load segments. This is a security risk sharedlibrarieswith W+E load segments will not be supported in a future Android release. Please fix the library.

W/linker: /data/app/packagename/lib/arm/libxxx.so has text relocations. This is wasting memory and prevents security hardening. Please fix.

App啓動時彈出提醒對話框

當targetSdkVersion<23且使用debug簽名的APK運行在高版本系統上(此處使用原生7.0測試),每次啓動時會彈出一個對話框,其內容如下:

Detectedproblems with appnative libraries (please consult log for detail) : libxxx.so: text relocations

上述各種表象可以發現是同一個問題所致,即libxxx.so: text relocations。

問題原因

“libxxx.so: text relocations”這個問題在Android 6.0官方的更新說明中有提及:

On previous versions of Android, if your app requested the system to load a shared library with text relocations, the system displayed a warning but still allowed the library to be loaded. Beginning in this release, the system rejects this library if yourapp’s target SDK version is 23 or higher. To help you detect if a library failed to load, yourapp should log the dlopen(3) failure, and include the problem description text that the dlerror(3) call returns. To learn more about handling text relocations, see this guide.

這個問題在6.0之前只會產生一個警告,系統還是可以正常加載包含text relocations的共享庫的,但從6.0起,即SDK Version>=23時,系統將會拒絕加載包含text relocations的共享庫,同時輸出錯誤Log,也就是上文中看到的錯誤日誌。

其實引起該問題的最根本原因,是so動態鏈接庫的代碼並非PIC(Position independent code),關於PIC的概念可以參考維基百科的介紹。在Android 6.0更新說明中,也只是簡單提了一句處理方案:

To learn more about handling text relocations, see this guide.

該鏈接是一個解決text relocations (TEXTRELs)的指南。

解決方案

當你在網上搜索此問題時,會發現90%以上的搜索結果都告訴你把targetSdkVersion設爲小於23。誠然,這樣是可以迴避掉上述問題,但只是治標不治本,並非終極方案。

如果App想使用6.0甚至7.0的新特性(前幾天Android O開發者預覽版已發佈,此問題更是刻不容緩),就必須將targetSdkVersion提高至23或以上,所以這個問題必須徹底解決,決不能採用targetSdkVersion設爲小於23的妥協方案。

真正的解決方案就是解決so動態鏈接庫中的text relocations (TEXTRELs)問題。

修復源碼中的text relocations

根據官方推薦的方案鏈接,會打開一個叫做“Hardened/Textrels Guide”的文檔,這篇文檔會指導大家解決text relocations問題。

我們大概看一下該文檔的目錄:

從文檔目錄可知我們可以先找到有text relocations問題的庫,然後可以定位到源代碼,從而修改源代碼,徹底解決問題。

但是這篇指導文檔學習成本較大,還需要安裝特定工具,不太好操作。另外如果沒有so動態庫的源碼,哪怕定位到了也無法修改。

編譯時處理

在使用NDK編譯so時配置Android.mk,增添PIC相關的配置項,這樣編譯出來的so文件將不再有text relocations的問題。具體配置如下:

LOCAL_LDFLAGS += -fPIC

PIC參數用於編譯位置無關代碼,生成可用於共享庫的位置獨立代碼。若不添加-fPIC,則加載so文件的代碼段時,代碼段引用的數據對象需要重定位,重定位會修改代碼段內容,這樣就導致沒使用這個so文件,代碼段的進程在內核中就會生成這個文件的拷貝。

關於Android.mk的語法及配置,可參考官方文檔

實踐建議

雖然有上述兩種解決方案,但在實施中也有一些要注意的地方,下面結合自我實踐來總結一下:

  • 定位哪個so文件有text relocations問題

    除了上述“Hardened/Textrels Guide”文檔中提到的查找方法,在Linux上,可以使用readelf命令行查看。示例如下:

    readelf -a path/to/yourlib.so | grep TEXTREL

    如果上邊的shell命令輸出類似下面的內容,則說明這該so文件不是PIC,是有text relocations問題的。

    0x00000016 (TEXTREL)                    0x0

    TEXTREL表示代碼段重定位表地址,PIC的共享對象不會包含任何代碼段重定位表。因此如果上述命令無任何輸出,則該表示so文件沒有問題。

    一個App中通常會有多個so文件,有自己編寫的,也有第三方的,可以使用上述方法來確定這些so文件是否有text relocations問題。

    另外還可以參考Android changes for NDK developers的“Text Relocations (Enforced since API 23)”章節。

  • 沒有so源碼的情況

    有時我們的App中使用的是第三方so,如地圖定位庫、音頻解碼庫等。一旦這些so文件有問題,而我們沒有源碼,那將無法解決。此種情況下如果想將targetSdkVersion升級到23及以上,只能另尋替代方案或者割捨涉及到的功能。

    我們在實踐中遇到的是一個第三方音頻解碼庫有問題,解決方案是使用原生多媒體API來替代。

    同時也建議在使用第三方so文件時,先做一次檢查,看有無text relocations的問題。

  • 修改編譯參數無效的情況

    例如so中某個功能直接引用了.s的彙編文件(無源文件),而剛好text relocations問題出在.s文件上,此時再使用-fPIC參數編譯是無效的,問題無法解決,只能另尋可替代的實現。

    當targetSdkVersion>=23且使用debug簽名時,在6.0+的Android設備上運行App會輸出以下錯誤Log:

    E/linker: /data/app/packagename/lib/arm/libxxx.so: has text relocations
    W/System.err: java.lang.UnsatisfiedLinkError: dlopen failed: /data/app/packagename/lib/arm/libxxx.so: has text relocations

    注意:當targetSdkVersion>=23,在低於6.0的設備上運行是正常的,至於原因,本文最後會談到。

    當targetSdkVersion<23且使用debug簽名時,在6.0+的Android設備上雖然運行正常,但會輸出以下警告Log:

    W/linker: “/data/app/packagename/lib/arm/libxxx.so”: has W+E (writable and executable) load segments. This is a security risk sharedlibrarieswith W+E load segments will not be supported in a future Android release. Please fix the library.

    W/linker: /data/app/packagename/lib/arm/libxxx.so has text relocations. This is wasting memory and prevents security hardening. Please fix.

    App啓動時彈出提醒對話框

    當targetSdkVersion<23且使用debug簽名的APK運行在高版本系統上(此處使用原生7.0測試),每次啓動時會彈出一個對話框,其內容如下:

    Detectedproblems with appnative libraries (please consult log for detail) : libxxx.so: text relocations

    上述各種表象可以發現是同一個問題所致,即libxxx.so: text relocations。

    問題原因

    “libxxx.so: text relocations”這個問題在Android 6.0官方的更新說明中有提及:

    On previous versions of Android, if your app requested the system to load a shared library with text relocations, the system displayed a warning but still allowed the library to be loaded. Beginning in this release, the system rejects this library if yourapp’s target SDK version is 23 or higher. To help you detect if a library failed to load, yourapp should log the dlopen(3) failure, and include the problem description text that the dlerror(3) call returns. To learn more about handling text relocations, see this guide.

    這個問題在6.0之前只會產生一個警告,系統還是可以正常加載包含text relocations的共享庫的,但從6.0起,即SDK Version>=23時,系統將會拒絕加載包含text relocations的共享庫,同時輸出錯誤Log,也就是上文中看到的錯誤日誌。

    其實引起該問題的最根本原因,是so動態鏈接庫的代碼並非PIC(Position independent code),關於PIC的概念可以參考維基百科的介紹。在Android 6.0更新說明中,也只是簡單提了一句處理方案:

    To learn more about handling text relocations, see this guide.

    該鏈接是一個解決text relocations (TEXTRELs)的指南。

    解決方案

    當你在網上搜索此問題時,會發現90%以上的搜索結果都告訴你把targetSdkVersion設爲小於23。誠然,這樣是可以迴避掉上述問題,但只是治標不治本,並非終極方案。

    如果App想使用6.0甚至7.0的新特性(前幾天Android O開發者預覽版已發佈,此問題更是刻不容緩),就必須將targetSdkVersion提高至23或以上,所以這個問題必須徹底解決,決不能採用targetSdkVersion設爲小於23的妥協方案。

    真正的解決方案就是解決so動態鏈接庫中的text relocations (TEXTRELs)問題。

    修復源碼中的text relocations

    根據官方推薦的方案鏈接,會打開一個叫做“Hardened/Textrels Guide”的文檔,這篇文檔會指導大家解決text relocations問題。

    我們大概看一下該文檔的目錄:

    從文檔目錄可知我們可以先找到有text relocations問題的庫,然後可以定位到源代碼,從而修改源代碼,徹底解決問題。

    但是這篇指導文檔學習成本較大,還需要安裝特定工具,不太好操作。另外如果沒有so動態庫的源碼,哪怕定位到了也無法修改。

    編譯時處理

    在使用NDK編譯so時配置Android.mk,增添PIC相關的配置項,這樣編譯出來的so文件將不再有text relocations的問題。具體配置如下:

    LOCAL_LDFLAGS += -fPIC

    PIC參數用於編譯位置無關代碼,生成可用於共享庫的位置獨立代碼。若不添加-fPIC,則加載so文件的代碼段時,代碼段引用的數據對象需要重定位,重定位會修改代碼段內容,這樣就導致沒使用這個so文件,代碼段的進程在內核中就會生成這個文件的拷貝。

    關於Android.mk的語法及配置,可參考官方文檔

    實踐建議

    雖然有上述兩種解決方案,但在實施中也有一些要注意的地方,下面結合自我實踐來總結一下:

    • 定位哪個so文件有text relocations問題

      除了上述“Hardened/Textrels Guide”文檔中提到的查找方法,在Linux上,可以使用readelf命令行查看。示例如下:

      readelf -a path/to/yourlib.so | grep TEXTREL

      如果上邊的shell命令輸出類似下面的內容,則說明這該so文件不是PIC,是有text relocations問題的。

      0x00000016 (TEXTREL)                    0x0

      TEXTREL表示代碼段重定位表地址,PIC的共享對象不會包含任何代碼段重定位表。因此如果上述命令無任何輸出,則該表示so文件沒有問題。

      一個App中通常會有多個so文件,有自己編寫的,也有第三方的,可以使用上述方法來確定這些so文件是否有text relocations問題。

      另外還可以參考Android changes for NDK developers的“Text Relocations (Enforced since API 23)”章節。

    • 沒有so源碼的情況

      有時我們的App中使用的是第三方so,如地圖定位庫、音頻解碼庫等。一旦這些so文件有問題,而我們沒有源碼,那將無法解決。此種情況下如果想將targetSdkVersion升級到23及以上,只能另尋替代方案或者割捨涉及到的功能。

      我們在實踐中遇到的是一個第三方音頻解碼庫有問題,解決方案是使用原生多媒體API來替代。

      同時也建議在使用第三方so文件時,先做一次檢查,看有無text relocations的問題。

    • 修改編譯參數無效的情況

      例如so中某個功能直接引用了.s的彙編文件(無源文件),而剛好text relocations問題出在.s文件上,此時再使用-fPIC參數編譯是無效的,問題無法解決,只能另尋可替代的實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章