日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最新的ndkr20编译c_史上最优雅的NDK加载pass方案

發(fā)布時(shí)間:2025/4/5 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最新的ndkr20编译c_史上最优雅的NDK加载pass方案 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

關(guān)鍵詞:

不需要編譯llvm

僅依賴NDK,不需要額外的其他環(huán)境

不會(huì)遇到配置引起的符號(hào)NotFound問題

不污染NDK

一、背景介紹

現(xiàn)在代碼保護(hù)技術(shù)很多是在llvm上實(shí)現(xiàn)的,例如 ollvm 和 hikari,作者給出的實(shí)現(xiàn)是將源碼混雜在llvm中,這樣做非常不優(yōu)雅。近來越來越多安全工作者都開始接觸和研究基于llvm的代碼保護(hù),工欲善其事必先利其器,在編譯、運(yùn)行均是本機(jī)的環(huán)境下,不會(huì)出問題,因此本文介紹的是,如何優(yōu)雅地在NDK中加載pass。

安卓開發(fā)者使用混淆技術(shù)來保護(hù)native代碼時(shí),一般有兩種選擇:

第一個(gè)選擇是獲得git上 ollvm 或 hikari 的代碼,編譯后,替換掉NDK中原先的toolchain。

這是最不優(yōu)雅的方式,因?yàn)榫S護(hù)起來很麻煩,因?yàn)樾枰幾g整個(gè)llvm工程,并且對(duì)NDK有侵入性,無法保證修改前和修改后NDK的功能不發(fā)生變化。

第二個(gè)選擇是,編譯llvm工程,替換掉NDK中原先的toolchain,并且在相同環(huán)境下,移植 ollvm 或hikari 為獨(dú)立的plugin,(移植方案我的github里有寫 https://github.com/LeadroyaL/llvm-pass-tutorial )用編譯為插件的形式,動(dòng)態(tài)加載插件。

相比第一個(gè)方案,極大降低維護(hù)的代價(jià),只編譯一個(gè)pass即可,但仍然對(duì)NDK有侵入性。

這兩種方案的共同特點(diǎn)是:都需要編譯整個(gè)llvm項(xiàng)目,初次部署時(shí)要消耗大量的時(shí)間和資源,另外在選擇llvm版本時(shí),也會(huì)糾結(jié)適配性的問題(雖然通常不會(huì)出現(xiàn)適配問題)

筆者曾經(jīng)使用的是第二種方案,經(jīng)過研究,本文提出第三種方案,使用NDK中的環(huán)境編譯pass并加載pass,優(yōu)雅程度上來看,有以下的特點(diǎn):

最最重要的,不需要編譯llvm項(xiàng)目,節(jié)省巨大的時(shí)間和資源消耗;

其次,不修改原先的NDK運(yùn)行環(huán)境,和原生的NDK是最像的,沒有侵入性;

再次,上下文均和NDK完全一致,不需要擔(dān)心符號(hào)問題,不需要額外安裝軟件和環(huán)境,有NDK的環(huán)境就足矣;

本文演示的環(huán)境是:ubuntu18.04(任意linux均可)、ndk-r20(任意NDK版本均可)、cmake(選擇較高版本)

二、使用NDK的環(huán)境編譯一個(gè)pass

眾所周知,編譯Pass時(shí)需要使用llvm的環(huán)境,由于NDK中的llvm環(huán)境是破損的,所以開發(fā)者一般自己編譯一份llvm環(huán)境出來,替換掉NDK中的llvm環(huán)境,包括我本人之前也是這樣處理的,這樣做的原因是NDK中的llvm是破損的,因?yàn)镹DK來自AOSP編譯好的toolchain,而AOSP在制作toolchain的過程中是移除了部分文件的。

上文提到,本文的方案是不需要親自編譯llvm的,因此就需要使用NDK中的破損的llvm環(huán)境來編譯一個(gè)pass。

根據(jù)對(duì) https://android.googlesource.com/toolchain/llvm_android/ 的閱讀和調(diào)試,NDK中的llvm缺失的是一部分binary文件、全部靜態(tài)鏈接庫(kù)文件、全部頭文件,采用的是靜態(tài)連接的方式,它的clang是較為獨(dú)立的文件(它會(huì)依賴libc++,因此成為較為獨(dú)立)。

