人生苦短,在我们所关注的领域里,行业进步似乎永远都不够快。这是我们大多数人都熟知的观感。有人会认为现在大多数的aarch64桌面应用都可在arm64环境下运行,并在需要时提供多架构支持。
可是令人失望的是,截至2019年底,aarch64上的chromeOS仍在发布aarch64架构内核和armhf用户空间。尽管优秀的chromebrew开发人员做的工作很好,但在开发模式下的arch64 chromeOS系统–一个本来优秀的公路骑行勇士,却被32位armhf所束缚。有人可能会问,这是一个问题吗?是的,aarch64 客观上是 MCU 之外更好的 arm ISA,从通用代码到各种 ISA 扩展方面都是,尤其是 SIMD。这主要体现在当代编译器支持和代码生成质量的差异上。特别是鉴于2019年为 aarch64 和 Thumb2 引入了 ILP32 ABI,尽管它在 aarch64 下不受支持,目前却没有充分的理由寄希望于近期可以在 aarch64 机器上构建 aarch32 代码。
作为一个长期的arm chromebook用户,我一直在我的chromebook上使用沙盒式arm64编译器。最近我像往日一样离家骑行时,发生的一件事故,由于我的马虎,那个沙盒一不小心损坏了,所以我不得不使用昂贵的移动数据从头再做一遍。不过这促使我记录下了建立沙盒所必需的最少步骤,现在将这些步骤与各位分享。请注意,这些步骤对任何3.7或更新的arch64内核和armhf用户空间的设置都是有效的,但在chromeOS的情况下,有一些额外的限制–严格限制用户对根系统文件进行更改。
在chromeOS开发模式下,/usr/local目录下有一个可写的系统文件空间,因此我们将在此处设置沙箱。
1 2 3 |
$ cd /usr/local $ mkdir root64 $ cd root64 |
我们需要选择一个好的arm64软件包来源–一些软件包的最新基础发行版就可以了。这意味着Debian或Ubuntu符合–我们选择了软件包刷新率更好的后者。接下来需要决定要哪个版本的编译器,因为这将决定我们需要哪些系统库。由于个人原因,我决定使用gcc 8.2.0。以下是选择的gcc软件包版本列表,需要从http://ports.ubuntu.com/ubuntu-ports/pool/main/下载:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-rw-r--r--. 1 chronos chronos 2180000 Apr 9 2018 binutils-aarch64-linux-gnu_2.30-15ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 2391032 Apr 9 2018 binutils-dev_2.30-15ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 5711228 Sep 18 2018 cpp-8_8.2.0-7ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 6465184 Sep 18 2018 gcc-8_8.2.0-7ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 428092 Apr 9 2018 libbinutils_2.30-15ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 2270660 Apr 17 2018 libc6_2.27-3ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 2051800 Apr 17 2018 libc6-dev_2.27-3ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 474272 Apr 17 2018 libc-bin_2.27-3ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 36228 Sep 18 2018 libcc1-0_8.2.0-7ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 58632 Apr 17 2018 libc-dev-bin_2.27-3ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 34292 Sep 18 2018 libgcc1_8.2.0-7ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 856036 Sep 18 2018 libgcc-8-dev_8.2.0-7ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 212856 Dec 2 2018 libgmp10_6.1.2+dfsg-4_arm64.deb -rw-r--r--. 1 chronos chronos 520464 Aug 14 2018 libisl19_0.20-2_arm64.deb -rw-r--r--. 1 chronos chronos 37156 Jan 24 2018 libmpc3_1.1.0-1_arm64.deb -rw-r--r--. 1 chronos chronos 202380 Feb 8 2018 libmpfr6_4.0.1-1_arm64.deb -rw-r--r--. 1 chronos chronos 371988 Sep 18 2018 libstdc++6_8.2.0-7ubuntu1_arm64.deb -rw-r--r--. 1 chronos chronos 45430 Sep 13 2015 zlib1g_1.2.8.dfsg-2ubuntu4_arm64.deb |
如果在主Ubuntu存储库中定位这些软件包有困难,可以使用全局注册http://ports.ubuntu.com/ubuntu-ports/ls-lR.gz。
接下来 “部署”这些软件包。我们可以逐个进行,例如想检查每个软件包中的内容:
1 2 3 |
$ ar x package.deb $ tar xf data.tar.xz $ rm -v control.tar.{xz,gz} data.tar.xz debian-binary |
或者很多:
1 |
$ for i in *.deb; do ar x $i ; tar xf data.tar.xz ; rm -v control.tar.{xz,gz} data.tar.xz debian-binary ; done |
该步骤简单直接,但是接下来的步骤就不那么简单了。
aarch64 内核在本地运行静态链接的二进制文件,OK,这没什么问题。不过,此类二进制文件在Linux桌面应用上很少见——你将遇到的或自己编译的大多数二进制文件都属于动态链接。对于平台支持的每个架构,都必须有对应的动态加载器。目前,应通过如下命令行构建ARM多架构:
1 2 |
/lib/ld-linux-aarch64.so.1 /lib/ld-linux-armhf.so.3 |
分别对aarch64和armhf架构,自然只有一个单架构;如果你查询任何给定的动态链接二进制文件,通过如下命令行,你会看到在ELF类型之后立即报告所加载的信息:
1 2 3 4 5 |
$ file a.out # some freshly built armhf dynamically-loaded binary a.out: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.32, stripped $ file a.out # same code built for aarch64 a.out: ELF 64-bit LSB pie executable ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=e999425cae14ba4fa0ad8bf44ad1c50bb32e012e, stripped |
上面的命令行“dynamically linked, interpreter /lib/ld-linux-armhf.so.3”,告诉我们应该用什么动态加载器来启动每个二进制文件,包括路径和所有内容。由于不能在chromeOS上修改/lib,不能在那里放置arch64加载器,所以我们需要返回到其他方式。
正如许多人所熟知的那样,gcc 的二进制文件只是一个门面模式,它调用大量现有商业二进制文件来进行构建。在我们的沙盒环境中,命令行如下:
1 2 3 4 |
/usr/local/root64/usr/lib/gcc/aarch64-linux-gnu/8/cc1 /usr/local/root64/usr/lib/gcc/aarch64-linux-gnu/8/collect2 /usr/local/root64/usr/bin/as /usr/local/root64/usr/bin/ld |
现在,前两行命令行所需文件由.deb软件提供,而后两行命令行通常只是现有arch64-linux-gnu-*二进制文件的符号链接,但我们没有这些链接,而且反正我们也不需要这些作为符号链接。
每次arm64 gcc尝试执行其中一个时,它都会由于缺少aarch64 的系统级加载程序而失败。那么我会如何解决呢?很容易——通过手动指定应该与想要运行的任何给定arm64二进制文件一起使用的加载程序。因此,按我们的要求通过一些同名脚本隐藏这些二进制文件,同时将cc1和collect2的原始二进制文件作为.bak 文件保留在其原始位置:
1 2 3 |
$ cd usr/lib/gcc/aarch64-linux-gnu/8 $ mv cc1 cc1.bak $ mv collect2 collect2.bak |
最终,我们希望在下面相应目录位置分别有以下脚本:
- /usr/local/root64/usr/lib/gcc/aarch64-linux-gnu/8/cc1:
1 2 |
#!/bin/bash /usr/local/root64/lib/ld-linux-aarch64.so.1 /usr/local/root64/usr/lib/gcc/aarch64-linux-gnu/8/cc1.bak $@ |
- /usr/local/root64/usr/lib/gcc/aarch64-linux-gnu/8/collect2:
1 2 |
#!/bin/bash /usr/local/root64/lib/ld-linux-aarch64.so.1 /usr/local/root64/usr/lib/gcc/aarch64-linux-gnu/8/collect2.bak $@ |
- /usr/local/root64/usr/bin/as:
1 2 |
#!/bin/bash /usr/local/root64/lib/ld-linux-aarch64.so.1 /usr/local/root64/usr/bin/aarch64-linux-gnu-as $@ |
- /usr/local/root64/usr/bin/ld:
1 2 |
#!/bin/bash /usr/local/root64/lib/ld-linux-aarch64.so.1 /usr/local/root64/usr/bin/aarch64-linux-gnu-ld $@ |
最后,同样很重要的一点。我们需要一个 gcc 顶层脚本来为我们设置一些内容最好放在目录PATH中的某个位置:
- gcc-8.sh:
1 2 3 4 5 |
#!/bin/bash root=/usr/local/root64 export PATH=$root/usr/bin/:$PATH export LD_LIBRARY_PATH=$root/lib/aarch64-linux-gnu/:$root/usr/lib/aarch64-linux-gnu/ $root/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 $root/usr/bin/gcc-8 $@ --sysroot=$root |
让我们用以下示例命令行测试一下这个新沙盒装置:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <stdio.h> #if __LP64__ #define PLATFORM_STR "arm64" #else #define PLATFORM_STR "armhf" #endif int main(int argc, char** argv) { fprintf(stdout, "hello from " PLATFORM_STR "\n"); return 0; } |
首先,使用系统自带的armhf gcc-8.2.0编译器,这由chromebrew提供:
1 2 3 4 |
chronos@localhost /usr/local/root64/misc $ gcc-8.2 test.c chronos@localhost /usr/local/root64/misc $ ./a.out hello from armhf chronos@localhost /usr/local/root64/misc $ |
接下来使用我们刚刚设置的arm64 gcc-8.2.0:
1 2 3 4 |
chronos@localhost /usr/local/root64/misc $ ./gcc-8.sh test.c chronos@localhost /usr/local/root64/misc $ LD_LIBRARY_PATH=/usr/local/root64/lib/aarch64-linux-gnu/:/usr/local/root64/usr/lib/aarch64-linux-gnu/ ../lib/ld-linux-aarch64.so.1 ./a.out hello from arm64 chronos@localhost /usr/local/root64/misc $ |
请注意,由于我们还没有导出arm64支持的LD_LIBRARY_PATH路径,我们必须在调用aarch64加载器之前在命令行中指定它。
图片描述:Chromebrew Arm64 gcc命令行代码
好吧!伙计们,以上就是全部步骤。我希望这些对aarch64+armhf用户能够有用。

文章翻译者:Rita Wang,CNX中文站翻译人员,文字功底扎实,将科技文献以通俗易懂的形式呈现给读者,对开源硬件、AI、IoT等领域多有涉猎。