鸿蒙PC适配实战:binutils的构建、测试与排错
欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
引言
这次将 GNU binutils 2.46.0 适配到鸿蒙 PC,在 AArch64 环境中完成了原生构建、Conan 打包和安装包测试。过程中主要处理了构建脚本兼容、Clang 与 GNU as 配合、DejaGNU 测试运行和 ELF 签名问题。PR !9025 已于 2026 年 9 月 10 日合入。
最终五套 DejaGNU 测试得到 3419 PASS、35 FAIL、0 UNRESOLVED,普通通过率 98.99%;安装包功能测试为 16/16。下面介绍环境准备、构建命令、适配过程和剩余失败项的分析。上游测试数字来自 2026 年 9 月 8 日归档的 final39 真机记录;9 月 26 日另在原设备重新执行安装包测试,结果仍为 16/16,运行截图见第 8 节。
1. binutils 包含哪些工具
binutils 是一组处理二进制文件的工具,常见用法如下:
汇编源码 → as → 目标文件 → ld → 可执行文件
↓
readelf / objdump / nm:查看结构、指令和符号
ar:建立静态归档;objcopy / strip:处理文件副本
其中,as 是汇编器,ld 是链接器;ELF 是本次目标文件、动态库和可执行文件使用的文件格式。Conan 按配方获取源码、安装依赖、构建和打包;test_package 用来运行包内工具,检查安装后的功能。这类直接使用已安装工具的测试,下文称为“消费者测试”。
本项目按高难度挑战申报,Issue #2846 记录了技术依据:binutils 包含 BFD、opcodes、gas、ld 等相互配合的组件,涉及多架构汇编、重定位、符号版本和动态加载。源码归档中有 8815 个 .s 文件和 7 个 .asm 文件;这些数量描述了源码与测试规模,其中也包含其他架构的测试材料。
本次使用鸿蒙环境中的 Clang 编译 binutils,再调用生成的 GNU as、GNU ld 等工具执行测试。运行时 Conan 依赖列表为空;DejaGNU、Expect、Tcl 属于测试阶段的工具依赖。
2. 开发环境与设备配置
本次使用 Windows 和 WSL Ubuntu 做源码整理及对照试验,最终构建与验收在鸿蒙 PC 上执行。
| 项目 | 本次记录 |
|---|---|
| 目标设备 | HarmonyOS PC,AArch64 |
| 本次截图复验的系统 | 实测参数 OpenHarmony-6.1.0.115(2026-09-26) |
| Conan 目标配置 | os=OHOS、os.version=6.0、arch=armv8 |
| 编译器配置 | Clang 15、libc++、Release |
| SDK 路径记录 | ohos-sdk_26.0.0.18/ohos/native |
| 包管理器 | 社区 OHOS 适配版 Conan 2.29.1 |
| 测试工具 | DejaGNU 1.6.3.1、Expect 5.45.4、Tcl 8.6.14 |
| 上游版本 | binutils 2.46.0,tag 为 binutils-2_46 |
| 本次适配提交 | 62bd363f7a8ef6e60f5fec0393fc01c868eaa5cf |
Conan profile 是指定操作系统、架构、编译器和环境变量的配置文件。表中的 6.0 是 profile 的配置值;复现时还应记录设备“关于本机”中的完整系统版本和实际 SDK 版本。Conan 的 armv8 在这里对应 AArch64,它与终端中 uname -m 输出的 aarch64 表示同一目标架构。
首次准备设备,可以从官方仓库 README 的部署入口和环境初始化脚本 了解所需工具。本次复现的前提是设备已经具备 HNP 命令行环境、Git、Python、make、Clang 和 OHOS 适配版 Conan。普通桌面平台上的同版本 Conan,配置行为可能不同。
在鸿蒙 PC 终端进入 Bash 后,先检查工具:
/data/service/hnp/bin/bash
export PATH="/data/service/hnp/bin:/system/bin:$HOME/.local/bin:$PATH"
export CONFIG_SHELL=/data/service/hnp/bin/bash
export SHELL="$CONFIG_SHELL"
uname -m
conan --version
python3 --version
/data/service/hnp/bin/clang --version
command -v git make
test -x /data/service/hnp/bin/binary-sign-tool
test -x /data/service/hnp/bin/llvm-objcopy
应能看到 AArch64 架构及各工具版本,两个 test 命令成功时返回 0。后续原生程序测试会使用签名工具和 llvm-objcopy,因此这两项也要提前检查。
3. 适配过程
核对源码,确定要运行的测试
先固定 binutils 2.46.0 的上游 tag、源码归档和 SHA256,再检查构建系统、组件及测试入口。binutils 使用 Autotools + Make 构建,使用 DejaGNU/Expect 测试。上游 config.sub 已接受 OHOS,config.guess 还需要补充系统名识别。
本次验收检查六项内容:AArch64 汇编、链接、ELF 检查、静态归档、objcopy/strip 变换后执行,以及启用的上游测试驱动完整结束。gprofng 等受限组件也在这一阶段记录,后续在报告中说明关闭原因。
开始编写配方前,先整理源码、依赖和测试要求。本次记录如下,适配其他项目时也可以逐项检查:
| 调查项 | 本次记录 | 后续处理 |
|---|---|---|
| 源码版本 | 固定 tag 与源码哈希 | 各轮实验使用同一份源码 |
| 交付产物 | 鸿蒙 PC 原生二进制工具包 | 用包内工具汇编、链接并检查 ELF |
| 构建与测试工具 | Autotools/Make、DejaGNU/Expect | 检查各工具及其依赖是否齐全 |
| 已有平台支持 | 已有 OHOS triplet、平台知识条目 | 在当前版本验证已有方案,补充缺少的修改 |
| 运行检查 | 正常输入的输出、错误输入的退出码、原生程序执行 | 在消费者脚本中加入对应断言 |
源码地址和哈希记入 conandata.yml,依赖和构建步骤写入 Conan 配方;test_package/MANIFEST.yml 记录测试文件、执行命令和断言数量,实际运行结果保存在日志和验证报告中。
在 WSL 上做工具链对照
本次先在 WSL 做 Clang/工具链对照,并运行上游测试。主机结果为 5571 PASS、83 FAIL、1 UNRESOLVED,消费者为 13/13。后续遇到编译器诊断、调试信息等问题时,可以查这些日志,确认 WSL 上是否有同样的失败。主机与 AArch64 设备启用的用例不同,两个平台的通过率不适合直接比较高低。
随后把源码版本信息、配方、补丁、profile 和测试脚本打成设备文件包,在鸿蒙 PC 原生构建。文件传入设备后,按清单核对哈希,确认设备使用的是本轮修改后的文件。最终的 r6 文件包共核验了 46 个文件。
每轮运行都记录源码版本、编译器版本、完整命令、退出码、标准输出和标准错误。失败日志同样保留,修改后可以对照具体报错是否消失。
用小例子复现错误
遇到错误时,先从日志判断出错阶段:
源码获取 → configure → 编译 → 链接 → 测试启动 → 程序运行 → 结果比较
例如,临时目录创建失败时检查配置脚本和文件系统;Expect 启动报错时检查测试依赖和进程交互方式;程序输出与预期不同时,检查程序行为和比较规则。导致测试驱动中断的问题要先处理,待驱动运行完整后再统计结果。
排查 .addrsig 指令错误时,测试代码只有 int fixture(void) { return 1; }。用同一 Clang 分别生成默认汇编和加入 -fno-addrsig 的汇编,再交给同一 GNU as,比较退出码与 stderr,检查加上该选项后指令错误是否消失。这个实验只需编译一个函数。
排查原生程序执行错误时,将同一个 hello.c 分别交给 SDK 默认链接器和本轮 GNU ld 链接,检查两个 ELF 的段、节和执行结果,随后按平台流程签名,再次运行。后续分析 preinit_array、符号绑定等问题时也采用 LLD/GNU ld 对照,两组产物使用相同的签名步骤。
做这类对照时,要保留原用例的编译与链接参数。漏掉 -O3、PIE、符号可见性或 DSO 替换步骤,小例子的行为就可能与原用例不同。本次后续补充了 O3、libc++ 和重定位场景的对照实验。
修复后重跑相关套件
系统识别、HMDFS 临时目录、DejaGNU 传输和脚本解释器等问题参考了仓库里的已有处理方法,并在 binutils 2.46.0 上逐项验证。新增的 .addrsig 问题也保留了最小复现代码和编译输出。
补齐 SFrame 只读夹具复制和 ld 日志参数处理后,先重跑对应套件;libctf 明确选择 GNU ld 后,定向运行得到 18 PASS,原来的驱动错误消失。修改保存到补丁和配方中,下一次从源码重新构建时自动应用。
本次还为适配逻辑、签名辅助程序和复验脚本保留了回归检查。binutils 的工具功能由上游测试和安装包消费者测试验证。
完整回归中的几次调整
定向测试通过后,再运行完整套件,检查其他测试是否出现新的失败。下表是几次真机运行的结果和后续处理:
| 阶段 | 观察到的结果 | 后续处理 |
|---|---|---|
| r4 | 3223 PASS、193 FAIL、1 UNRESOLVED | 继续定位夹具、测试传输和运行部署问题 |
| final20 | 3393 PASS、60 FAIL | 定位压缩调试测试的汇编器选择及 dlopen 夹具漏签 |
| final34 | 3425 PASS、36 FAIL | 全局外部汇编器引入 mbind2a/b 两项新失败,收窄修改范围 |
| final36 | 设备会话中断,退出码 130 | 记录为中断运行,另起完整运行 |
| final39 | 3419 PASS、35 FAIL、0 UNRESOLVED | 作为本次最终测试记录 |
各轮启用的用例有所变化。final34 到 final39 之间,部分驱动恢复了原来的能力探测状态,PASS 总数随之变化。因此比较两轮结果时,还要核对失败名称、实际执行的驱动,以及修改前的测试状态。
最终 final39 使用新的 Conan 缓存,从固定版本的源码、配方和补丁重新构建测试依赖及 binutils,运行五套 DejaGNU 测试、libiberty 检查与安装包消费者。归档的 138 个结果文件回传后,逐项核对大小和哈希。同一份配方、补丁、测试和说明随后提交到官方任务分支,通过 PR 检查并合入。
GNU ld 程序执行失败的排查记录
下面以 GNU ld 链接后的程序执行返回 126 为例,列出当时的排查记录:
| 项目 | GNU ld 原生程序执行案例 |
|---|---|
| 触发条件 | 同一源码经 GNU ld 链接后在鸿蒙设备执行 |
| 原始现象 | 链接完成,执行返回 126 |
| 待验证假设 | 产物部署环节缺少平台签名 |
| 最小实验 | 对比 SDK 默认链接器与 GNU ld 产物的 ELF 信息及执行状态 |
| 单项修改 | 对 GNU ld 产物按平台流程签名并确认执行权限 |
| 观察结果 | 同一程序随后执行成功,退出码和 stdout 均留存 |
| 修改位置 | 测试签名辅助程序与 native-test.py |
| 回归范围 | 主程序、本地动态库、明确的 dlopen 夹具,以及 objcopy/strip 产物 |
排查记录还应附上完整命令、退出码和 stdout/stderr,方便按相同参数重新运行。表中只列主要结果,原始输出单独保存。
4. 用公开配方复现构建
为对应本文记录,先取固定提交。以下命令在鸿蒙 PC 的 Bash 中执行,选一个用于复现的新目录:
git clone https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos.git binutils-2460-article
cd binutils-2460-article
git checkout --detach 62bd363f7a8ef6e60f5fec0393fc01c868eaa5cf
RECIPE="$PWD/archives/b/binutils/2.46.0"
PROFILE="$PWD/ci/conan/profiles/ohos-aarch64"
cat "$PROFILE"
配方目录里,conandata.yml 固定上游源码地址和 SHA256,conanfile.py 组织构建与验证,patches/ 保存 25 个补丁,test_package/ 保存安装包测试。Conan 会自动应用补丁。复现前检查 profile 中编译器及 SDK 动态库目录与本机一致;目录有差异时,复制一份 profile,调整路径并让 PROFILE 指向该文件。
随后使用独立缓存保存这次实验,避免旧制品干扰判断:
set -o pipefail
RUN_ROOT="$PWD/article-run-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$RUN_ROOT/tmp"
export CONAN_HOME="$RUN_ROOT/conan-home"
export TMPDIR="$RUN_ROOT/tmp"
export MAKEFLAGS=-j1
conan remote add ohpcd \
https://conan.cnb.cool/OpenHarmonyPCDeveloper/Conan/-/packages/
conan profile show -pr:h "$PROFILE" -pr:b "$PROFILE"
conan create "$RECIPE" -tf "$RECIPE/test_package" \
-pr:h "$PROFILE" -pr:b "$PROFILE" \
--build=missing --build="binutils/*" \
2>&1 | tee "$RUN_ROOT/conan-create.log"
build_rc=${PIPESTATUS[0]}
printf 'conan create exit=%s\n' "$build_rc"
这里 host 和 build 都使用鸿蒙 profile,因为是在目标设备上原生构建。--build="binutils/*" 指定重新构建 binutils,--build=missing 允许补建缺少的依赖包。pipefail 与 PIPESTATUS 用来保留构建进程的真实退出码,便于识别日志管道里的失败。
本次归档运行使用独立缓存,conan create 返回 0,任务耗时约 44 分 45 秒。这个耗时对应当时的设备和缓存条件。归档运行从设备文件包启动;上面的命令使用同一提交下的公开配方目录,执行时应保存自己的新日志。后文数字仍引用原始 final39 记录。
5. 构建脚本与编译选项
平台识别和临时目录
Autotools 通过 config.guess 判断构建平台。本次在脚本里补充 HarmonyOS、OpenHarmony、OHOS 的系统名分支,AArch64 对应的识别结果为 aarch64-unknown-linux-ohos。同版本 config.sub 已接受 linux-ohos,因此最终方案保留了它的原有实现。
本次在 HMDFS 上还遇到了 config.status 创建临时目录失败的问题。最终补丁调整了 14 个生成的 configure 脚本中临时目录的 umask,从 077 调整为 022,同时为脚本入口选择已配置的 Bash。
这类报错发生在编译之前。排查时先看失败的是目录创建、平台探测还是 C/C++ 编译,再决定修改位置。相关补丁保存在项目目录内,复现时由配方应用;其他项目采用同一办法前,需要先验证自己的文件系统行为。
Clang 与 GNU as 的 .addrsig 问题
Clang 输出的文本汇编包含 LLVM 的 .addrsig 相关指令,本次交给 GNU as 处理时出现了指令错误。配方为 Clang 的 C/C++ 编译标志追加 -fno-addrsig,让 Clang 省略这部分汇编元数据。
最终配方 中的代码为:
if str(self.settings.compiler) == "clang":
toolchain.extra_cflags.append("-fno-addrsig")
toolchain.extra_cxxflags.append("-fno-addrsig")
这个片段属于 Conan 配方的 generate() 方法,完整上下文以链接中的文件为准。它解决的是本次 Clang 与 GNU as 的衔接问题,采用其他编译器或版本组合时,应先确认实际输出和报错。
6. DejaGNU 测试与工具选择
DejaGNU 与终端环境
binutils 大量测试由 DejaGNU 组织,Expect 负责启动进程和读取输出。原有运行方式依赖伪终端,简称 PTY。本次设备环境需要使用 pipe 或文件通道完成相应交互。
以 gas 测试读取 gas.out 为例,补丁 0019 把读取方式调整为:
spawn -open [open gas.out r]
这样仍然读取汇编器输出,由原来的比较逻辑给出结果。其他修复还包括:用普通文件复制部署只读测试夹具、通过指定 shell 执行辅助脚本,以及给 send_log 加 --,让负退出状态按数据写入日志。
在运行长测试前,配方先执行传输预检,完成 5 项检查和 256 次子进程循环。这些预检结果单独记录,帮助判断进程启动与输出读取是否可靠。
检查 Clang 实际调用的汇编器
压缩调试信息测试中,Clang 收到了指向测试工具目录的 -B 参数,却仍使用自身的集成汇编器,测试未调用预期的 GNU as。
本次通过 -fno-integrated-as 让相关测试调用 GNU as,并明确选择本轮生成的 GNU ld。final34 曾把外部汇编器开关扩大到全部 ld 测试,导致 mbind2a/b 出现新的失败。
最终补丁 0025 将这个开关限定在 compress.exp 运行期间:进入前保存 C/C++ 标志,执行相关测试,结束时恢复原值。最终压缩调试组为 28/28 PASS,mbind2a/b 回到基线的 UNSUPPORTED 状态。
-fno-addrsig 控制元数据生成,-fno-integrated-as 控制汇编器选择。调整参数后,需要核对实际调用的工具,并重跑受影响的测试驱动。
7. ELF 签名与原生程序执行
GNU ld 生成的程序还需要完成鸿蒙平台的签名部署,随后才能验证其运行行为。排查记录中出现过未签名产物执行返回 126 的现象;退出码 126 本身并不唯一对应签名问题,还要结合文件权限、架构、加载器和签名记录定位。
本次增加了测试签名辅助程序。它只处理当前测试套件 tmpdir 范围内符合条件的 AArch64 ELF,并处理程序本地的 DT_NEEDED 依赖。DT_NEEDED 可以理解为 ELF 记录的动态库需求列表。
部分测试还会用 dlopen 在运行过程中加载动态库,这些库未必出现在主程序的 DT_NEEDED 中。例如 pr21964-2b.so 就需要作为明确的测试夹具纳入签名范围。补齐后,pr21964-2 测试通过。
objcopy 和 strip 会改变 ELF 文件,因此安装包测试在每次变换后重新签名,再执行程序。包含自定义外部 RPATH/RUNPATH 的应用,还需要按自身部署方式验证依赖查找和签名过程。
8. 安装包测试与真机复验
上游测试主要验证构建目录里的工具。交付时,还需要检查 Conan 包内的工具、测试输入和运行环境是否齐全。
本次通用消费者脚本 用 as 汇编夹具,再用 readelf、objdump、nm 检查格式和符号,用 ar 创建归档,用 ld -r 链接目标文件,最后用 strip 处理副本。它还故意读取一个不存在的文件,检查工具返回非零值,并在标准错误中包含缺失路径和原因。该流程共有 13 个断言。
原生消费者脚本 则验证 GNU ld 链接后的程序、objcopy 副本和 strip 产物,三个程序签名后均在真机输出:
binutils native consumer: 2460
这部分是 3 个运行断言,两类合计 16/16。运行 conan create 时会自动执行这些入口;只想复验安装包时,可以使用 conan test,传入自己的包引用和本次 profile。
2026 年 9 月 26 日,在原设备保留的安装包上重新运行相同消费者脚本,输入文件的 SHA256 与最终提交一致。此次实测系统参数为 OpenHarmony-6.1.0.115,编译器为 Clang 15.0.4。通用断言 13/13、原生运行断言 3/3 均通过,三个原生程序均输出上述字符串。

