新人第一次发帖,请各位前辈们多多指教qwq
最近总算在 Windows 上用插件的方式把 LLVM Pass 集成进了 NDK,中间踩了不少坑。发现全网几乎没人把完整流程写出来,那就由我来补上这块空白吧~
在查阅一些资料后,笔者发现,想要让我们的NDK也能吃到LLVM Pass一般有两种办法:
显然方法2可插拔性较强,所以我们接下来的讨论致力于方法2的实现
若无特殊说明,以下内容均站在NDK 29.0.14206865的角度分析
笔者在编译成功后偶然看到 这篇文章 ,虽然与本文的思路很相似,但有趣的是他也未给出Windows下的实现。~~不要慌,我接下来介绍的就是在Windows上的食用方法~~~
由于LLVM pass 插件使用 CPP ABI,所以我们必须严格保证插件的CPP ABI和Android NDK对齐。
但(从某个版本之后?)预编译NDK的头文件、静态库(注意:NDK的预构建版本中,Windows和[Linux与Mac,下统称Unix平台] 的思路不同,Linux和Mac上有llvm的动态链接库,但是Windows上llvm是静态链接到clang的)、以及缺失了LLVM CMake文件,编译LLVM的libc++静态库等 …
综上,我们在Windows上就真的需要编译一个和NDK版本完全对齐且可以编译我们pass插件的clang了!
本部分极大程度上参考了同论坛的另外一篇帖子: [原创]一种将LLVM Pass集成到NDK中的通用方法,感谢原帖作者的努力!
请直接参考上面的看雪原帖的 0x01.1 - 0x01.3 部分即可
2026.01.18: 注意到以下几个revision和上面的不一样,表现在分支名和最终 29.0.14206865\toolchains\llvm\prebuilt\windows-x86_64 内的 manifest_{}.xml。

因此我们需要手动修正下 manifest_{}.xml 。
替换remote

替换内部remote分支名

以下Patch启用了LLVM的插件功能,并避免裁剪静态链接库和最终的可执行文件(因为我们要完整的对齐版本号的LLVM!)
以下编译命令供参考

