我是去年10月左右開始研究Ollvm,現將一部分心得分享出來
基本概念
LLVM Pass按加載方式分類:
-
靜態Pass
,即將Pass邏輯編譯到clang/opt
中,通過給clang/opt
傳遞適當參數觸發Pass。如果目標語言和編譯環境不支持命令行方式指定Pass則可嘗試此方式。目前大部分開源Ollvm項目都是此種方式,且一定程度修改了LLVM源碼部分 -
動態Pass
,即將Pass邏輯編譯為獨立動態庫,通過給clang/opt
傳遞適當參數觸發Pass。如果目標語言和編譯環境不方便使用自定義clang/opt
但支持命令行方式指定動態Pass則可嘗試此方式,此方式在Pass開發編譯調試時優勢明顯。
常見問題
-
動態Pass
和LLVM
版本要匹配嗎?是的,至少大版本要匹配否則大概率崩 - 開發
動態Pass
給XCode
使用以實現Ollvm
混淆的思路可行嗎?可行 - 直接針對
XCode
的Apple Clang
開發動態Pass
可行嗎?可行
XCode使用Pass的方式(以下方式筆者均驗證過可行)
-
靜態Pass
方式1,編譯為XCode Toolchain
,也是目前大部分開源Ollvm
項目所采用的方式。不過編譯時間久一些且Toolchain
占用的存儲空間也大一些。 -
靜態Pass
方式2,直接使用clang
,在Xcode
中指定CC
變量為內嵌Pass
的clang
。如果isysroot
不兼容可能需要在Other C Flags
中覆蓋 -
動態Pass
方式3,在Xcode中指定CC
變量為開源clang
(如brew install llvm@15
),且指定Other C Flags
指定-fpass-plugin
為對應Pass
路徑 -
動態Pass
方式4,在Xcode
中指定CC
變量為編譯腳本,腳本邏輯為"先用clang -emit-llvm
參數生成bitcode
,然后運行opt
執行Pass
,最后用clang -c
生成原本要生成的obj文件"。此種方式可以直接使用Xcode
自帶的Apple clang
,能比較好的兼容arm64e
架構 -
動態Pass
方式5,直接針對Xcode自帶的Apple clang
開發動態Pass
,在Xcode中指定Other C Flags
指定-fpass-plugin
為Pass路徑。此種方式復雜度較高,只適合精通llvm編譯的開發者。此種方式可以直接使用Xcode
自帶的Apple clang
,能比較好的兼容arm64e
架構
針對AppleClang
開發動態Pass
現在來看看最難的部分,如果直接使用AppleClang
對應版本的開源LLVM開發Pass給AppleClang
使用是會崩潰的,因為AppleClang
相對于開源LLVM魔改了很多地方。為了讓AppleClang
能運行起來一個最簡單的Pass
你至少需要如下hacker代碼:
class Ollvm : public PassInfoMixin<Ollvm> {
public:
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) {
M.dump();
#ifdef APPLE_CLANG
static PreservedAnalyses (*PreservedAnalyses__all)() = 0;
static PreservedAnalyses (*PreservedAnalyses__none)() = 0;
if (PreservedAnalyses__all == 0) {
const char* image = _dyld_get_image_name(0);
PreservedAnalyses__all = (__typeof(PreservedAnalyses__all)) DobbySymbolResolver(image,
"__ZN4llvm6detail9PassModelINS_6ModuleEN12_GLOBAL__N_114NoOpModulePassENS_17PreservedAnalysesENS_15AnalysisManagerIS2_JEEEJEE3runERS2_RS7_");
PreservedAnalyses__none = (__typeof(PreservedAnalyses__none))DobbySymbolResolver(image,
"__ZN4llvm6detail9PassModelINS_6ModuleENS_25InvalidateAllAnalysesPassENS_17PreservedAnalysesENS_15AnalysisManagerIS2_JEEEJEE3runERS2_RS6_");
}
return (opc > 0) ? PreservedAnalyses__all() : PreservedAnalyses__none();
#else
return (opc > 0) ? PreservedAnalyses::all() : PreservedAnalyses::none();
#endif
};
static bool isRequired() { return true; }
};
以上操作僅僅是讓你“入門”,而下一步就是處理AppleClang
魔改造成的沖突,包括但不限于拆分靜態庫/自行實現沖突代碼,也是個復雜且麻煩的工程了,有興趣的可以接著研究下去