欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

zipflinger導(dǎo)致的UnsatisfiedLinkError的實例分析

zipflinger導(dǎo)致的UnsatisfiedLinkError的實例分析,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

創(chuàng)新互聯(lián)成立與2013年,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都網(wǎng)站設(shè)計、做網(wǎng)站、成都外貿(mào)網(wǎng)站建設(shè)公司網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元赤壁做網(wǎng)站,已為上家服務(wù),為赤壁各地企業(yè)和個人服務(wù),聯(lián)系電話:18982081108

筆者在安卓源碼環(huán)境下做一些開發(fā)工作。幾日前碰到了一個奇怪的問題,預(yù)裝的APP突然報了一個UnsatisfiedLinkError的崩潰。查了一下最近的改動記錄,只是將AGP(Androidd gradle plugin) 從3.6.1版本升級到了4.1.0版本。

源碼環(huán)境為Android 9.0,app預(yù)裝在 /system/priv-app下,且app中包含有so。為了簡化問題,寫了一個極簡的 Demo app,將這個app預(yù)裝在 /system/priv-app下,使用AGP 4.0及其以下的版本都正常,一旦使用AGP 4.1及其以上的版本打出來的apk包,就會報 UnsatisfiedLinkError的錯誤。

app預(yù)裝的配置

include $(CLEAR_VARS)
LOCAL_MODULE := MyTestApp.apk
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_PRIVILEGED_MODULE := true
LOCAL_DEX_PREOPT := nostripping
include $(BUILD_PREBUILT)

報錯信息

E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: library "/system/priv-app/MyTestApp/MyTestApp.apk!/lib/armeabi-v7a/libmytest.so" not found
E AndroidRuntime:       at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
E AndroidRuntime:       at java.lang.System.loadLibrary(System.java:1669)
...

問題原因很明顯是由于升級了AGP到4.1導(dǎo)致的,不過查看了一下官方的changelog,沒發(fā)現(xiàn)有什么明顯的跟這個問題相關(guān)的改動。 分析log發(fā)現(xiàn)是so加載失敗了,可是把MyTestApp.apk pull出來解壓,發(fā)現(xiàn)so文件是存在的,路徑也沒問題,那問題出現(xiàn)在哪呢,這個時候只能先分析一下系統(tǒng)加載so的流程,看看問題出在哪了。

So加載的流程網(wǎng)上文章很多,就不逐一分析了,這里列出調(diào)用棧

ojluni/src/main/java/java/lang/System.java  --> System.loadLibrary
ojluni/src/main/java/java/lang/Runtime.java  --> Runtime.loadLibrary0 -> nativeLoad
ojluni/src/main/native/Runtime.c  --> Runtime_nativeLoad
art/openjdkjvm/OpenjdkJvm.cc  --> JVM_NativeLoad
art/runtime/java_vm_ext.cc --> JavaVMExt::LoadNativeLibrary
system/core/libnativeloader/native_loader.cpp --> OpenNativeLibrary
bionic/libdl/libdl.cpp --> android_dlopen_ext
bionic/linker/dlfcn.cpp --> __loader_android_dlopen_ext
bionic/linker/dlfcn.cpp --> dlopen_ext
bionic/linker/linker.cpp --> do_dlopen
bionic/linker/linker.cpp --> find_library
bionic/linker/linker.cpp --> find_libraries
bionic/linker/linker.cpp --> find_library_internal
bionic/linker/linker.cpp --> load_library
bionic/linker/linker.cpp --> open_library
bionic/linker/linker.cpp --> open_library_in_zipfile

經(jīng)過大量的debug,最終發(fā)現(xiàn)系統(tǒng)會使用 "!/" 這個分隔符來分隔路徑 /system/priv-app/MyTestApp/MyTestApp.apk!/lib/armeabi-v7a/libmytest.so,然后在 /system/priv-app/MyTestApp/MyTestApp.apk這個apk文件(apk其實就是一個zip文件)中搜索name為 lib/armeabi-v7a/libmytest.so的entry。這部分邏輯在 bionic/linker/linker.cpp --> open_library_in_zipfile中。 導(dǎo)致加載失敗的是以下條件 entry.offset % PAGE_SIZE != 0

if (entry.method != kCompressStored || (entry.offset % PAGE_SIZE) != 0) {
    close(fd);
    return -1;
}

