本文为红队学院直播内容整理https://www.bilibili.com/video/BV1qy2vYXEpD/
环境准备
MSYS2安装MINGW、CMAKE、
pacman -Syu –noconfirm #更新
#工具下载
pacman -S mingw-w64-x86_64-llvm \
mingw-w64-x86_64-clang \
mingw-w64-x86_64-nasm \
mingw-w64-x86_64-cmake \
mingw-w64-x86_64-ninja \
make \
git –noconfirm
MSVC
rustc –version –verbose #查看当前rustc 版本
下载对应版本的llvm_msvc Release
编译Pass
目录结构
<project dir>/
|
CMakeLists.txt
build.bat
<pass name>/
|
CMakeLists.txt
Pass.cpp
Project下CmakeLists.txt
SET (CMAKE_CXX_STANDARD 17)
find_package(LLVM REQUIRED CONFIG)
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(AddLLVM)
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
add_subdirectory(HelloWorld)
Pass下CmakeLists.txt
add_library(HelloWorld MODULE Pass.cpp)
# Find the libraries that correspond to the LLVM components
# that we wish to use
llvm_map_components_to_libnames(llvm_libs support core irreader)
# Link against LLVM libraries
target_link_libraries(HelloWorld ${llvm_libs})
pass.cpp
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
namespace llvm {
class HelloWorldPass : public PassInfoMixin<HelloWorldPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
static bool isRequired() { return true; }
};
} // namespace llvm
using namespace llvm;
PreservedAnalyses HelloWorldPass::run(Function &F,
FunctionAnalysisManager &AM) {
errs() << F.getName() << "\n";
return PreservedAnalyses::all();
}
/* New PM Registration */
llvm::PassPluginLibraryInfo gethelloPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "hello", LLVM_VERSION_STRING,
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, llvm::FunctionPassManager &PM,
ArrayRef<llvm::PassBuilder::PipelineElement>) {
if (Name == "hello") {
PM.addPass(HelloWorldPass());
return true;
}
return false;
});
}};
}
extern "C" __declspec(dllexport) LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return gethelloPluginInfo();
}
build.bat
SET LLVM_DIR=pathto\clang+llvm-18.1.2-x86_64-pc-windows-msvc
SET LLVM_DIR_BIN=%LLVM_DIR%\bin
SET PATH=%LLVM_DIR_BIN%;%PATH%
rmdir /q /s build
mkdir build
cd build
cmake -G "Ninja" -S .. -B . -DCMAKE_BUILD_TYPE=Release -DLLVM_DIR=%LLVM_DIR%
cmake --build .
GNU
如果用GNU就需要自己先用GNU编译llvm,
到github的rust对应版本中找到.gitmodules 文件。rust自己改了llvm,在gitmodules中有对应llvm的链接和版本
到url里面根据branch下载对应的源码
编译之前还需要解除Windows的路径长度限制
计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem LongPathsEnabled设置为1,这个选项好像重启后会关闭,编译时记得检查
在源码目录下执行执行cmake生成命令
cmake -G “Ninja” -S ./llvm -B ./llvm_x64 -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=”clang;lld;” -DLLVM_TARGETS_TO_BUILD=”X86″ -DBUILD_SHARED_LIBS=ON -DLLVM_INCLUDE_TESTS=OFF -DLLVM_BUILD_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -DLLVM_ENABLE_DUMP=ON -DLLVM_ENABLE_PLUGINS=ON
-G 指定 生成”MinGW Makefiles”生成MinGW格式的make文件
-S 指定生成Make文件的来源
-B 指定生成的llvm位置
-DCMAKE_BUILD_TYPE=Release #这里必须Release,Debug版本好像有dll的导出函数个数超过PE格式限制了
-DLLVM_ENABLE_PROJECTS=”clang;lld;”:仅需要编译clang和lld模块
-DLLVM_TARGETS_TO_BUILD=”X86″:构建X86架构的llvm
-DBUILD_SHARED_LIBS=ON开启共享libs
-DLLVM_INCLUDE_TESTS=OFF -DLLVM_BUILD_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_BUILD_BENCHMARKS=OFF:不编译测试模块,不然会占据大量空间,用VS编译没关闭测试选项大概180个G是不够用的。
-DLLVM_ENABLE_DUMP=ON:运行Dump
-DLLVM_ENABLE_PLUGINS=ON 允许插件
编译好之后的安装命令
cmake -DCMAKE_INSTALL_PREFIX=C:\MyProgramFiles\LLVM\llvm8\llvm8_dyn_x64 -P .\llvm_x64\cmake_install.cmake
安装之后运行会报缺DLL,在MINGW的对应的GCC目录中把缺少的DLL复制当llvm/bin的目录下就行,同时将编译目录/lib中的所有.a文件复制到安装后的lib目录中
pass的编译与MSVC相同
RUST加载Pass
在rust项目中创建.cargo/config.toml
[target.x86_64-pc-windows-msvc]
rustflags = [
“-Zllvm-plugins=PATH/HelloWorld.dll”,
“-Cpasses=function(hello)”,
]
直接cargo build 就能出效果
这里注意如果是GNU编译的Pass,必须将llvm/bin加入到环境变量中再cargo build ,不然会报错找不到模块 0x7e
build.bat