图 1:2026 年 9 月 26 日通过 HDC 获取的 3120×2080 完整屏幕,保留终端窗口和系统任务栏。终端显示当次消费者测试的运行汇总,原始 stdout/stderr 另行保存。
9. 测试结果与剩余失败
本次五套 DejaGNU 汇总如下,数字来自原始 .sum 文件:
| 套件 | PASS | FAIL | XFAIL | UNSUPPORTED | UNTESTED |
|---|---|---|---|---|---|
| binutils | 273 | 1 | 2 | 8 | 2 |
| gas | 1239 | 0 | 0 | 10 | 0 |
| ld | 1721 | 34 | 13 | 182 | 2 |
| libctf | 18 | 0 | 0 | 3 | 0 |
| libsframe | 168 | 0 | 0 | 0 | 0 |
| 合计 | 3419 | 35 | 15 | 203 | 4 |
五套汇总的 UNRESOLVED 和 XPASS 均为 0。普通通过率按 PASS / (PASS + FAIL + UNRESOLVED) 计算,即 3419 / 3454 = 98.99%。XFAIL 是预期失败,UNSUPPORTED 表示当前配置不支持相应用例,UNTESTED 表示未完成测试;三者都单独保留。
上游共有 386 个 .exp 文件,其中 7 个属于关闭的 gprofng 组件;启用的 379 个文件包含 367 个执行驱动和 12 个支持文件。一个驱动会产生多个测试结果,文件数、驱动数与 PASS 数各自统计。传输预检的 5 项检查也未并入上游五套汇总。
另外,libiberty 的检查为 930/930,包括 860 个符号解码用例、69 项输出检查和 1 项进程执行检查。gprof 因 gprof_cv_sys_native=no,该环境实际发现和执行数量均为 0;gprofng 的 Linux 专用采集器在配置中显式关闭。配方也显式关闭了 NLS,并使用 --without-zstd。
35 个失败项的分析
失败分析报告 保留了完整名称,并记录 15 类根因。以下是其中几项的对照结果:
- 有用例要求 ELF 携带 glibc 的
GLIBC_ABI_DT_RELR版本需求,而本次目标使用 musl 环境,预期条件存在平台差异。 preinit_array场景用同一源码分别经 SDK LLD 和 GNU ld 链接、签名,两种产物出现一致的运行现象,相关证据指向目标加载器行为。- 精确诊断行号的部分用例在 WSL Clang 对照中也失败,需要结合编译器调试信息分析。
- AArch64 PIE 重定位的部分场景中,SDK LLD 与 GNU ld 结果确有差异,使用这些重定位的程序需要单独验证。
归档报告结合日志与对照实验,未发现最终适配补丁引入的保留失败。这个结论限定于已测试场景,35 个 FAIL 仍是实际结果。特定项目如果依赖其中的符号版本、加载顺序或重定位行为,应增加自己的验证。