由此我們可以推測出問題應(yīng)該發(fā)生在zipalign相關(guān)的事情上。根據(jù)官方文檔的描述,zipalign的目的是確保所有未壓縮數(shù)據(jù)的開頭均相對于文件開頭部分執(zhí)行特定的對齊。具體來說,它會使 APK 中的所有未壓縮數(shù)據(jù)(例如圖片或原始文件)在 4 字節(jié)邊界上對齊。這樣一來,即可使用 mmap() 直接訪問所有部分,即使其中包含具有對齊限制的二進制數(shù)據(jù)也沒關(guān)系。這樣做的好處是可以減少運行應(yīng)用時消耗的 RAM 容量。

很顯然/system/priv-app/MyTestApp/MyTestApp.apk這個apk的對齊處理應(yīng)該是有問題的,我們來做一下驗證。將這個apk pull出來,執(zhí)行以下命令,發(fā)現(xiàn)確實有問題。

xxx@debian:~/workspace$ zipalign -c -v -p 4 MyTestApp.apk 
Verifying alignment of out.apk (4)...
    3964 lib/armeabi-v7a/libmytest.so (BAD - 3964)
  108038 META-INF/CERT.SF (OK - compressed)
  108568 AndroidManifest.xml (OK - compressed)
  109583 META-INF/CERT.RSA (OK - compressed)
  110676 res/layout/activity_main.xml (OK - compressed)
  111012 res/mipmap-xhdpi-v4/ic_launcher.png (OK)
  115704 resources.arsc (OK)
  116722 META-INF/MANIFEST.MF (OK - compressed)
  117196 classes.dex (OK)
Verification FAILED

那么gradle打包生成的apk是否有問題呢,我們按照相同的方法驗證一下源文件,發(fā)現(xiàn)是沒問題的!那么問題就很明顯了,安卓系統(tǒng)在編譯的時候一定是對這個apk做了一些處理,導(dǎo)致出現(xiàn)了問題。于是我們需要來看一下編譯相關(guān)的處理。

Android系統(tǒng)對應(yīng) BUILT_PREBUILT 的腳本在 build/core/prebuilt_internal.mk中,其中

ifeq (true, $(LOCAL_UNCOMPRESS_DEX))
        $(uncompress-dexs)
endif  # LOCAL_UNCOMPRESS_DEX

也就是說如果LOCAL_UNCOMPRESS_DEX 為true,那么會對apk進行一個 uncompress-dexs 的處理,uncompress-dexs定義在 build/core/definitions.mk

# Uncompress dex files embedded in an apk.
#
define uncompress-dexs
$(hide) if (zipinfo $@ '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \
  tmpdir=$@.tmpdir; \
  rm -rf $$tmpdir && mkdir $$tmpdir; \
  unzip -q $@ '*.dex' -d $$tmpdir && \
  zip -qd $@ '*.dex' && \
  ( cd $$tmpdir && find . -type f | sort | zip -qD -X -0 ../$(notdir $@) -@ ) && \
  rm -rf $$tmpdir; \
  fi
endef

分析發(fā)現(xiàn),這個處理就是判斷apk中的 dex 后綴的文件是否是壓縮存儲的,如果不是壓縮存儲的那么不做任何操作,如果是壓縮存儲的,那么將其變?yōu)椴粔嚎s存儲的方式。(zip文件中的文件項目的存儲方式分為不壓縮存儲(stored)和壓縮存儲(deflated))

繼續(xù)分析發(fā)現(xiàn)經(jīng)過uncompress-dexs之后,編譯系統(tǒng)對這個apk還進行了一步 align-package的操作,定義還是在 build/core/definitions.mk

# Align STORED entries of a package on 4-byte boundaries to make them easier to mmap.
#
define align-package
$(hide) if ! $(ZIPALIGN) -c $(ZIPALIGN_PAGE_ALIGN_FLAGS) 4 $@ >/dev/null ; then \
  mv $@ $@.unaligned; \
  $(ZIPALIGN) \
    -f \
    $(ZIPALIGN_PAGE_ALIGN_FLAGS) \
    4 \
    $@.unaligned $@.aligned; \
  mv $@.aligned $@; \
  fi
endef

那現(xiàn)在問題比較明顯了,就是對于AGP4.1打出來的apk包,經(jīng)過uncompress-dexs操作后,再重新執(zhí)行zipalign,生成的apk文件的對齊是有問題的。為了方便debug,將uncompress-dexs對應(yīng)的操作寫了一個shell腳本 uncompress-dexs

那么為什么系統(tǒng)會對apk做這樣的處理呢,LOCAL_UNCOMPRESS_DEX 這個參數(shù)我們似乎也沒有定義呀,查看LOCAL_UNCOMPRESS_DEX這個參數(shù)的定義和用法,在 build/core/dex_preopt_odex_install.mk

