为大端嵌入式处理器编译 Arm GNU 工具链

 

最近正在试图把 CanoKey 的闭源版本从 Arm Keil 工具链切换到开源的 GNU 工具链。这过程中有许多波折,或许之后会有独立的博客讲述这个故事。在迁移初期就遇到的一大问题是,Canokey 的安全芯片使用的处理器是大端SC000,本质上是基于 Armv6-M 架构的 Cortex-M0,使用 Thumb 指令集。而 Arm 官方提供的 Arm GNU Toolchain 对于这一架构仅有小端支持(即 host triple 为 arm-none-eabi,而非 armeb-none-eabi)。

虽然小端的 GCC 使用支持 -mbig-endian 来切换端序,但其携带的所有标准库都是小端的。如对于 Cortex-M0,其 multilib ABI 为 thumb/v6-m/nofp,在解压后的工具链的 arm-none-eabi/lib/thumb/v6-m/nofp 目录下即可找到 libclibgcclibm 等比较关键的标准库(尤其是 libgcc,即使替换其他 C 标准库,也同样会依赖它)。由于这些库都是跟随 GCC 一同编译的,并且 multilib 中不存在端序标识,因此比较简单的方案是重新编译整个工具链,以获得完整的大端支持。

一番搜索后,我在 GitHub 找到了 armeb-none-eabi-gcc-buildpatch 项目,它提供了对 Arm 官方编译脚本的 patch(但已经无法在新版的 GCC 上使用了),甚至在 release 中提供了编译好的 GCC 8.3.1。但目前 GCC 都已经 11.2 了,当然要自己动手了。一番尝试后,得到步骤如下:

  1. 从上面给的 Arm GNU Toolchain 页面下载源码(当前名为 gcc-arm-src-snapshot-11.2-2022.02.tar.xz),解压并进入目录。
  2. 运行 ./install-sources.sh,获得工具链源码(如有必要,可替换 src/gcc 等目录为更新的版本,但不保证成功编译)。
  3. build-common.shbuild-toolchain.sh 打补丁,主要是修改所有的默认 host triple 和 configure 选项为大端。具体 patch 内容由于太长,我上传到了 Gist
  4. 编译依赖项:./build-prerequisites.sh --skip_steps=howto,mingw,mingw32,package_sources
  5. 编译工具链:./build-toolchain.sh --skip_steps=gdb-with-python,howto,manual,mingw,mingw32,mingw-gdb-with-python,mingw32-gdb-with-python,package_sources

在经过漫长的等待后(真的很慢,因为有非常多的 multilib 要编译——如果愿意的话,大概可以进一步 patch,关掉不需要的部分),在 pkg 目录下就能找到名称以 gcc-armeb-none-eabi- 开头的工具链包;如果没有跳过 package_sources,则还有一份源码。这样得到的工具链就是我所需要的,其中包含了 newlibnewlib-nano 的大端版本。