cmake_minimum_required(VERSION 3.4)

# we need LLVM_HOME in order not automatically set LLVM_DIR

if(NOT DEFINED ENV{LLVM_HOME})

message(FATAL_ERROR "$LLVM_HOME is not defined")

else ()

set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib/cmake/llvm)

endif()

find_package(LLVM REQUIRED CONFIG)

add_definitions(${LLVM_DEFINITIONS})

include_directories(${LLVM_INCLUDE_DIRS})

link_directories(${LLVM_LIBRARY_DIRS})

add_subdirectory(skeleton) # Use your pass name here.

幸運(yùn)的是,NDK中的lib/cmake/llvm還在,里面的cmake文件都是原汁原味的的。

不幸的是,由于AOSP在編譯toolchain時(shí)設(shè)置了 defines['LLVM_LIBDIR_SUFFIX'] = '64' ,導(dǎo)致find_package的路徑應(yīng)該是 lib64/cmake/llvm,需要稍加修改

之后進(jìn)行 mkdir b;cd b;cmake ..

會(huì)報(bào)如下的錯(cuò)誤:

? b git:(master) ? cmake ..

CMake Error at /home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/lib64/cmake/llvm/LLVMExports.cmake:806 (message):

The imported target "LLVMDemangle" references the file

"/home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/lib64/libLLVMDemangle.a"

but this file does not exist. Possible reasons include:

* The file was deleted, renamed, or moved to another location.

* An install or uninstall procedure did not complete successfully.

* The installation package was faulty and contained

"/home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/lib64/cmake/llvm/LLVMExports.cmake"

but not all the files it references.

Call Stack (most recent call first):

/home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/lib64/cmake/llvm/LLVMConfig.cmake:173 (include)

CMakeLists.txt:8 (find_package)

-- Configuring incomplete, errors occurred!

See also "/home/leadroyal/llvm-pass-tutorial/b/CMakeFiles/CMakeOutput.log".

因?yàn)镹DK不含有.a文件,而cmake會(huì)檢查這些文件,用于靜態(tài)連接,被認(rèn)為初始化失敗,出錯(cuò)。

看源碼對(duì)應(yīng)的位置:

# Loop over all imported files and verify that they actually exist

foreach(target ${_IMPORT_CHECK_TARGETS} )

foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} )

if(NOT EXISTS "${file}" )

