☰
鸿蒙PC适配实战:binutils的构建、测试与排错
2026/9/27 3:02:30 网站建设 项目流程

欢迎加入开源鸿蒙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/bashexportPATH="/data/service/hnp/bin:/system/bin:$HOME/.local/bin:$PATH"exportCONFIG_SHELL=/data/service/hnp/bin/bashexportSHELL="$CONFIG_SHELL"uname-mconan--versionpython3--version/data/service/hnp/bin/clang--versioncommand-vgitmaketest-x/data/service/hnp/bin/binary-sign-tooltest-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 的工具功能由上游测试和安装包消费者测试验证。

完整回归中的几次调整

定向测试通过后,再运行完整套件,检查其他测试是否出现新的失败。下表是几次真机运行的结果和后续处理:

阶段观察到的结果后续处理
r43223 PASS、193 FAIL、1 UNRESOLVED继续定位夹具、测试传输和运行部署问题
final203393 PASS、60 FAIL定位压缩调试测试的汇编器选择及dlopen夹具漏签
final343425 PASS、36 FAIL全局外部汇编器引入mbind2a/b两项新失败,收窄修改范围
final36设备会话中断,退出码 130记录为中断运行,另起完整运行
final393419 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 中执行,选一个用于复现的新目录:

gitclone https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos.git binutils-2460-articlecdbinutils-2460-articlegitcheckout--detach62bd363f7a8ef6e60f5fec0393fc01c868eaa5cfRECIPE="$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-opipefailRUN_ROOT="$PWD/article-run-$(date+%Y%m%d-%H%M%S)"mkdir-p"$RUN_ROOT/tmp"exportCONAN_HOME="$RUN_ROOT/conan-home"exportTMPDIR="$RUN_ROOT/tmp"exportMAKEFLAGS=-j1 conan remoteaddohpcd\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 省略这部分汇编元数据。

最终配方 中的代码为:

ifstr(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文件:

套件PASSFAILXFAILUNSUPPORTEDUNTESTED
binutils2731282
gas123900100
ld172134131822
libctf180030
libsframe1680000
合计341935152034

五套汇总的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如何构建和运行上游测试。遇到具体报错时,查相应补丁和失败分析中的复现记录。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询