正确做法是按项目需求选择llvm版本:ollvm 4.0必须源码编译并降级gcc至8.x;o-mvll推荐用官方docker镜像;仅需clang编译可apt install clang-12并切换默认版本。

用 apt install 安装 LLVM,但版本常不匹配
直接 apt install llvm 在 Ubuntu 镜像里看似省事,但实际会装最新版(比如 14.x 或 15.x),而很多混淆工具(如 OLLVM、O-MVLL)明确要求 LLVM 4.0 或 12.x 等特定版本。装错版本会导致编译失败或插件加载报错 undefined symbol: _ZN4llvm12PassRegistry12getNormalCtorEv 这类符号缺失错误。
正确做法是:查清目标项目文档里的 LLVM 版本要求,再选对应安装方式:
- OLLVM 4.0 → 必须用源码编译 LLVM 4.0,
apt仓库无此版本 - O-MVLL → 推荐用其官方 Docker 镜像
openobfuscator/omvll-ndk,已预装适配的clang和llvm - 仅需
clang做前端编译(非插件开发)→ 可用apt install clang-12+update-alternatives切换默认
从源码编译 LLVM 4.0(OLLVM 必选路径)
OLLVM 4.0 与现代 LLVM 主干完全不兼容,必须单独编译旧版。关键不是“能不能编”,而是“怎么避开 GCC 版本冲突”——Ubuntu 22.04+ 默认带 GCC 11+,但 LLVM 4.0 只支持 GCC 8.x。
在 Dockerfile 中这样写才稳定:
RUN apt update && apt install -y \
git cmake ninja-build gcc-8 g++-8 python3 \
&& rm -rf /var/lib/apt/lists/*
<p>RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 8 \
&& update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 8</p><h1>拉取并编译 LLVM 4.0(非主干,是 obfuscator-llvm 分支)</h1><p>RUN git clone -b llvm-4.0 --depth=1 <a href="https://www.php.cn/link/a3ed5c9ef7dbd64b5f492b987b11c14e">https://www.php.cn/link/a3ed5c9ef7dbd64b5f492b987b11c14e</a> /tmp/ollvm-src \
&& mkdir -p /tmp/ollvm-build /opt/ollvm \
&& cd /tmp/ollvm-build \
&& cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/ollvm \
-DLLVM_ENABLE_RTTI=ON \
/tmp/ollvm-src \
&& ninja install
</p>
编译完记得把 /opt/ollvm/bin 加进 PATH,否则 clang 命令找不到。
用官方预编译二进制(适合快速验证)
LLVM 官方提供各版本的 .tar.xz 包,比源码编译快,且避免依赖污染系统。适用于不需要修改 LLVM IR Pass 的场景(比如只调用 clang 编译 C 代码)。
在 Dockerfile 中可这样写:
ARG LLVM_VERSION=12.0.0
RUN curl -L "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-20.04.tar.xz" \
| tar -xJ -C /opt/ \
&& ln -sf /opt/clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-20.04 /opt/llvm
ENV PATH="/opt/llvm/bin:$PATH"
注意三点:
- URL 中的
ubuntu-20.04必须和基础镜像一致,否则可能缺libtinfo.so.5等运行时库 - 别用
llvmorg-15.0.0这类新版本去跑 OLLVM 插件,IR 格式已变,直接段错误 - 预编译包不含
llvm-config,如果构建脚本依赖它,得手动 symlinks 或改脚本
交叉编译场景下,NDK 的 clang 才是真实主力
做 Android 混淆或 ARM64 二进制保护时,真正调用的往往不是系统 clang,而是 NDK 里的 clang(路径类似 toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang)。Docker 镜像里装系统 LLVM 只是铺路,重点是把 NDK 工具链挂进去。
典型操作:
- 下载 NDK r25+(必须 ≥r23,旧版不支持 ARM64 混淆 Pass)
- 解压后
docker cp进容器,或构建时用COPY指令 - 设置环境变量:
export ANDROID_NDK_ROOT=/opt/android-ndk,并在PATH中加入$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin - 调用时明确指定 target:
aarch64-linux-android21-clang -Xclang -load -Xclang /path/to/obfuscation.so ...
这里容易忽略的是:NDK 的 clang 实际是 wrapper,它内部调用的 llvm 是自带的精简版,和你 apt install 的系统 llvm 完全无关——所以别试图用系统 llvm-config 去查它的版本。