message(FATAL_ERROR "The imported target \"${target}\" references the file

\"${file}\"

but this file does not exist. Possible reasons include:

* The file was deleted, renamed, or moved to another location.

* An install or uninstall procedure did not complete successfully.

* The installation package was faulty and contained

\"${CMAKE_CURRENT_LIST_FILE}\"

but not all the files it references.

")

endif()

endforeach()

unset(_IMPORT_CHECK_FILES_FOR_${target})

endforeach()

在文件不存在時(shí),報(bào) message(FATAL_ERROR xxxxxx),從而中斷編譯,但我們本來就是編譯動(dòng)態(tài)鏈接庫(kù)的,不需要.a文件,所以這里做一個(gè)patch,降低log_level,使用WARNING等級(jí)。

- message(FATAL_ERROR "The imported target \"${target}\" references the file

+ message(WARNING "The imported target \"${target}\" references the file

接下來面對(duì)第二個(gè)問題,之前提到過,NDK中缺失我們需要的頭文件,它們本該出現(xiàn)在include/llvm中,因此這句話失效了

include_directories(${LLVM_INCLUDE_DIRS})

但我們又不能隨便找一堆頭文件過來,版本之間可能有變更,萬一用到一些配置不一樣的頭文件,就會(huì)出現(xiàn)非預(yù)期(例如經(jīng)常出錯(cuò)的LLVM_ENABLE_ABI_BREAKING_CHECKS配置)

此時(shí)的思路是,找到NDK中l(wèi)lvm生成時(shí)的那份commit,從中獲取include文件,有兩個(gè)方案

第一個(gè)方案是找到源碼并使用cmake幫我們提取一遍。

第二個(gè)方案是直接使用aosp提供的prebuilt文件,顯然為了方便我們選擇后者。

toolchain 在生成時(shí)會(huì)有一份描述版本信息的文件,在ndk生成時(shí)也被拷貝過來了

? linux-x86_64 cat /home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/AndroidVersion.txt

8.0.7

based on r346389c

【AOSP相關(guān)訪問google的前提條件你懂的】

即可獲得到這個(gè)目錄的壓縮包。

如果可以接受NDK被污染(我使用的是這個(gè)方案),可以將它放到NDK的toolchain中,這樣就可以繼續(xù)使用 ${LLVM_INCLUDE_DIRS} 這個(gè)變量;

如果不能接受NDK被污染,就隨便放個(gè)目錄,使用 include_directories(/path/to/clang-r346389c/include)

比如放在NDK里的include里,是這個(gè)樣子(c++目錄本來就有)

? include lsa

total 5.4M

drwxr-xr-x 8 leadroyal leadroyal 4.0K Oct 21 02:13 .

drwxr-xr-x 15 leadroyal leadroyal 4.0K Oct 20 23:16 ..

drwxr-xr-x 4 leadroyal leadroyal 4.0K Oct 21 02:12 c++

drwxr-xr-x 33 leadroyal leadroyal 4.0K Oct 21 02:12 llvm

drwxr-xr-x 3 leadroyal leadroyal 4.0K Oct 21 02:12 llvm-c

然后有幾率遇到C++版本的問題,llvm10以上需要添加

set(CMAKE_CXX_STANDARD 14)

在這在情況下使用的CMakeLists.txt最終是:

cmake_minimum_required(VERSION 3.4)

if(NOT DEFINED ENV{LLVM_HOME})

message(FATAL_ERROR "$LLVM_HOME is not defined")

endif()

if(NOT DEFINED ENV{LLVM_DIR})

set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib64/cmake/llvm)

endif()

find_package(LLVM REQUIRED CONFIG)

add_definitions(${LLVM_DEFINITIONS})

include_directories(${LLVM_INCLUDE_DIRS})

set(CMAKE_CXX_STANDARD 14)

add_subdirectory(skeleton) # Use your pass name here.

修復(fù)完include問題后,就可以舒舒服服地使用cmake來生成demo了,如下:

export LLVM_HOME=/home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64

? b git:(master) ? cmake ..

-- The C compiler identification is GNU 7.4.0

-- The CXX compiler identification is GNU 7.4.0

-- Check for working C compiler: /usr/bin/cc

-- Check for working C compiler: /usr/bin/cc -- works

-- Detecting C compiler ABI info

-- Detecting C compiler ABI info - done

-- Detecting C compile features

-- Detecting C compile features - done

-- Check for working CXX compiler: /usr/bin/c++

-- Check for working CXX compiler: /usr/bin/c++ -- works

-- Detecting CXX compiler ABI info

-- Detecting CXX compiler ABI info - done

-- Detecting CXX compile features

-- Detecting CXX compile features - done

-- Configuring done

-- Generating done

-- Build files have been written to: /home/leadroyal/llvm-pass-tutorial/b

? b git:(master) ? cmake --build .

Scanning dependencies of target SkeletonPass

[ 50%] Building CXX object skeleton/CMakeFiles/SkeletonPass.dir/Skeleton.cpp.o

[100%] Linking CXX shared module libSkeletonPass.so

[100%] Built target SkeletonPass

三、使用NDK的環(huán)境加載一個(gè)pass

編譯部分完成了,接下來是加載部分,我們隨便找一個(gè)android native項(xiàng)目,修改build.gradle中的flag

externalNativeBuild {

cmake {

cppFlags "-Xclang -load -Xclang /home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so"

}

}

gradle build 命令后,可能會(huì)如下報(bào)錯(cuò)(當(dāng)編譯pass時(shí)使用了GNU系列的c++時(shí)候會(huì)遇到,常見于ubuntu,因?yàn)镹DK使用的是llvm系列的c++)

如果出現(xiàn)如下報(bào)錯(cuò)的話,解決方案如下,如果沒有報(bào)錯(cuò),請(qǐng)?zhí)^這部分

