找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 87|回复: 0

OLLVM学习之四 —— 使用CMake构建LLVM Pass

[复制链接]
发表于 2024-9-12 15:36:38 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 lichao 于 2024-9-12 22:30 编辑

前言

  LLVM Pass是LLVM提供的用于优化/分析/处理IR的组件, 第三方可以自由开发Pass从而干涉编译过程, 实现代码优化/静态分析/代码混淆. 上一节学习了CMake的基本用法, 现在来用CMake实现最简单的LLVM Pass. 笔者的环境仍是MacOS. 需要注意的是LLVM Pass分为Legacy Pass和New Pass两种, 前者是历史遗留, 具体兼容性如下表.

LLVM 默认 可选
5-12 LegacyPassManager -fexperimental-new-pass-manager 启用New, 该功能有限
13-14 NewPassManager -flegacy-pass-manager 启用 Legacy
15-? NewPassManager

编码

  只需要demo.cppCMakeLists.txt两个文件. 因为LLVM的版本较多, 网上开源的LLVM Pass项目只支持部分版本, 笔者根据刚学习的CMake将其改造为兼容LLVM12-18.   

demo.cpp  

#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
#if LLVM_VERSION_MAJOR < 16
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
using namespace llvm;

#include <iostream>

#define PASSNAME          "MyPassDemo"

// ---------------- Legacy Pass ---------------- //
class MyPassDemoLegacy : public FunctionPass {
public:
    static char ID;
    MyPassDemoLegacy() : FunctionPass(ID) {}
    virtual bool runOnFunction(Function& F) override {
        errs() << "LegacyPass: I saw a function called " << F.getName() << "!\n";
        return false;
    }
};
char MyPassDemoLegacy::ID = 0;
#if LLVM_VERSION_MAJOR < 16
static RegisterStandardPasses RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, 
    [](const PassManagerBuilder &, legacy:assManagerBase &M) {
        PM.add(new MyPassDemoLegacy());
    }
);
#else
static RegisterPass<MyPassDemoLegacy> RegisterMyPass(PASSNAME, PASSNAME, false, false);
#endif
// ---------------- Legacy Pass ---------------- //

// ---------------- New Pass ---------------- //
class MyPassDemo : public PassInfoMixin<MyPassDemo> {
public:
    PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) {
        errs() << "hello MyPassDemo\n";
        return PreservedAnalyses::all();
    }
};

extern "C" LLVM_ATTRIBUTE_WEAK ::llvm:assPluginLibraryInfo llvmGetPassPluginInfo() {
    return {
        .APIVersion = LLVM_PLUGIN_API_VERSION,
        .PluginName = PASSNAME,
        .PluginVersion = "1.0",
        .RegisterPassBuilderCallbacks = [](PassBuilder &B) {
            PB.registerPipelineStartEPCallback(
#if LLVM_VERSION_MAJOR < 14
                [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
#else
                [](ModulePassManager &MPM, OptimizationLevel Level) {
#endif
                    MPM.addPass(MyPassDemo());
                });
        }
    };
}
// ---------------- New Pass ---------------- //

CMakeLists.txt

cmake_minimum_required(VERSION 3.6)
project(MyPassDemo)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE "Release")

find_package(LLVM REQUIRED CONFIG) # 引入LLVMConfig.cmake以初始化LLVM相关变量

list(APPEND CMAKE_MODULE_PATH "${LLVM_DIR}") # 不加会导致低版本LLVM cmake失败

include(AddLLVM)                # 引入AddLLVM.cmake以导入add_llvm_pass_plugin函数
include(HandleLLVMOptions)      # 不加会导致低版本LLVM cmake失败

add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})

add_llvm_pass_plugin(MyPassDemo${LLVM_VERSION_MAJOR}
    demo.cpp
)

测试

编译:

export LLVM_DIR=/path/to/llvm12/build/lib/cmake/llvm
cmake -B build --fresh
cmake --build build
# for LLVM<=12      Legacy Pass
llvm12/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -Xclang -load -Xclang build/MyPassDemo12.dylib /tmp/1.cpp
# 等价于上面
llvm12/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fplugin=build/MyPassDemo12.dylib /tmp/1.cpp

# for LLVM=13/14    Legacy Pass
llvm13/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -flegacy-pass-manager -fplugin=build/MyPassDemo13.dylib /tmp/1.cpp

# for LLVM=13/14    New Pass
llvm13/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo13.dylib /tmp/1.cpp

# for LLVM>=15      New Pass
llvm15/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo15.dylib /tmp/1.cpp

经过测试可以发现, Pass的LLVM版本需要和LLVM大版本一致, 否则也会产生错误, 但总体来说Pass解决了LLVM编译代码量过大的问题, 所以仍然值得采用, 测试结果:
hello MyPassDemo

AppleClang的Pass

  AppleClang, 即XCode自带的Clang, 苹果因为安全性考虑阉割掉了LLVM Pass, 因此常规方法并不能加载起来.
当然如果如果逆向技术过关, 也很容易将Pass改为兼容AppleClang的.

ExtensionPointTy

// EP_Peephole                  PeepholeEPCallbacks
void registerPeepholeEPCallback(const std::function<void(FunctionPassManager&, OptimizationLevel)>&);
// EP_LoopOptimizerEnd          LateLoopOptimizationsEPCallbacks
void registerLateLoopOptimizationsEPCallback(const std::function<void(LoopPassManager&, OptimizationLevel)>&);
// EP_LateLoopOptimizations     LoopOptimizerEndEPCallbacks
void registerLoopOptimizerEndEPCallback(const std::function<void(LoopPassManager&, OptimizationLevel)>&);
// EP_ScalarOptimizerLate       ScalarOptimizerLateEPCallbacks
void registerScalarOptimizerLateEPCallback(const std::function<void(FunctionPassManager&, OptimizationLevel)>&);
// EP_CGSCCOptimizerLate        CGSCCOptimizerLateEPCallbacks
void registerCGSCCOptimizerLateEPCallback(const std::function<void(CGSCCPassManager&, OptimizationLevel)>&);
// EP_VectorizerStart           VectorizerStartEPCallbacks
void registerVectorizerStartEPCallback(const std::function<void(FunctionPassManager&, OptimizationLevel)>&);
// EP_EarlyAsPossible           PipelineStartEPCallbacks
void registerPipelineStartEPCallback(const std::function<void(ModulePassManager&, OptimizationLevel)>&);
// EP_ModuleOptimizerEarly      PipelineEarlySimplificationEPCallbacks
void registerPipelineEarlySimplificationEPCallback(const std::function<void(ModulePassManager&, OptimizationLevel)>&);
// 
void registerOptimizerEarlyEPCallback(const std::function<void(ModulePassManager&, OptimizationLevel)>&);
// EP_OptimizerLast             OptimizerLastEPCallbacks
void registerOptimizerLastEPCallback(const std::function<void(ModulePassManager&, OptimizationLevel)>&);
//
void registerFullLinkTimeOptimizationEarlyEPCallback(const std::function<void(ModulePassManager&, OptimizationLevel)>&);
//
void registerFullLinkTimeOptimizationLastEPCallback(const std::function<void(ModulePassManager&, OptimizationLevel)>&);

使用helloworld测试EP顺序:

  • Debug: PipelineStart -> PipelineEarlySimplification -> OptimizerLast
  • Release: PipelineStart -> PipelineEarlySimplification -> Peephole -> Peephole -> Peephole -> ScalarOptimizerLate -> Peephole -> VectorizerStart -> OptimizerLast
回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-9-21 11:20 , Processed in 0.031479 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表