LLVM 13.1 new Pass插件形式 [for win]
4大关键点:
- LLVM 13.1版本(当前最新版)
- windows平台上的pass demo
- new pass形式(非legacy)
- clang 动态插件形式(pass插件与llvm本体编译分离)
背景:
当前外部资料均为老版本LLVM + legacy模式的pass,和当前llvm版本脱节,新模式下没找到资料就自己研究了下,具体新旧LLVM版本对pass的编写有啥影响,具体原因是啥,怎么做能兼容旧版本见我的上一篇文章(设计新旧异同分析的原理与思路)
操作步骤
(后续所有步骤若目录不一致需自行改动目录结构,不赘述)
- 建立:D:\Code\llvm-project文件夹,放入名为clang的源码文件夹,和名为llvm的源码文件夹,再在此目录新建一个MyCustom文件夹用以存放我们的动态插件工程(版本LLVM 13.1)
- 按照我的上一篇文章介绍的步骤1~3构建好x64release版本的clang&llvm(新旧pass对llvm和clang的构建并无区别),编译较慢且CPU占用高请找空闲时间完成
- 切换至D:\Code\llvm-project\llvm\build\Release\bin目录键入
.\clang.exe --version
命令确保输出如下形式
clang version 13.0.1
Target: x86_64-pc-windows-msvc
...
- 新建D:\Code\llvm-project\MyCustom\src文件夹,在其中新建文件MyPass.cpp并写入
#include <llvm/IR/PassManager.h>
#include <llvm/IR/Module.h>
#include <llvm/Pass.h>
#include <llvm/Passes/PassBuilder.h>
#include <llvm/Passes/PassPlugin.h>
using namespace llvm;
// test for my custom pass
class MyCustomPass : public PassInfoMixin<MyCustomPass> {
public:
PreservedAnalyses run(Module& M, ModuleAnalysisManager& AM)
{
for (auto& f : M) {
errs() << "MyCustomPass: " << f.getName() << "\n";
}
return PreservedAnalyses::all();
}
};
// 在clang里根据配置创建自定义pass,called by PassManagerBuilder::populateModulePassManager
extern "C" __declspec(dllexport) void __stdcall clangAddCustomPass(ModulePassManager & MPM)
{
MPM.addPass(MyCustomPass());
}
- 在D:\Code\llvm-project\MyCustom目录下新建文件CMakeLists.txt,并写入
cmake_minimum_required(VERSION 3.4)
project(custompass)
# 设置编译模式
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") #/MT
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") #/MTd
# 添加源码目录
aux_source_directory(./src src_1)
set(srcs ${src_1})
# 生成动态链接库,那么在Windows下就是dll,最终会生成custompass.dll
add_library(custompass SHARED ${srcs})
set(LLVM_PROJECT_DIR "" CACHE STRING
"You must input the correct llvm-project dir")
if( LLVM_PROJECT_DIR STREQUAL "" )
MESSAGE(FATAL_ERROR "LLVM_PROJECT_DIR is empty")
else()
MESSAGE("LLVM_PROJECT_DIR=${LLVM_PROJECT_DIR}")
endif()
# 添加头文件,需要编译后的以及源码头文件
include_directories("${LLVM_PROJECT_DIR}/llvm/build/include")
include_directories("${LLVM_PROJECT_DIR}/llvm/include")
# 添加编译后的lib
set(mylibdir "${LLVM_PROJECT_DIR}/llvm/build/Release/lib")
set(VTKLIBS LLVMCore LLVMSupport LLVMBinaryFormat LLVMRemarks LLVMBitstreamReader)
foreach(libname ${VTKLIBS})
SET(FOUND_LIB "FOUND_LIB-NOTFOUND")
find_library(FOUND_LIB NAMES ${libname} HINTS ${mylibdir} NO_DEFAULT_PATH)
IF (FOUND_LIB)
message("found lib: ${FOUND_LIB}")
LIST(APPEND mylibs ${FOUND_LIB})
ELSE()
MESSAGE("not lib found: ${libname}")
ENDIF ()
endforeach(libname)
#message(${mylibs})
#message(${CPPUNIT_LIBRARY})
target_link_libraries(custompass PUBLIC ${mylibs})
- cmake选中D:\Code\llvm-project\MyCustom工程,配置LLVM_PROJECT_DIR字段为D:\Code\llvm-project,并确保成功生成sln工程文件
- 编译工程文件得到custompass.dll输出文件,将其拷贝到D:\Code\llvm-project\llvm\build\Release\bin目录下
- 打开llvm工程,依次打开
Object Libraries\obj.clangCodeGen\Source Files\BackendUtil.cpp
文件 - 在EmitAssemblyHelper::EmitAssemblyWithNewPassManager函数内的
if (!CodeGenOpts.DisableLLVMPasses)
判断体末尾加上下方代码已使其固定加载custompass.dll的clangAddCustomPass导出函数(记得引入#include "windows.h"
)
#if _WINDOWS
using myPassType = void(__stdcall *)(ModulePassManager & MPM);
auto customHandle = ::LoadLibraryA("custompass.dll");
if (!customHandle) {
errs() << "custompass.dll not found\n";
} else {
auto pfn =
(myPassType)::GetProcAddress(customHandle, "clangAddCustomPass");
if (pfn != NULL) {
pfn(MPM);
} else {
errs() << "clangAddCustomPass not found\n";
}
}
#endif // _WINDOWS
- 增量编译LLVM工程(修改不多编译较快)
测试
12. D:\Code\llvm-project\llvm\build\Release\bin下新建文件test.cpp,并写入
#include <stdio.h>
int add(int a,int b)
{
return a+b;
}
int min(int a,int b)
{
return a-b;
}
void show()
{
printf("show\n");
}
int main(int argc, char* argv[]){
add(1,2);
min(5,3);
show();
return 0;
}
- 输入命令
.\clang.exe test.cpp -o test.exe
成功得到输出,后续仅需修改MyCustom工程并将编译后的插件放入clang目录即可,同时该工程支持加入多个PASS,先将pass流程泡通搞清楚PASS流程逻辑&llvm的语法组件…一些代码混淆,数据加密等功能后续发文
MyCustomPass: ?add@@YAHHH@Z
MyCustomPass: ?min@@YAHHH@Z
MyCustomPass: ?show@@YAXXZ
MyCustomPass: printf
MyCustomPass: main
MyCustomPass: llvm.va_start
MyCustomPass: _vfprintf_l
MyCustomPass: __acrt_iob_func
MyCustomPass: llvm.va_end
MyCustomPass: __stdio_common_vfprintf
MyCustomPass: __local_stdio_printf_options