通常被搜索的關(guān)鍵詞是:_ZNK4llvm12FunctionPass17createPrinterPassERNS_11raw_ostreamERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE

========GNU使用兼容libc++的方案(沒遇到可以跳過) =======

./gradlew build

error: unable to load plugin '/home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so': '/home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so: undefined symbol: _ZNK4llvm12FunctionPass17createPrinterPassERNS_11raw_ostreamERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE'

很奇怪,提醒這個(gè)符號(hào)找不到,但是我們編譯時(shí)能找到、連接時(shí)找不到,就很奇怪。

demangle一下:

c++filt _ZNK4llvm12FunctionPass17createPrinterPassERNS_11raw_ostreamERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE

llvm::FunctionPass::createPrinterPass(llvm::raw_ostream&, std::__cxx11::basic_string, std::allocator > const&) const

去NDK的相關(guān)目錄下grep,發(fā)現(xiàn)了該符號(hào):

? lib64 pwd

/home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/lib64

? lib64 strings * | grep _ZNK4llvm12FunctionPass17createPrinterPass

strings: Warning: 'clang' is a directory

strings: Warning: 'cmake' is a directory

_ZNK4llvm12FunctionPass17createPrinterPassERNS_11raw_ostreamERKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE

demangle一下:

c++filt _ZNK4llvm12FunctionPass17createPrinterPassERNS_11raw_ostreamERKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE

llvm::FunctionPass::createPrinterPass(llvm::raw_ostream&, std::__1::basic_string, std::__1::allocator > const&) const

對(duì)比一下二者,注意一個(gè)細(xì)節(jié),參數(shù)命名空間不一致:

NDK里的叫std::__1::basic_string,我們編出來的叫std::__cxx11::basic_string

NDK里的叫std::__1::char_traits,我們編出來的叫std::char_traits

如果是老司機(jī)的話,一眼就知道它們使用了不同版本的c++,最初的源碼是一致的,解決起來不難。

用__cxx11的叫l(wèi)ibc++,用__1的叫l(wèi)ibstdc++。

解決方案是在連接時(shí)使用libc++,set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++"),但由于ubuntu裝的一般是gcc系列,而gcc系列是沒有l(wèi)ibc++的,編譯會(huì)crash如下:

Using built-in specs.

COLLECT_GCC=/usr/bin/c++

OFFLOAD_TARGET_NAMES=nvptx-none

OFFLOAD_TARGET_DEFAULT=1

c++: error: unrecognized command line option ‘-stdlib=libc++’

gcc沒有l(wèi)ibc++,只有l(wèi)lvm系列擁有l(wèi)ibc++,所以需要將編譯器切換到clang。

重申我們之前的原則:不需要安裝額外的軟件,恰好NDK提供了一個(gè)clang給我們,為了方便我就用它提供的了(畢竟安裝一個(gè)clang也挺麻煩的)

我把libc++的頭文件放在 /home/leadroyal/Android/Sdk/r346389c/include/ 下

放好后對(duì)它進(jìn)行include,在這在情況下使用的CMakeLists.txt最終是:

cmake_minimum_required(VERSION 3.4)

set(CMAKE_C_COMPILER /home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/clang)

set(CMAKE_CXX_COMPILER /home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++)

if(NOT DEFINED ENV{LLVM_HOME})

message(FATAL_ERROR "$LLVM_HOME is not defined")

endif()

if(NOT DEFINED ENV{LLVM_DIR})

set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib64/cmake/llvm)

endif()

find_package(LLVM REQUIRED CONFIG)

add_definitions(${LLVM_DEFINITIONS})

include_directories(${LLVM_INCLUDE_DIRS})

include_directories(/home/leadroyal/Android/Sdk/r346389c/include/c++/v1)

set(CMAKE_CXX_STANDARD 14)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")

add_subdirectory(skeleton) # Use your pass name here.

我們使用gcc和clang編譯兩份pass出來,對(duì)比一下前后的區(qū)別:

使用GCC編譯出來的文件

? b git:(master) ? ldd skeleton/libSkeletonPass.so

linux-vdso.so.1 (0x00007ffc3c3d5000)

libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff114c76000)

libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff114a5e000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff11466d000)

libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff1142cf000)

/lib64/ld-linux-x86-64.so.2 (0x00007ff115205000)

使用clang編譯出來的文件

? b git:(master) ? ldd skeleton/libSkeletonPass.so

linux-vdso.so.1 (0x00007ffc369e2000)

libc++.so.1 => not found

libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f002042c000)

libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0020214000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f001fe23000)

/lib64/ld-linux-x86-64.so.2 (0x00007f00209d3000)

雖然后者提醒libc++.so.1找不到,感覺很詫異,于是去查ndk clang的依賴

? bin ldd /home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/clang

linux-vdso.so.1 (0x00007ffc99bc7000)

libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3bb9d24000)

libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f3bb9b07000)

librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f3bb98ff000)

libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3bb96fb000)

libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f3bb935d000)

libc++.so.1 => /home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/../lib64/libc++.so.1 (0x00007f3bba07e000)

libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f3bb9145000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3bb8d54000)

/lib64/ld-linux-x86-64.so.2 (0x00007f3bb9f43000)

發(fā)現(xiàn)在NDK里確實(shí)存在libc++.so.1環(huán)境,問題解決,我們回歸主題,最后一步,使用NDK加載它!

========GNU使用兼容libc++的方案 end =======

我們先用簡(jiǎn)單的c文件驗(yàn)證我們的pass,沒有任何問題

? /tmp cat test.c

#include

int main(){

printf("HelloWorld\n");

return 0;

}

? /tmp /home/leadroyal/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -Xclang -load -Xclang /home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so test.c

I saw a function called main!

? /tmp ./a.out

HelloWorld

最后一步,見證奇跡的時(shí)刻!

? MyApplication ./gradlew clean build

............

> Task :app:externalNativeBuildDebug

Build native-lib_armeabi-v7a

