本文为红队学院直播内容整理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

SETLLVM_DIR=LLVMPATH
SETLLVM_DIR_BIN=%LLVM_DIR%/bin
SETPATH=%LLVM_DIR_BIN%;%PATH%;
cargo clean
cargo build –release