图 2:2026 年 9 月 26 日在设备上读取 final39 原始日志,并由 HDC 获取完整屏幕。画面明确标注历史记录;上游全量测试对应 9 月 8 日的运行。设备日志 SHA256 与本地归档一致。
10. 代码合入与发布检查
本次交付包含 Conan 配方、25 个补丁、安装包测试、运行签名辅助程序和失败分析。PR 检查 #42539 通过;2026 年 9 月 10 日 PR 合入后,发布流水线 #6203 的公开回执记录了构建测试、制品上传和制品签名通过。
11. 常见问题
Windows 或 WSL 编译成功,可以作为鸿蒙端成功的证据吗?
它们可以提供源码、编译器和测试逻辑的对照。本次最终结论来自 HarmonyOS PC 原生构建与执行,主机结果在验证材料中单列。
为什么使用 -j1?
本次 HMDFS 环境对 GNU make 的 FIFO jobserver 路径有兼容问题,配方采用串行构建与测试。换到其他环境后,可以在确认文件系统和调度行为的基础上评估并行设置。
Conan 提示 OHOS 不是有效的系统配置,先查什么?
先核对是否使用社区 OHOS 适配版 Conan,再检查当前 CONAN_HOME 的配置是否来自旧版本。独立实验缓存有助于排除历史设置影响,具体配置参照官方初始化脚本。
HDC 已连接,为什么它的 shell 看不到 HNP 工具目录?
本次设备上,HDC shell 与 HiShell 的目录视图不同:HDC 使用 /storage/media/100/local/files/Docs/ 传输文件,HiShell 使用对应的 /storage/Users/currentUser/ 路径并运行 HNP 工具。因此本次由 HDC 传入脚本,在独立 HiShell 标签页执行,再由 HDC 截图和取回日志。遇到相同情况时,先核对当前 shell 的身份和目录视图,再查工具路径。
make check 返回 2,为什么最终 conan create 返回 0?
原始 make check 保留了 35 个失败项,因此返回 2。配方另设验收条件:五套汇总文件齐全、367 个驱动按清单执行、未发生指定的驱动中断错误,且普通通过率至少为 90%;此外还检查 gprof、libiberty 和运行预检结果,最后执行安装包测试。本次通过了这些检查,因此 conan create 返回 0。复现时要同时查看原始日志和失败分析,35 个上游失败项仍保留在结果中。
UNSUPPORTED 可以算成通过吗?
统计时单列,并保留它产生的条件。此次收窄 compress.exp 的汇编器开关后,相关驱动恢复了基线状态;报告据实记录。
新手从哪里读源码最有效?
建议先看 test_package/test.sh 中的汇编、链接和文件检查命令,再看 conanfile.py 如何构建和运行上游测试。遇到具体报错时,查相应补丁和失败分析中的复现记录。
更多推荐



所有评论(0)