首页
社区
课程
招聘
[原创] 在 Windows下搭建LLVM 使用环境
发表于: 2023-2-19 22:26 23931

[原创] 在 Windows下搭建LLVM 使用环境

2023-2-19 22:26
23931

搭建一个windows 下应用层能够快捷使用的 llvm 工具链,文中将会解释为什么要这么做,以及阐述其他方式可能会遇到的坑点,同时这个文章只是一个实践文,并不涉及具体原理,只为了提供一个windows 下搭建llvm的最佳实践方案。

为什么不用 VS2019 自带的llvm,而要很复杂的一系列编译替换来捣鼓它?
答:不从头编译llvm项目的话,就无法编译生成Pass,你只是能用clang去编译生成exe,比起VS2019 自带的cl并没有明显优势。作为一个小白,我并不清楚为什么微软回出一个支持llvm工具编译的原因(也许是对于大型c++项目的编译速度考虑?),但对于我们来说,Pass相当于一个插件,起到中间层实际执行混淆的作用,是我们混淆功能的核心,所以必须要用它,而用它,你就需要对llvm进行从头到尾的编译。

为什么一定要用12.0.0版本的llvm?而且llvm 官方文档中支持VS2019直接进行工具链的编译,为什么不用 VS2019 进行项目编译,而要使用mingw来编译llvm项目?
经过对12.0.1 / 12.0.0 / 15.0.6 几个版本的实践,会遇到各种问题,其中最主要的问题是,如果不使用12.0.0 ,你编译出来的llvm 集成到VS 2019中使用时,会出现各种意想不到的错误。另外用MSVC(VS 2019) 编译llvm时,不支持开启BUILD_SHARED_LIBS 选项,但可以使用LLVM_EXPORT_SYMBOLS_FOR_PLUGINS 选项或LLVM_ENABLE_PLUGINS选项,但这样会出现一个问题,编译后的pass仅能使用new Pass语法,而且必须使用opt 进行加载插件使用,实际只有registerPipelineParsingCallback回调函数可以正常使用

既然我们需要的只是Pass,也就是一个dll文件,那么我们是否能生成12.0.0 版本的llvm pass,然后就直接用VS 2019 自带的llvm来编译集成呢?
不是自己编译出来的llvm pass 和 自己编译出来的 llvm clang-cl 在使用时会报0x7E(无法加载模块)的错误,因为不想在windows下试图调试llvm 源码找出报错原因,所以只能将就了。

