type
status
date
slug
summary
tags
category
icon
password
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工程(修改不多编译较快)
测试
- 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