然后我们需要对一些文件重新建立符号链接
以该LLVM Pass仓库为例,进行一个抛砖引玉(
注意:我们需要在这里显式加入上面编译的 LLVM 的 libc++ 头文件与运行时库(libc++.a / libc++abi.a 等)进行编译链接,从而确保 LLVM Pass 用与 NDK 一致的 C++ ABI 构建。
将以下的patch apply,即可编译出我们所需的 libLLVMObfuscationx.dll
在Clion内配置下基本的工具链:


然后应用补丁,插件可以成功编译


以下的Android项目基于 Android Studio Otter | 2025.2.1 Patch 1 的 Native C++ 模板
前面的铺垫都做完了:
接下来就只剩下最后一步:如何把这个 Pass 串进实际的 NDK 项目构建流程里。
在这一步上,大致有两种思路:
这一种思路的核心就是:自己当一回“小型编译器驱动”。也就是说,不再让 add_library() 直接去编 .cpp,而是:
本方案较为繁琐,需要实际修改CMakeFiles.txt,故不在这里详述,感兴趣的作者可以自行探索(
由于NDK27的Clang 18前端可以使用 -fpass-plugin 加载Pass插件,但不能主动接收PASS中的 cl::opt 参数(具体原因可以去翻Clang的源码,社区也有关于这个问题的issue),故以下部分均针对于 “如何让Clang吃到 cl::opt 参数” 这一问题展开(
Android Gradle Plugin + NDK 的构建流程里,真正调 CMake 的那一层会自动传:
在这个 toolchain 里,CMAKE_C_COMPILER / CMAKE_CXX_COMPILER 最终都会被强行设成 NDK 自带的 clang/clang++。简单粗暴地在 Gradle 里塞:
是不会生效的,它在 toolchain 文件里会被改回去,NDK 真正留给我们的扩展点是:ANDROID_CCACHE
在 android.toolchain.cmake 里,大致会干这件事:
也就是说,只要在 CMake 配置阶段传入 -DANDROID_CCACHE=/path/to/wrapper.bat,所有的编译命令都会变成 wrapper.bat <真实的clang或clang++路径> <原始编译参数...>
于是我们就可以写一个 launcher 脚本(Windows 上就是 wrapper.bat),逻辑是:
精简后的脚本大概长这样:
然后在 app/build.gradle.kts 里给 CMake 加上 ANDROID_CCACHE 即可:
而CMakeLists.txt保持不变
就这样,在不改任何 CMakeLists / 不改 NDK 本身的情况下,所有 NDK 的 C/C++ 编译都会经过这层 launcher,能吃到你自己的 LLVM Pass。
这个方案的优点:
编译输出(记得在 gradle.properties 设置 android.native.buildOutput=verbose)

App运行效果:

原生库混淆前/后对比:
使用的混淆参数:irobf(irobf,irobf-indgv,irobf-cse,irobf-cff)
原函数:

混淆前:

混淆后:


可见字符串混淆生效,无法直接检索到字符串 ,证明我们成功加载并使用了OLLVM Pass插件~
project toolchain/llvm_android/
diff --git a/do_build.py b/do_build.py
index 36e7500..ec818e7 100755
--- a/do_build.py
+++ b/do_build.py
@@ -543,10 +544,11 @@ def package_toolchain(toolchain_builder: LLVMBuilder,
necessary_lib_files = set()
if with_runtimes:
if not (host.is_windows and win_sdk.is_enabled()):
- necessary_lib_files |= {
- 'libc++.a',
- 'libc++abi.a',
- }
+ # necessary_lib_files |= {
+ # 'libc++.a',
+ # 'libc++abi.a',
+ # }
+ pass
if host.is_linux:
necessary_lib_files |= {
'libbolt_rt_instr.a',
diff --git a/src/llvm_android/base_builders.py b/src/llvm_android/base_builders.py
index ac11750..77b5e27 100644
--- a/src/llvm_android/base_builders.py
+++ b/src/llvm_android/base_builders.py
@@ -520,7 +520,7 @@ class LLVMBaseBuilder(CMakeBuilder): # pylint: disable=abstract-method
# a59K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8X3c8J5L8$3W2V1i4K6u0V1L8X3c8C8i4K6u0r3L8X3c8C8i4K6u0r3K9i4y4K6N6h3g2K6i4K6u0r3y4e0M7@1 - Don't depend on libtinfo.
defines['LLVM_ENABLE_TERMINFO'] = 'OFF'
- defines['LLVM_ENABLE_PLUGINS'] = 'OFF'
+ defines['LLVM_ENABLE_PLUGINS'] = 'ON'
if patch_level := android_version.get_patch_level():
defines['LLVM_VERSION_PATCH'] = patch_level
defines['LLVM_VERSION_SUFFIX'] = ""
project toolchain/llvm_android/
diff --git a/do_build.py b/do_build.py
index 36e7500..ec818e7 100755
--- a/do_build.py
+++ b/do_build.py
@@ -543,10 +544,11 @@ def package_toolchain(toolchain_builder: LLVMBuilder,
necessary_lib_files = set()
if with_runtimes:
if not (host.is_windows and win_sdk.is_enabled()):
- necessary_lib_files |= {
- 'libc++.a',
- 'libc++abi.a',
- }
+ # necessary_lib_files |= {
+ # 'libc++.a',
+ # 'libc++abi.a',
+ # }
+ pass
if host.is_linux:
necessary_lib_files |= {
'libbolt_rt_instr.a',
diff --git a/src/llvm_android/base_builders.py b/src/llvm_android/base_builders.py
index ac11750..77b5e27 100644
--- a/src/llvm_android/base_builders.py
+++ b/src/llvm_android/base_builders.py
@@ -520,7 +520,7 @@ class LLVMBaseBuilder(CMakeBuilder): # pylint: disable=abstract-method
# 93cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8X3c8J5L8$3W2V1i4K6u0V1L8X3c8C8i4K6u0r3L8X3c8C8i4K6u0r3K9i4y4K6N6h3g2K6i4K6u0r3y4e0M7@1 - Don't depend on libtinfo.
defines['LLVM_ENABLE_TERMINFO'] = 'OFF'
- defines['LLVM_ENABLE_PLUGINS'] = 'OFF'
+ defines['LLVM_ENABLE_PLUGINS'] = 'ON'
if patch_level := android_version.get_patch_level():
defines['LLVM_VERSION_PATCH'] = patch_level
defines['LLVM_VERSION_SUFFIX'] = ""
python3 toolchain/llvm_android/build.py --build-name=win-clang-release --create-tar --no-build=linux --lto --bootstrap-use-prebuilt --builders-package
python3 toolchain/llvm_android/build.py --build-name=win-clang-release --create-tar --no-build=linux --lto --bootstrap-use-prebuilt --builders-package
cd clang-win-clang/bin
mklink wasm-ld.exe lld.exe
mklink llvm-windres.exe llvm-rc.exe
mklink clang++.exe clang.exe
mklink ld64.lld.exe lld.exe
cd clang-win-clang/bin
mklink wasm-ld.exe lld.exe
mklink llvm-windres.exe llvm-rc.exe
mklink clang++.exe clang.exe
mklink ld64.lld.exe lld.exe
Index: ollvm-pass/CMakeLists.txt
<+>UTF-8
===================================================================
diff --git a/ollvm-pass/CMakeLists.txt b/ollvm-pass/CMakeLists.txt
--- a/ollvm-pass/CMakeLists.txt (revision 5c31b5ec0b2360195643381719f797f659e3d12f)
+++ b/ollvm-pass/CMakeLists.txt (date 1763698027522)
@@ -5,7 +5,7 @@
# 1. LOAD LLVM CONFIGURATION
#===============================================================================
# Set this to a valid LLVM installation dir
-set(LT_LLVM_INSTALL_DIR "" CACHE PATH "D:\\dev\\rust_ollvm\\llvm-build\\llvm_x64")
+set(LT_LLVM_INSTALL_DIR "D:\\another-clang-ndk\\clang-win-clang" CACHE PATH "Path to NDK LLVM installation")
# Add the location of LLVMConfig.cmake to CMake search paths (so that
# find_package can locate it)
@@ -22,6 +22,7 @@
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
+include_directories(SYSTEM "${LT_LLVM_INSTALL_DIR}/include/c++/v1")
#===============================================================================
# 2. LLVM-TUTOR BUILD CONFIGURATION
Index: ollvm-pass/obfuscation/CMakeLists.txt
<+>UTF-8
===================================================================
diff --git a/ollvm-pass/obfuscation/CMakeLists.txt b/ollvm-pass/obfuscation/CMakeLists.txt
--- a/ollvm-pass/obfuscation/CMakeLists.txt (revision 5c31b5ec0b2360195643381719f797f659e3d12f)
+++ b/ollvm-pass/obfuscation/CMakeLists.txt (date 1763697579344)
@@ -19,8 +19,15 @@
llvm_map_components_to_libnames(llvm_libs support core irreader linker)
target_link_libraries(LLVMObfuscationx PRIVATE ${llvm_libs})
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set_target_properties(LLVMObfuscationx PROPERTIES
- LINK_FLAGS "-static -static-libgcc -Wl,-Bstatic,--whole-archive -lwinpthread -lstdc++ -Wl,--no-whole-archive -Wl,-Bdynamic"
- )
-endif()
+target_link_libraries(LLVMObfuscationx PRIVATE
+ ${llvm_libs}
+ "${LT_LLVM_INSTALL_DIR}/lib/x86_64-w64-windows-gnu/libc++.a"
+ "${LT_LLVM_INSTALL_DIR}/lib/x86_64-w64-windows-gnu/libc++abi.a"
+ "${LT_LLVM_INSTALL_DIR}/lib/libwinpthread-1.dll"
+ "C:/msys64/mingw64/lib/libucrtbase.a"
+ "C:/msys64/mingw64/lib/libvcruntime140.a"
+)
+target_compile_options(LLVMObfuscationx PRIVATE
+ -fno-rtti
+ -nostdinc++
+)
Index: ollvm-pass/CMakeLists.txt
<+>UTF-8
===================================================================
diff --git a/ollvm-pass/CMakeLists.txt b/ollvm-pass/CMakeLists.txt
--- a/ollvm-pass/CMakeLists.txt (revision 5c31b5ec0b2360195643381719f797f659e3d12f)
+++ b/ollvm-pass/CMakeLists.txt (date 1763698027522)
@@ -5,7 +5,7 @@
# 1. LOAD LLVM CONFIGURATION
#===============================================================================
# Set this to a valid LLVM installation dir
-set(LT_LLVM_INSTALL_DIR "" CACHE PATH "D:\\dev\\rust_ollvm\\llvm-build\\llvm_x64")
+set(LT_LLVM_INSTALL_DIR "D:\\another-clang-ndk\\clang-win-clang" CACHE PATH "Path to NDK LLVM installation")
# Add the location of LLVMConfig.cmake to CMake search paths (so that
# find_package can locate it)
@@ -22,6 +22,7 @@
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
+include_directories(SYSTEM "${LT_LLVM_INSTALL_DIR}/include/c++/v1")
#===============================================================================
# 2. LLVM-TUTOR BUILD CONFIGURATION
Index: ollvm-pass/obfuscation/CMakeLists.txt
<+>UTF-8
===================================================================
diff --git a/ollvm-pass/obfuscation/CMakeLists.txt b/ollvm-pass/obfuscation/CMakeLists.txt
--- a/ollvm-pass/obfuscation/CMakeLists.txt (revision 5c31b5ec0b2360195643381719f797f659e3d12f)
+++ b/ollvm-pass/obfuscation/CMakeLists.txt (date 1763697579344)
@@ -19,8 +19,15 @@
llvm_map_components_to_libnames(llvm_libs support core irreader linker)
target_link_libraries(LLVMObfuscationx PRIVATE ${llvm_libs})
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set_target_properties(LLVMObfuscationx PROPERTIES
- LINK_FLAGS "-static -static-libgcc -Wl,-Bstatic,--whole-archive -lwinpthread -lstdc++ -Wl,--no-whole-archive -Wl,-Bdynamic"
- )
-endif()
+target_link_libraries(LLVMObfuscationx PRIVATE
+ ${llvm_libs}
+ "${LT_LLVM_INSTALL_DIR}/lib/x86_64-w64-windows-gnu/libc++.a"
+ "${LT_LLVM_INSTALL_DIR}/lib/x86_64-w64-windows-gnu/libc++abi.a"
+ "${LT_LLVM_INSTALL_DIR}/lib/libwinpthread-1.dll"
+ "C:/msys64/mingw64/lib/libucrtbase.a"
+ "C:/msys64/mingw64/lib/libvcruntime140.a"
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2026-1-18 14:04
被霜降白羽编辑
,原因: upd