基础环境
windows 10
https://github.com/llvm/llvm-project/releases/tag/llvmorg-12.0.0
CMake (https://cmake.org/download/)
VS 2019 16.11.10
MSYS2
Python
Git

安装MSYS2
PS:llvm 是支持 VS 2019直接去生成llvm的,不用安装这些东西,官方有相关文章(https://llvm.org/docs/GettingStartedVS.html),编译很快,但编出来不会写相关Pass并利用,这里用gcc + vs2019 构造工具链,欢迎踩完坑后分享~
官网:https://www.msys2.org/
详细安装步骤:https://bbs.kanxue.com/thread-272346.htm#msg_header_h3_1
安装后安装gcc工具链

在VS 2019 中安装clang cmake 等工具
图片描述

将源码解压,文件树如下:
PS:其中build* 文件夹是我运行bat文件后产生的目录,lld 可以不用下载,是之前踩坑的产物
图片描述

编译源码:
x64_gcc_build.bat 文件内容

x86_gcc_build.bat 文件内容

运行 bat 文件即可编译对应的 llvm
图片描述

编译安装完成之后可以在下面两个目录中找到编译好的clang 文件

图片描述

3 . bat文件的内容如下(x86 就是将前面的环境设置为x86的即可):

4 . TestProgram.cpp的内容,是一道简单的re逆向题:

5 . 配置好之后,直接运行bat文件就能编译测试:
图片描述

我使用的是vs 2019,在之前下载了llvm 12.0 ,并且我们编译替换成自己的,做好准备工作之后,只需要在命令行选项中加入调用语句即可。

图片描述

使用前还需要将平台工具集先改了
图片描述

pacman -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-i686-toolchain cmake
pacman -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-i686-toolchain cmake
cd C:\Users\admin\Documents\llvmProject12
set PATH=%PATH%;C:\msys64\mingw64\bin
gcc --version
cmake -G "MinGW Makefiles" -S ./llvm -B ./build_dyn_x64  ^
-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;" ^
-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
cmake --build ./build_dyn_x64 -j 4
cmake -DCMAKE_INSTALL_PREFIX="C:\Program Files\LLVM\llvm12\llvm12_dyn_x64" -P .\build_dyn_x64\cmake_install.cmake
cd C:\Users\admin\Documents\llvmProject12
set PATH=%PATH%;C:\msys64\mingw64\bin
gcc --version
cmake -G "MinGW Makefiles" -S ./llvm -B ./build_dyn_x64  ^
-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;" ^
-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
cmake --build ./build_dyn_x64 -j 4
cmake -DCMAKE_INSTALL_PREFIX="C:\Program Files\LLVM\llvm12\llvm12_dyn_x64" -P .\build_dyn_x64\cmake_install.cmake
cd C:\Users\admin\Documents\llvmProject12
set PATH=%PATH%;C:\msys64\mingw32\bin
gcc --version
cmake -G "MinGW Makefiles" -S ./llvm -B ./build_dyn_x32  ^
-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;" ^
-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
cmake --build ./build_dyn_x32 -j 4
cmake -DCMAKE_INSTALL_PREFIX="C:\Program Files\LLVM\llvm12\llvm12_dyn_x32" -P .\build_dyn_x32\cmake_install.cmake
cd C:\Users\admin\Documents\llvmProject12
set PATH=%PATH%;C:\msys64\mingw32\bin
gcc --version
cmake -G "MinGW Makefiles" -S ./llvm -B ./build_dyn_x32  ^
-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;" ^
-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
cmake --build ./build_dyn_x32 -j 4
cmake -DCMAKE_INSTALL_PREFIX="C:\Program Files\LLVM\llvm12\llvm12_dyn_x32" -P .\build_dyn_x32\cmake_install.cmake
C:\Program Files\LLVM\llvm12\llvm12_dyn_x64
C:\Program Files\LLVM\llvm12\llvm12_dyn_x32
C:\Program Files\LLVM\llvm12\llvm12_dyn_x64
C:\Program Files\LLVM\llvm12\llvm12_dyn_x32
libstdc++-6.dll
libgcc_s_seh-1.dll
libwinpthread-1.dll
zlib1.dll
libstdc++-6.dll
libgcc_s_seh-1.dll
libwinpthread-1.dll
zlib1.dll
libstdc++-6.dll
libgcc_s_dw2-1.dll (这个库名称不一样)
libwinpthread-1.dll
zlib1.dll
libstdc++-6.dll
libgcc_s_dw2-1.dll (这个库名称不一样)
libwinpthread-1.dll
zlib1.dll
project(mydemo)
cmake_minimum_required(VERSION 3.10)
 
if(NOT DEFINED ENV{LLVM_HOME})
    # User must define the LLVM_HOME environment that point to the root installation dir of llvm
    message(FATAL_ERROR "Environment variable $LLVM_HOME is not defined, user should define it before running cmake!")
endif()
 
message(STATUS "LLVM_HOME = [$ENV{LLVM_HOME}]")
 
if(NOT DEFINED ENV{LLVM_DIR})
    # Default llvm config file path
    set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib/cmake/llvm)
endif()
 
# Check the path
if (NOT EXISTS $ENV{LLVM_DIR})
    message(STATUS "Path ($ENV{LLVM_DIR}) not found!")
 
    # If default llvm config path not found, try this one,
    # which is config with [-DLLVM_LIBDIR_SUFFIX=64] before building llvm
    set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib64/cmake/llvm)
    if (NOT EXISTS $ENV{LLVM_DIR})
        message(FATAL_ERROR "Path ($ENV{LLVM_DIR}) not found!")
    else()
        message(STATUS "Path ($ENV{LLVM_DIR}) found!")
    endif()
else()
    message(STATUS "Path ($ENV{LLVM_DIR}) found!")
endif()
 