# We explicitly uncompress APKs of privileged apps, and used by
# privileged apps
LOCAL_UNCOMPRESS_DEX := false
ifneq (true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS))
ifeq (true,$(LOCAL_PRIVILEGED_MODULE))
  LOCAL_UNCOMPRESS_DEX := true
else
  ifneq (,$(filter $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES), $(LOCAL_MODULE)))
    LOCAL_UNCOMPRESS_DEX := true
  endif  # PRODUCT_LOADED_BY_PRIVILEGED_MODULES
endif  # LOCAL_PRIVILEGED_MODULE
endif  # DONT_UNCOMPRESS_PRIV_APPS_DEXS

分析發(fā)現(xiàn),如果DONT_UNCOMPRESS_PRIV_APPS_DEXS為默認值false,那么系統(tǒng)會對privileged app,也就是 /system/priv-app/下的app執(zhí)行uncompress-dexs操作。

那么現(xiàn)在就需要去調(diào)研AGP4.1到底有什么改動,導(dǎo)致uncompress-dexs這個操作會對zipalign造成影響。

經(jīng)過一些搜索最終發(fā)現(xiàn),google從AGP 3.6版本開始加入了一個新的打包工具zipflinger,不過只在構(gòu)建調(diào)試版本的時候生效,但是從AGP4.1開始,構(gòu)建release版本默認也會啟用zipflinger。通過在gradle.properties中加入以下屬性可禁用zipflinger

android.useNewApkCreator=false

我們使用AGP4.1,加入這個配置打包測試,發(fā)現(xiàn)問題果然解決了。

雖然問題解決了,不過難道我們就不能在系統(tǒng)集成的時候,集成啟用了zipflinger工具打包的apk嗎?google既然推出了這個工具想必是做了充分的測試的吧。由于我們目前使用的是Android 9.0的源碼,那我們看看最新的master上的這部分代碼是如何處理的,查看代碼果然發(fā)現(xiàn)了一些改動。在最新的aosp源碼中,uncompress-dexs的實現(xiàn)如下 鏈接

# Uncompress dex files embedded in an apk.
#
define uncompress-dexs
  if (zipinfo $@ '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \
    $(ZIP2ZIP) -i $@ -o $@.tmp -0 "classes*.dex" && \
    mv -f $@.tmp $@ ; \
  fi
endef

我們發(fā)現(xiàn)google使用了一個新的工具zip2zip來處理apk中dex文件的解壓縮。我們找到這個工具的源碼,其實就是一個單獨的文件,使用go語言編寫的 zip2zip.go,我們將源碼下載下來,使用 go build 命令編譯成可執(zhí)行文件,這里我編譯了一個 zip2zip,我們來使用這個工具對啟用zipflinger生成的apk進行測試,發(fā)現(xiàn)果然沒問題

zip2zip -i MyTestApp.apk -o out.apk -0 classes.dex
zipalign -f -p 4 out.apk MyTestApp.apk
zipalign -c -v -p 4 MyTest.apk

至此這個問題終于算是解決了,總結(jié)來看就是一個舊版本的AOSP不兼容新的zipflinger打包工具的問題。根據(jù)分析過程解決辦法有如下幾個:

  • 解決辦法1

    在BoardConfig.mk文件中聲明

    	DONT_UNCOMPRESS_PRIV_APPS_DEXS := true

    (不推薦這種方式,只有在分區(qū)空間不足的情況下,才會聲明這個屬性,以犧牲一點dex的加載速度來換取空間)

  • 解決辦法2

    預(yù)裝APP的時候設(shè)置不為privileged app

    	LOCAL_PRIVILEGED_MODULE := false

    這種處理方式不通用,有些app必須是privileged

  • 解決辦法3

    修改 build/core/definitions.mk 中的 uncompress-dexs方法,使用新的zip2zip方案來適配

    這種方法可行,不過需要修改的地方有點多,需要更新很多AOSP的新代碼過去,比較麻煩

  • 解決辦法4

    回退AGP版本到4.0或其以下 (顯然這不是一個好辦法)

  • 解決辦法5 (推薦方法)

    在app工程的gradle.properties中聲明禁用zipflinger

    	android.useNewApkCreator=false

看完上述內(nèi)容,你們掌握zipflinger導(dǎo)致的UnsatisfiedLinkError的實例分析的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

網(wǎng)站標(biāo)題:zipflinger導(dǎo)致的UnsatisfiedLinkError的實例分析
新聞來源:http://chinadenli.net/article6/gedoog.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序、做網(wǎng)站企業(yè)網(wǎng)站制作、網(wǎng)站內(nèi)鏈域名注冊、用戶體驗

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作