ninja: Entering directory `/home/leadroyal/AndroidStudioProjects/MyApplication/app/.cxx/cmake/debug/armeabi-v7a'

[1/2] Building CXX object CMakeFiles/native-lib.dir/native-lib.cpp.o

I saw a function called Java_com_example_myapplication_MainActivity_stringFromJNI!

I saw a function called _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2EPKc!

I saw a function called _ZN7_JNIEnv12NewStringUTFEPKc!

I saw a function called _ZNKSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5c_strEv!

I saw a function called _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev!

淚流滿面!我們終于成功編譯并且加載了這個(gè)Pass!

四、當(dāng)我們來到macOS上

同Linux一樣,先修復(fù)cmake文件,再下載include/llvm和incude/llvm-c,因?yàn)閙acOS默認(rèn)就是clang了,所以不會(huì)存在libstdc++和libc++沖突的問題,編譯過程全程沒有任何障礙。

但是在加載時(shí)卻遇到了如下的錯(cuò)誤,也是在網(wǎng)上經(jīng)常被貼出來問問題的報(bào)錯(cuò)

? /tmp $ANDROID_NDK/20.0.5594570/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang -Xclang -load -Xclang /home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so test.c

error: unable to load plugin '/home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so':

'dlopen(/home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so, 9): Symbol not found:

__ZN4llvm12FunctionPass17assignPassManagerERNS_7PMStackENS_15PassManagerTypeE

Referenced from: /home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so

Expected in: flat namespace

in /home/leadroyal/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so'

demangle一下

c++filt __ZN4llvm12FunctionPass17assignPassManagerERNS_7PMStackENS_15PassManagerTypeE

llvm::FunctionPass::assignPassManager(llvm::PMStack&, llvm::PassManagerType)

這個(gè)符號(hào)是llvm中導(dǎo)出的符號(hào),供開發(fā)者調(diào)用,libSkeletonPass.so需要該符號(hào),但是clang的進(jìn)程空間里沒有這個(gè)符號(hào)。

經(jīng)過仔細(xì)對(duì)照,發(fā)現(xiàn)不僅僅缺失這一個(gè)符號(hào),缺失的是一大堆相關(guān)的符號(hào),而且都是較為基礎(chǔ)的符號(hào),只是最先被尋找的是這個(gè)就停下來了。

【先劇透一下,這個(gè)符號(hào)缺失是apple基礎(chǔ)工具的bug,但是google沒有發(fā)現(xiàn)這個(gè)bug,已報(bào)告https://issuetracker.google.com/issues/143160164】

這時(shí)有另一個(gè)線索:我們自己編譯出來的pass是可以正常加載pass的,一定是AOSP動(dòng)了手腳,這里省去大量的diff時(shí)間,直接說結(jié)果。

記作X:使用llvm默認(rèn)配置(與Android無關(guān))編譯出來的clang,可以找到符號(hào)

記作Y:使用AOSP得到的stage2-install/bin/clang,可以找到符號(hào)

記作Z:使用AOSP得到的toolchain中的clang,無法找到符號(hào)

X/Y 可以說明, https://android.googlesource.com/toolchain/llvm_android/ 中對(duì)llvm的編譯配置,是不影響符號(hào)的

Y/Z 可以說明,strip前和strip后會(huì)導(dǎo)致符號(hào)缺失。在ubuntu上符號(hào)仍然被保留,在macOS上符號(hào)會(huì)消失。

for bin_filename in os.listdir(bin_dir):

binary = os.path.join(bin_dir, bin_filename)

if os.path.isfile(binary):

if bin_filename not in necessary_bin_files:

remove(binary)

elif strip and bin_filename not in script_bins:

check_call(['strip', binary])

之后我將X進(jìn)行/usr/bin/strip,發(fā)現(xiàn)仍然可以加載pass,這時(shí)就開始犯暈,開始缺乏思路。

于是出現(xiàn)了另一個(gè)可能引發(fā)問題的原因:我編譯X、strip-X都是在CommandLineTools 10.15上完成的,但編譯Y、strip-Y是在CommandLineTools 10.13上完成的,二者的strip不完全一致!

經(jīng)過最后一個(gè)實(shí)驗(yàn),發(fā)現(xiàn)低版本的/usr/bin/strip會(huì)錯(cuò)誤地移除掉很多符號(hào),導(dǎo)致加載失敗,日志如下,我分別用10.13/10.14/10.15的strip去處理stage2-install/bin/clang文件,發(fā)現(xiàn)10.13/14處理出來的文件是錯(cuò)誤的。

至此,真相大白,失敗的原因是:AOSP在編譯NDK時(shí)觸發(fā)了macOS自帶的strip的bug。

最后的掙扎:NDK中存在一個(gè)完備的、擁有符號(hào)的文件 LLVM.dylib 中的,如果我們讓libSkeleton.so依賴它,從LLVM.dylib中獲取符號(hào)會(huì)怎樣?

最終結(jié)果是,關(guān)鍵變量PassManager在NDK-clang中是沒有符號(hào)的,雖然在LLVM.dylib中可以找到,但二者已經(jīng)完全不是同一個(gè)instance了。

因此,macOS宣告失敗,等將來AOSP把這個(gè)bug修掉,就可以復(fù)用史上最優(yōu)雅的方法了。

五、當(dāng)我們來到Windows

對(duì)不起,能力有限告辭。。。

六、其他

不想看到的事情:

根據(jù)這次commit,開發(fā)者建議砍掉toolchain里的.cmake文件,因?yàn)樽髡咭呀?jīng)砍掉.a文件了,防止.cmake加載失敗引起的誤會(huì)。我也是弄完這一系列才看到這條commit,于是想盡自己的綿薄之力回滾一下,希望能成功吧。

以及,開發(fā)者建議砍掉ndk里的.cmake文件,體現(xiàn)在這次commit里

反正ndk-r21肯定是沒有cmake了,到時(shí)候就從toolchain里下載回來吧。

本文介紹了一種非常優(yōu)雅的NDK加載Pass方案,目前從未聽說國(guó)內(nèi)外有人使用該方案,感覺非常有意義,在此分享出來,希望更多人可以用到它、共同推動(dòng)安全行業(yè)的發(fā)展,完結(jié)撒花~

總結(jié)

以上是生活随笔為你收集整理的最新的ndkr20编译c_史上最优雅的NDK加载pass方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。