# Enable verbose output for debug,
# Same as: cmake -D CMAKE_VERBOSE_MAKEFILE=ON or make VERBOSE=1
# set(CMAKE_VERBOSE_MAKEFILE on)
 
find_package(LLVM REQUIRED CONFIG)
add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
 
# Debug
message(STATUS "LLVM_DEFINITIONS  : ${LLVM_DEFINITIONS}")
message(STATUS "LLVM_INCLUDE_DIRS : ${LLVM_INCLUDE_DIRS}")
message(STATUS "LLVM_LIBRARY_DIRS : ${LLVM_LIBRARY_DIRS}")
 
add_library(mydemo SHARED
    # Add your source file here, header file is not neccessary
    ./src/mydemo.cpp
)
 
# Use C++11 to compile your pass (i.e., supply -std=c++11).
target_compile_features(mydemo PRIVATE cxx_range_for cxx_auto_type)
 
include_directories(./include)
 
# LLVM is (typically) built with no C++ RTTI. We need to match that;
# otherwise, we'll get linker errors about missing RTTI data.
set_target_properties(mydemo PROPERTIES COMPILE_FLAGS "-fno-rtti")
 
# Get proper shared-library behavior (where symbols are not necessarily
# resolved when the shared library is linked) on OS X.
if(APPLE)
    set_target_properties(mydemo PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
endif(APPLE)
 
target_link_libraries(mydemo
 
    libLLVMCore.dll.a
    libLLVMSupport.dll.a
    libLLVMipo.dll.a
 
    libLLVMDemangle.dll.a
    libLLVMTransformUtils.dll.a
    libLLVMAnalysis.dll.a
    libpthread.a
)
project(mydemo)
cmake_minimum_required(VERSION 3.10)
 
if(NOT DEFINED ENV{LLVM_HOME})
    # User must define the LLVM_HOME environment that point to the root installation dir of llvm
    message(FATAL_ERROR "Environment variable $LLVM_HOME is not defined, user should define it before running cmake!")
endif()
 
message(STATUS "LLVM_HOME = [$ENV{LLVM_HOME}]")
 
if(NOT DEFINED ENV{LLVM_DIR})
    # Default llvm config file path
    set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib/cmake/llvm)
endif()
 
# Check the path
if (NOT EXISTS $ENV{LLVM_DIR})
    message(STATUS "Path ($ENV{LLVM_DIR}) not found!")
 
    # If default llvm config path not found, try this one,
    # which is config with [-DLLVM_LIBDIR_SUFFIX=64] before building llvm
    set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib64/cmake/llvm)
    if (NOT EXISTS $ENV{LLVM_DIR})
        message(FATAL_ERROR "Path ($ENV{LLVM_DIR}) not found!")
    else()
        message(STATUS "Path ($ENV{LLVM_DIR}) found!")
    endif()
else()
    message(STATUS "Path ($ENV{LLVM_DIR}) found!")
endif()
 
# Enable verbose output for debug,
# Same as: cmake -D CMAKE_VERBOSE_MAKEFILE=ON or make VERBOSE=1
# set(CMAKE_VERBOSE_MAKEFILE on)
 
find_package(LLVM REQUIRED CONFIG)
add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
 
# Debug
message(STATUS "LLVM_DEFINITIONS  : ${LLVM_DEFINITIONS}")
message(STATUS "LLVM_INCLUDE_DIRS : ${LLVM_INCLUDE_DIRS}")
message(STATUS "LLVM_LIBRARY_DIRS : ${LLVM_LIBRARY_DIRS}")
 
add_library(mydemo SHARED
    # Add your source file here, header file is not neccessary
    ./src/mydemo.cpp
)
 
# Use C++11 to compile your pass (i.e., supply -std=c++11).
target_compile_features(mydemo PRIVATE cxx_range_for cxx_auto_type)
 
include_directories(./include)
 
