Android CI/CD 编译 AIDL 报错分析与解决:undefined symbol: _ZnwmSt11align_val_t
问题背景
在 CI/CD 环境编译 Android 项目时,遇到 AIDL 编译失败:
> Task :app:compileReleaseAidl FAILED
/path/to/android-sdk/build-tools/30.0.3/aidl: symbol lookup error:
/path/to/android-sdk/build-tools/30.0.3/aidl: undefined symbol: _ZnwmSt11align_val_t
奇怪的是:远程登录 Docker 容器手动编译一切正常,但 CI/CD 流水线直接执行则报错。
问题分析
1. 错误符号解读
_ZnwmSt11align_val_t 是 C++ 的 name mangling(名称修饰) 后的符号:
_ZnwmSt11align_val_t → operator new(unsigned long, std::align_val_t)
这是 C++17 引入的对齐内存分配(Aligned Memory Allocation) 操作符。
2. 根本原因
通过日志发现 CI/CD 宿主机环境:
bash
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
| 组件 | 版本 | C++17 支持 |
|---|---|---|
| CI/CD 宿主机 GCC/libstdc++ | 4.8.5 (2015) | ❌ 不支持 |
| Android Build Tools 30.0.3 | 30.0.3 | 需要 C++17 |
结论 :Android SDK Build Tools 30.0.3 的 aidl 二进制文件是用 C++17 编译的,而 CI/CD 宿主机的 GCC 4.8.5 版本太老,其 libstdc++ 不包含 C++17 的符号。
3. 为什么 Docker 内正常?
Docker 容器的基础镜像包含更新版本的 libstdc++ (通常 GCC 7+ 或更高),而 CI/CD 直接执行脚本时用的是宿主机的老旧系统库。
关键排查命令
检查符号是否存在
bash
# 查看 build-tools 自带的库是否包含 C++17 符号
nm -D /path/to/android-sdk/build-tools/30.0.3/lib64/libc++.so | grep _ZnwmSt11align_val_t
| 输出 | 含义 |
|---|---|
| 有匹配行 | ✅ 库包含此符号 |
| 无输出 | ❌ 库不包含此符号 |
查看可执行文件依赖的动态库
bash
ldd /path/to/android-sdk/build-tools/30.0.3/aidl
输出示例:
linux-vdso.so.1 => (0x00007ffd9a7fe000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f...)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)
重点观察 libstdc++.so.6 或 libc++.so 指向哪个路径:
| 情况 | 含义 |
|---|---|
=> /usr/lib/.../libstdc++.so.6 |
使用系统库 |
=> build-tools/lib64/libc++.so |
使用 build-tools 自带库 |
not found |
找不到依赖 |
检查 libstdc++ 版本
bash
# 查看支持的 GLIBCXX 版本
strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX | tail -5
GLIBCXX 版本对照表:
| GLIBCXX 最高版本 | GCC 版本 | C++17 支持 |
|---|---|---|
| GLIBCXX_3.4.21 | GCC 5.x | ❌ |
| GLIBCXX_3.4.22 | GCC 6.x | ❌ |
| GLIBCXX_3.4.23 | GCC 7.x | ✅ |
| GLIBCXX_3.4.24 | GCC 8.x | ✅ |
| GLIBCXX_3.4.25+ | GCC 9.x+ | ✅ |
C++17 对齐内存分配需要 GCC 7+(GLIBCXX_3.4.23 或更高)。
LD_LIBRARY_PATH 作用分析
什么是 LD_LIBRARY_PATH?
LD_LIBRARY_PATH 是 Linux 动态链接器在运行时搜索共享库(.so)的路径列表。
动态库搜索顺序
1. RPATH/RUNPATH(编译时嵌入二进制的路径)
2. LD_LIBRARY_PATH(环境变量) ← 优先级较高
3. /etc/ld.so.cache(ldconfig 缓存)
4. 默认路径 /lib、/usr/lib 等
问题场景
bash
# CI/CD 环境可能设置了自定义路径
LD_LIBRARY_PATH=/some/custom/path
# 导致 aidl 加载了错误的旧库
aidl → libstdc++.so.6 (GCC 4.8.5) → 缺少 C++17 符号
解决方案
方案一:降级 Build Tools 版本(快速方案)
修改 app/build.gradle:
groovy
android {
buildToolsVersion "29.0.2" // 或 "28.0.3"
}
优点 :改动最小,立即生效
缺点:无法使用新版本特性
方案二:清空 LD_LIBRARY_PATH(推荐)
bash
# 方式一:全局清空
export LD_LIBRARY_PATH=
# 方式二:仅对编译命令生效
LD_LIBRARY_PATH= ./gradlew assembleRelease
# 方式三:脚本内临时修改
OLD_LDLP=$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=
./gradlew assembleRelease
export LD_LIBRARY_PATH=$OLD_LDLP
原理:清空后,动态链接器会按默认顺序查找,可能找到 build-tools 自带的新版库或系统更新的库。
方案三:指定 Build Tools 库路径
bash
export LD_LIBRARY_PATH=/path/to/android-sdk/build-tools/30.0.3/lib64
强制使用 build-tools 自带的 libc++.so。
方案四:升级系统 libstdc++(根治方案)
bash
# CentOS/RHEL
yum install centos-release-scl
yum install devtoolset-7 # 或更高版本
scl enable devtoolset-7 bash
# Ubuntu/Debian
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install gcc-7 g++-7
方案五:确保全流程在 Docker 内执行
修改 CI/CD 配置,确保所有编译步骤都在 Docker 容器内完成,而非直接在宿主机执行。
验证修复
bash
# 1. 验证 aidl 可以正常执行
LD_LIBRARY_PATH= /path/to/android-sdk/build-tools/30.0.3/aidl --version
# 2. 验证库加载路径变化
ldd /path/to/android-sdk/build-tools/30.0.3/aidl
LD_LIBRARY_PATH= ldd /path/to/android-sdk/build-tools/30.0.3/aidl
# 3. 完整编译测试
LD_LIBRARY_PATH= ./gradlew assembleRelease
总结
| 现象 | 原因 |
|---|---|
| Docker 内编译正常 | 容器内有新版 libstdc++ |
| CI/CD 直接编译失败 | 宿主机 GCC 4.8.5 不支持 C++17 |
| Build Tools 29 正常 | 不依赖 C++17 符号 |
| Build Tools 30 报错 | aidl 需要 C++17 对齐内存分配 |
核心问题:Build Tools 30.0.3 的 aidl 工具需要 C++17 运行时支持,老旧系统环境缺少对应符号。
推荐解决 :清空 LD_LIBRARY_PATH 或确保编译全流程在 Docker 容器内执行。