LLD 15.0.4 适配 HarmonyOS PC:从平台识别到 Conan 制品真机验证欢迎加入开源鸿蒙PC社区
Harmony PC 开发者社区
欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper/
欢迎在PC社区平台申请新建项目:
OpenHarmony PC Developer - 开源代码托管,代码协作 - AtomGit
本文记录 LLVM LLD 15.0.4 在 HarmonyOS PC AArch64 上的适配与真机消费者验证。
一、交付结果
LLD 是 LLVM 项目中的高性能链接器。它并非一个独立的小型库,而是 LLVM monorepo 的工具链组件,构建和测试横跨 CMake 平台判断、多个目标后端、lit 测试框架,以及 ELF、COFF、Mach-O、WebAssembly 等测试矩阵。
| 项目 | 内容 |
|---|---|
| 上游项目 | LLVM Project |
| 上游版本 | 15.0.4 |
| 上游地址 | GitHub - llvm/llvm-project: The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. · GitHub |
| 目标平台 | HarmonyOS PC / AArch64 |
| 交付分支 | feat/lld-15.0.4 |
| 合入 PR | MR #9167 |
| 交付提交 | 705d16b8e9a2e1854b8a4bdeb049830b839c605c |

图 1:LLD 15.0.4 适配 PR 已合入,页面同时展示上游信息、测试统计和评审状态。
本文不把“能编译”当作适配完成。一个链接器制品至少要经过四层验证:源码能在目标工具链下构建、上游测试按平台边界分类、Conan 包能从制品仓取得、包内程序能在真机被下游实际调用。后文的五张图片正好对应这四层证据。
二、从零理解 LLD、LLVM 与 Conan 的关系
初次接触这个项目时,最容易误解的一点是:LLD 并不是单独下载一个小仓库后直接 make 的工具。它位于 llvm-project 单仓中,依赖 LLVM 的公共构建框架和工具组件。可以把本次交付拆为三层:
LLVM monorepo 源码
-> CMake/Ninja 构建出 LLD 系列二进制
-> Conan 配方将 bin/ 下程序打包为 lld/15.0.4
-> 下游在 HarmonyOS PC 下载并调用包内 ld.lld
其中,lld 是通用驱动入口,ld.lld 是 GNU 风格链接器入口;还可能包含 lld-link、ld64.lld、wasm-ld 等不同目标格式的入口。本文真机消费者选用 ld.lld,因为它适合验证 AArch64 ELF 链接链路。
对于初学者,建议先记住下面三个概念:
| 概念 | 本文中的含义 | 不能混淆的对象 |
|---|---|---|
| 上游源码 | LLVM 15.0.4 的官方 llvm-project 源码 | Conan 缓存中的已打包制品 |
| SDK 工具 | 系统已安装的 clang、lld 等工具 | 本次 LLD PR 产生的包内二进制 |
| Conan 制品 | ohpcd 远端的 lld/15.0.4 二进制包 | 本地源码构建目录中的临时 ld.lld |
后续验证始终用路径来区分它们:只有 .conan2/p/.../bin/ld.lld 才是本次从制品仓下载的 LLD。
三、为什么这是高难度适配
这次工作不是把 CC 改成 clang 后重新编译即可完成,主要有四类困难:
| 上游假设 | HarmonyOS PC 上的实际差异 | 处理方式 |
|---|---|---|
| 平台识别覆盖既有 Unix 系统 | aarch64:HarmonyOS 未被完整识别 | 补充 config.guess 和 LLVM CMake 平台分类 |
| 测试可使用常规管道行为 | grep -q 提前退出可能使写端收到 SIGPIPE | 使用 FileCheck 代替依赖提前关闭的管道检查 |
| Python 脚本保留 Python 2 除法语义 | Python 3 的 / 返回浮点数 | 将随机边界计算改为 // 整数除法 |
| POSIX 权限位行为稳定 | HMDFS 的 chmod u-w 语义不同 | 将已证实无法稳定复现的权限用例按边界处理,XPASS 仍保留阻断能力 |
具体修改包括:
- 增加 HarmonyOS AArch64 平台识别,使配置阶段获得正确目标信息。
- 在
HandleLLVMOptions中补充CMAKE_SYSTEM_NAME=HarmonyOS分类,使编译和链接选项进入正确的 Unix-like 分支。 - 将 Mach-O dead-strip 测试中的
grep -q管道检查转换为 FileCheck,消除 OHOS 上的管道提前关闭干扰。 - 将
random.randint(0, frame_size / 16 - 4)修正为random.randint(0, frame_size // 16 - 4),解决 Python 3 的TypeError。
3.1 平台识别为什么必须优先解决
大型 CMake 项目不会只在一个位置判断操作系统。config.guess 用于识别构建环境,CMake 的 CMAKE_SYSTEM_NAME 又决定编译选项、链接选项和条件测试。若只修改其中一个位置,可能出现“配置成功但编译参数不完整”或“能构建但测试条件走错分支”的假成功。
本次在两个层面补齐 HarmonyOS:首先让 AArch64 HarmonyOS 环境能够被稳定识别;再让 LLVM 的平台选项逻辑把 HarmonyOS 纳入 Unix-like 分类。这样做的目标不是修改上游行为,而是把原本未覆盖的平台显式纳入已有的合理分支。
3.2 为什么一个 Python 除法也会阻塞链接器适配
LLD 自身主要由 C++ 实现,但测试和辅助生成工具会调用 Python。Python 2 中整数相除返回整数,Python 3 中 / 返回浮点数。因此下面代码在 Python 3 中会把 float 传给需要整数范围的 random.randint:
random.randint(0, frame_size / 16 - 4)
正确的修复不是把异常吞掉,也不是临时降低 Python 版本,而是明确表达整数语义:
random.randint(0, frame_size // 16 - 4)
这类问题的价值在于它可复用:任何历史脚本中“数量、索引、对齐、页数、随机边界”一类的除法,都应先检查它是否依赖 Python 2 整数语义。
3.3 HMDFS 权限差异如何处理
某些上游 LTO 用例通过 chmod u-w 让文件变为不可写,再断言链接器或相关工具产生 Permission denied。这实际验证的是底层文件系统的权限语义,而不完全是 LLD 算法。
HarmonyOS PC 的 HMDFS 环境中,权限位行为与常规 Linux 本地文件系统不完全一致。正确处理流程是先做最小权限实验,再将无法稳定复现的用例单独记录为平台边界;不能将其删除,也不能把“没有触发预期权限错误”伪装成 LLD 功能通过。若以后环境改变导致预期失败意外通过,XPASS 仍应作为回归信号保留。
四、HarmonyOS PC 真机环境
真机不是服务器交叉编译截图。下图可见 HarmonyOS PC 桌面、终端窗口、HarmonyOS 内核标识、aarch64 架构和 Conan 版本。

图 2:HarmonyOS PC 真机环境,uname -a、uname -m 和 conan --version 分别确认系统、AArch64 架构和 Conan 2.29.1。
4.1 最小环境检查
开始前应在真机终端检查以下项目,而不是假定开发机、服务器和真机环境一致:
uname -a
uname -m
conan --version
command -v clang
command -v binary-sign-tool
本次交付对应的包设置是 OHOS / armv8 / Release / clang 15 / C++17 / libc++。这组设置不仅描述编译器版本,也决定 Conan 会选择哪个二进制 package ID。只要 C++ 标准或 C++ 运行库不同,Conan 就会把它当作不同的二进制兼容配置。
4.2 构建阶段的关键约束
LLD 15.0.4 的构建入口是 CMake 和 Ninja。适配时只启用本次需要的 LLD 项目,避免把完整 LLVM 工具链、文档和不相关绑定全部带入构建闭包。配置的核心含义如下:
LLVM_ENABLE_PROJECTS=lld 只构建 LLD 项目
LLVM_INCLUDE_TESTS=ON 保留 LLD lit 测试入口
LLVM_BUILD_TESTS=OFF 不额外构建 LLVM C++ 单元测试目标
LLVM_DEFAULT_TARGET_TRIPLE=... 设定 OHOS AArch64 默认目标
LLVM_PARALLEL_LINK_JOBS=2 限制大对象链接时的内存峰值
LLVM_BUILD_TESTS=OFF 并不表示“没有测试”。LLD 的主验证入口是 check-lld 对应的 lit 测试,两者属于不同层次。把它们混为一谈,是解释 LLVM 测试结果时最常见的错误之一。
五、从 ohpcd 制品仓下载 LLD 制品
先在真机下载已经发布的 LLD 制品,而不是运行 SDK 目录中同名的 ld.lld:
conan download "lld/15.0.4:*" -r=ohpcd
下载成功后,Conan 返回完整的 recipe revision、package ID 和 package revision。该制品的目标设置为 os=OHOS、arch=armv8、compiler=clang、compiler.version=15、compiler.cppstd=17、compiler.libcxx=libc++。
conan cache path "lld/15.0.4#<recipe_revision>:<package_id>#<package_revision>"

图 3:真机从 ohpcd 下载 86.1 MB 的 LLD 制品,包安装完成后定位到本地 Conan 缓存。
5.1 如何读懂下载输出
下载输出中有三段标识,建议在问题排查时完整保留:
lld/15.0.4#<recipe_revision>
:<package_id>
#<package_revision>
recipe_revision标识配方版本;package_id由 profile 的设置和选项计算得到;package_revision标识该二进制包的修订版本。
图 3 中的包 ID 是 6b129b068851896169fae42d399fa922d239ce8a。它不是随意的缓存目录名,而是与 OHOS AArch64、clang 15、C++17、libc++ 配置绑定的二进制身份。
5.2 为什么必须用 download 而不是只看仓库网页
网页只能说明项目存在,不能说明当前 HarmonyOS PC 能否拿到目标架构和 ABI 对应的包。conan download 真实执行了远端获取、包解压和本地安装;图 3 中的 Package installed、下载体积和本地缓存路径共同证明制品可获得。
如果命令报 Recipe not found,说明远端未发布配方;如果显示 Missing binary,说明配方存在但当前 profile 没有匹配包。两类失败的处理方向完全不同,不能混在一起。
六、以消费者运行环境调用制品中的 LLD
下载成功不等于消费者可用。必须使用与制品一致的 host settings 生成 Conan 运行环境,避免默认 profile 选择到不同的 C++ ABI 包:
conan install --requires=lld/15.0.4 -r=ohpcd --build=never \
-s:h=arch=armv8 \
-s:h=build_type=Release \
-s:h=compiler=clang \
-s:h=compiler.version=15 \
-s:h=compiler.cppstd=17 \
-s:h=compiler.libcxx=libc++ \
-s:h=os=OHOS \
-s:h=os.version=6.0 \
-g VirtualRunEnv -of=.
. ./conanrun.sh
command -v ld.lld
ld.lld --version
关键检查是 command -v ld.lld 必须指向 .conan2/p/.../bin/ld.lld,而不是 SDK 的 /data/service/hnp/bin/ld.lld。

图 4:VirtualRunEnv 生成成功,ld.lld 指向 Conan 缓存内的制品并正确输出 LLD 15.0.4。
6.1 这一步曾遇到的真实失败
直接执行 conan install --requires=lld/15.0.4 时,真机的默认 profile 使用了 compiler.cppstd=gnu14 和 compiler.libcxx=libstdc++11。Conan 因此计算出一个不同的 package ID,并提示 No compatible configuration found。
这不是制品仓下载失败,也不是 LLD 不能在鸿蒙上运行,而是消费者请求的 ABI 与已发布制品不一致。显式传入 cppstd=17 与 libc++ 后,Conan 命中图 3 下载的包,生成 conanrun.sh,随后 ld.lld --version 成功输出 LLD 15.0.4。
这个案例说明:遇到 Missing binary 时,先对比“远端包的 settings”和“当前 profile 的 settings”,不要立刻使用 --build=missing。后者会把本应验证远端制品的问题变成一次本地重编译,失去制品消费验证的意义。
6.2 如何确认没有误用 SDK 自带 LLD
设备 SDK 同时提供 /data/service/hnp/bin/ld.lld。一开始若只运行 command -v ld.lld,很容易命中 SDK 程序;该程序还可能因自己的动态库环境而无法加载 libxml2.so.16。这既不能证明本 PR 的制品正确,也不能作为本次 LLD 的失败结论。
正确顺序是:先执行 . ./conanrun.sh,再检查 command -v ld.lld;只有路径落在 Conan 缓存中的包目录,才继续运行版本命令和消费者链接。
七、最小消费者链接、签名和真机运行
为证明制品中的 LLD 确实参与了下游链接,创建最小 C 消费者程序,并将 Conan 包内的 ld.lld 绝对路径显式传给 clang:
LLD_PKG="$(conan cache path "lld/15.0.4#<recipe_revision>:<package_id>#<package_revision>")"
LLD_BIN="$LLD_PKG/bin/ld.lld"
clang -fuse-ld="$LLD_BIN" ./lld_consumer.c -o ./lld_consumer
/data/service/hnp/bin/readelf -h ./lld_consumer | grep -E 'Class:|Machine:'
/data/service/hnp/bin/binary-sign-tool sign \
-inFile ./lld_consumer \
-outFile ./lld_consumer \
-selfSign 1
./lld_consumer
printf 'consumer_exit=%s\n' "$?"
这里有一个容易踩到的环境问题:SDK clang 在仅使用 -fuse-ld=lld 时,可能优先调用 SDK 目录自身的链接器,而不是当前 PATH 中的 Conan 制品。因此本验证使用 -fuse-ld="$LLD_BIN" 显式锁定包内链接器。

图 5:消费者程序由 Conan 制品内的 ld.lld 链接,readelf 确认 ELF64 / AArch64;自签名成功后输出 LLD Conan consumer: PASS,退出码为 0。
7.1 消费者源码为什么要足够小
最小消费者只调用 puts 并返回 0。它不用于证明复杂 C 运行库功能,而是为了把结论收敛到一条可观察链路:C 源码被 clang 编译,clang 把链接阶段交给指定的 Conan ld.lld,最终产物是 AArch64 ELF,经签名后能在真机运行。
#include <stdio.h>
int main(void) {
puts("LLD Conan consumer: PASS");
return 0;
}
使用最小输入的好处是,若失败可以立即区分为“编译器/链接器/系统运行时/签名”哪一层的问题,而不会被业务逻辑、第三方库或网络条件干扰。
7.2 为什么传递绝对 linker 路径
clang -fuse-ld=lld 看起来合理,但 SDK clang 的工具查找逻辑可能优先定位 SDK 安装目录中的 lld,即使当前 shell 的 PATH 已经由 Conan 更新。实际验证中这会命中 SDK 自带的、运行时依赖不完整的 linker。
因此使用:
clang -fuse-ld="$LLD_BIN" ./lld_consumer.c -o ./lld_consumer
$LLD_BIN 是通过完整 Conan 包引用计算出的 .../.conan2/p/.../bin/ld.lld。这使链接器来源可审计,也避免同名工具覆盖。
7.3 为什么签名和运行缺一不可
readelf -h 只能证明文件是 ELF64 / AArch64,还不能证明它满足设备的执行要求。HarmonyOS PC 上,ELF 需要通过 binary-sign-tool 进行自签名。图 5 中连续出现“添加 codesign section 成功”“写入签名数据成功”“消费者输出 PASS”“退出码为 0”,因此它覆盖了链接、架构、签名和运行四个阶段。
八、上游测试口径与边界
LLD 的上游测试不是单一平台的简单总数。lld/test 同时包含 Windows、macOS、PPC64 等目标和系统专属测试;OHOS AArch64 的结果不能替代这些平台语义。
MR #9167 的测试台账以独立 lit 用例为中心,源码目录审查口径为 2690 项,其中 Inputs 等夹具文件不是独立断言。文章中应始终区分:上游测试发现数、当前配置实际实例化数、实际运行通过数、平台阻塞项和预期失败项;不能将可执行集合的成功表述为“所有上游平台测试全部通过”。
典型平台边界包括 Windows DIA/PDB、system-windows、macOS xar/system-darwin、PPC64 TOC 指令语义,以及 HMDFS 权限位行为。这些项目应保留首错、适用平台和恢复条件,而不是改写为通过。
8.1 为什么不能只报一个百分比
LLD 的测试目录含有测试输入、夹具、目标格式专属用例和真实断言。下面四个数字表达的不是同一件事:
| 口径 | 应回答的问题 |
|---|---|
| 源码发现数 | 上游目录里有多少可能的测试文件或入口? |
| 独立用例数 | 哪些项目真正拥有独立 RUN 行或断言? |
| OHOS 实际运行数 | 当前配置在 AArch64/OHOS 上注册并执行了多少? |
| 平台阻塞数 | 哪些用例需要 Windows、macOS、PPC64 或特定工具才能恢复? |
MR 台账采用 2690 项上游目录审查口径,并单独记录 2679 项独立实例化用例。文章刻意不把跨平台阻塞项从分母中抹掉,也不把某个子集合成功替换成“上游全量成功”。这是工具链项目比普通库更需要遵守的测试表达纪律。
8.2 测试失败的标准排查顺序
出现 lit 失败时,建议按以下顺序定位:
- 先确认测试实际使用的是本次构建产生的工具,而不是系统同名工具;
- 保存完整 RUN 命令、首个失败断言、退出码和临时目录;
- 判断失败是目标平台差异、缺少外部工具、文件系统语义,还是 LLD 输出本身变化;
- 对平台阻塞项写出恢复条件,例如“需要 Windows DIA SDK”或“需要 PPC64 后端语义”;
- 只有在根因确定且修复可复现后,才修改补丁或测试期望。
这种顺序比“先把失败跳过”更慢一点,但能防止把真实兼容性缺陷误归类为环境问题。
九、可复用流程清单
对于后续需要发布 Conan 工具型制品的 HarmonyOS PC 适配,可以复用以下流程:
- 固定官方源码版本、下载地址和 SHA-256。
- 枚举构建入口、测试入口和平台判断文件,先完成平台识别。
- 明确区分“构建 LLVM 单元测试”和“运行 LLD lit 测试”。
- 以 Conan 配方打包工具,并保留
bin/中真实可执行程序。 - 在真机执行
conan download,确认远端确实存在目标 ABI 的二进制包。 - 用与制品一致的 profile 执行
conan install --build=never,防止隐式本地重建掩盖问题。 - 通过
VirtualRunEnv和绝对路径确认消费者使用的确实是包内工具。 - 用最小消费者完成编译、链接、AArch64 ELF 检查、签名和运行。
- 在文章中分别呈现 PR、真机、下载、包内执行和消费者运行证据。
十、结论
LLD 15.0.4 的适配覆盖了 HarmonyOS 平台识别、LLVM CMake 分类、Python 3 脚本兼容、HMDFS 权限测试边界和跨平台测试矩阵处理。
真机侧形成了完整的可复核链路:
HarmonyOS PC AArch64
-> 从 ohpcd 下载 lld/15.0.4 制品
-> Conan VirtualRunEnv 解析包内 ld.lld
-> 显式使用该 ld.lld 链接消费者
-> ELF64/AArch64 校验
-> binary-sign-tool 自签名
-> 真机运行并返回 0
十一、FAQ
Q1:为什么不能直接运行 command -v ld.lld?
系统 SDK 也可能提供同名链接器。必须先确认路径指向 .conan2/p/.../bin/ld.lld,否则无法证明运行的是本次下载的 Conan 制品。
Q2:为什么 conan install 会提示没有兼容包?
Conan 二进制包由 OS、架构、编译器版本、C++ 标准和 C++ 运行库等共同确定。本制品为 cppstd=17 与 libc++,消费者 profile 若使用 gnu14 或 libstdc++11,会计算出另一个 package ID,需要显式对齐设置。
Q3:为什么链接后还要签名?
HarmonyOS PC 对 ELF 运行有签名要求。签名成功并在真机运行,证明的不只是“链接器生成了文件”,而是该文件能够作为实际 ELF 交付物运行。
Q4:为什么 clang 要传入 LLD 的绝对路径?
SDK clang 可能优先选择自身安装目录下的 linker。-fuse-ld="$LLD_BIN" 将链接动作固定到 Conan 制品中的 LLD,避免同名 SDK 工具干扰验证结论。
Q5:conan download 成功后,为什么 conan install 仍可能失败?
download 能证明某个包存在并已经被下载;install 会根据当前 profile 重新计算需要的 package ID。若编译器版本、cppstd、libcxx、架构或操作系统版本不同,就可能下载到了 A 包却请求 B 包。应按图 3 的 settings 显式对齐 profile。
Q6:能否使用 --build=missing 解决缺包?
可以用于开发阶段构建源码,但不能用于“验证远端制品仓已发布并可消费”的截图。本文的真机消费环节使用 --build=never,一旦远端没有匹配包就立即报错,结论更可信。
Q7:为什么图 5 中既使用 clang 又使用 LLD?
clang 负责 C 源码编译、sysroot 和启动文件选择;LLD 负责最终链接。-fuse-ld="$LLD_BIN" 保留 clang 驱动的目标平台配置,同时把关键链接步骤明确交给 Conan 包内的 LLD。这是验证链接器制品比手工拼接 crt*.o、动态链接器和 libc 参数更稳妥的方式。
更多推荐




所有评论(0)