# LLVM is (typically) built with no C++ RTTI. We need to match that;
# otherwise, we'll get linker errors about missing RTTI data.

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-6-6 14:55 被Rixo_叶默编辑 ,原因: 看到自个文章描述有些不清楚,改了一下描述,让人更容易看懂
收藏
免费 9
支持
分享
最新回复 (16)
雪    币: 1883
活跃值: (210)
能力值: ( LV13,RANK:288 )
在线值:
发帖
回帖
粉丝
2
直接替换vs自带的llvm也可以吧,最多只是改一下有个文件里面的版本号,可以换成任意版本。Microsoft.Cpp.Default.props,找到 LLVMToolsVersion
2023-4-4 07:45
1
雪    币: 3535
活跃值: (31016)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
vs2022适用吗
2023-4-4 09:16
1
雪    币: 1671
活跃值: (215852)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
4
秋狝 vs2022适用吗
可以用
2023-4-4 09:58
0
雪    币: 264
活跃值: (699)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
收藏一下,小菜最近也想写一个基于 LLVM 的混淆。
2023-4-7 18:58
0
雪    币: 1540
活跃值: (2807)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
我要拿这个llvm去编译ffmpeg
2023-4-8 22:02
0
雪    币: 525
活跃值: (677)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
leavesth 直接替换vs自带的llvm也可以吧,最多只是改一下有个文件里面的版本号,可以换成任意版本。Microsoft.Cpp.Default.props,找到 LLVMToolsVersion
我尝试过直接替换,但之前没解决怎么替换后还能在VS的工具集里面找到自己编译成功的llvm,以及对应工具集运行时参数配置的问题。
2023-4-12 09:55
0
雪    币: 547
活跃值: (363)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
大佬,请问 -Xclang -load -Xclang "C:\\Users\\admin\\Desktop\\Work\\ollvm++\\Build\\libmypass.dll" 这里如何传递pass需要的参数呢?

比如有个pass是做基本块分割的,有个参数-split_num指定分割数量,使用opt的话命令是这样的:opt -load ../Build/LLVMObfuscator.so -split -split_num 3 -S IR/TestProgram_orig.ll -o IR/TestProgram_split.ll

求教 如果要集成到VisualStudio2019中,对应的命令应该怎么写?
2023-6-3 23:42
0
雪    币: 525
活跃值: (677)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
好久没用了,但我觉得  -Xclang -load -Xclang "C:\\Users\\admin\\Desktop\\Work\\ollvm++\\Build\\libmypass.dll -mllvm -split -split_num 3   应该能解决你的问题
2023-6-6 14:44
0
雪    币: 547
活跃值: (363)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
谢谢大佬,可能是我环境问题,这样仍然不行。最终没使用插件方案,直接编译进工具链了。
2023-7-2 16:42
0
雪    币: 401
活跃值: (1165)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
我使用的是vs 2019,在之前下载了llvm 12.0 ,并且我们编译替换成自己的,做好准备工作之后,只需要在命令行选项中加入调用语句即可。

替换的步骤咋没有?最后一步没完成,哭了
2023-8-10 16:21
0
雪    币: 657
活跃值: (5509)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
大佬你好 麻烦问一下  vs自带的是不是没有混淆功能  加了参数看到没有效果
2023-10-21 17:27
0
雪    币: 525
活跃值: (677)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
梦_魇 大佬你好 麻烦问一下 vs自带的是不是没有混淆功能 加了参数看到没有效果
混淆需要自个写混淆的处理代码,llvm提供的是可以编写混淆插件的一个框架,VS自带的只能使用它的编译功能,编出来没有对应的混淆效果
2023-12-3 13:42
0
雪    币: 525
活跃值: (677)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
黑嘿黑 我使用的是vs 2019,在之前下载了llvm 12.0 ,并且我们编译替换成自己的,做好准备工作之后,只需要在命令行选项中加入调用语句即可。 替换的步骤咋没有?最后一步没完成,哭了
.....没有想到有人会在这里被卡住,哈哈哈哈,就是找到对应的VS目录,然后把对应的exe替换就好了啊,你可以看这篇帖子https://bbs.kanxue.com/thread-272346.htm#msg_header_h3_1,详细很多,
2023-12-31 11:41
0
雪    币: 10
活跃值: (1183)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
照着帖子步骤,编译llvm那步,出现了好多错误,说找不到文件,以及cpp文件中的错误
2024-3-22 16:05
0
雪    币: 10
活跃值: (1183)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
我编译的bin里面没有clang.exe 好多都没有
2024-3-22 16:07
0
游客
登录 | 注册 方可回帖
返回
//