使用 GCC 交叉编译制作 Add-inLinux 上多样而自由的编程环境离不开强大的编译器套件 GCC。这篇教程就将展示如何使 GCC 能够支持计算器所使用的 sh3eb-elf 结构文件的编译。
其实也没那么难,比军训轻松多了。
第一部分:准备工作步骤1:砺兵秣马(依赖安装)我们要安装的不只有 GCC,而是包括两部分:
在这篇教程中我们使用了 gcc-5.5.0 与 binutils-2.29(【译注】原文写作于2016年9月11日,作者使用了 gcc-6.1.0 与 binutils-2.26;但 gcc-6.x/7.x 在我电脑上死活编译不了,换了 5.5.0 反而好了)。你也可以选用其他版本合适的版本——当然那些老古董的版本还是不要考虑了。OSDev 社区(他们经常使用 GCC 交叉编译)维护了一份编译成功的版本的列表;如果你尝试其他组合成功了,也可以向他们反馈。 在操作过程中,你可能因为各种玄学原因失败。在这种情况下,很不幸,你只能下载其他版本然后从头再来(译者:深有体会……)。
你可以通过点击以下链接进行下载(【译注】出于国内访问速度考虑,我选了清华大学的 TUNA 镜像源,上面有很多其他的镜像源):
接下来是逃不掉的依赖项。下载时需要注意,名字中有 -dev 后缀的往往提供了库给其他程序调用,而不仅仅是他们的可执行文件。以下列举的是所需的依赖项,括号里的名字在本文写作时尚可用(【译注】原文写于2016年9月11日,事实上我在2018年1月翻译这篇文章时这些名字也还都可用;而且事实上,有些依赖并不是必需的,如果你没有成功安装其中的某个依赖项,仍然可以尝试编译 GCC):
mpfr (libmpfr-dev):可变精度浮点数
mpc(libmpc-dev):可变精度复数
gmp(libgmp-dev):多种精度的算数运算
png(libpng-dev):图像操作
ppl(libppl-dev):多面体(经过神奇的优化)
g++(g++):C++ 编译器
git(git):版本控制工具
总之,下载最新版本。在 Debian 系下(包括 Ubuntu,Mint 等等衍生版),用 apt-get;其他发行版嘛,看你的心情吧(比如 yogurt,yum,pacman,随便哪个,应该不用我来教你了)。
步骤 2:勘测地形(环境准备)所有的编译操作都将在你个人目录下的 opt 文件夹内完成(古老而神秘的好习惯),所以你需要先创建这个文件夹,再进入它。打开终端,准备好与它共度一段时光。
$ mkdir -p ~/opt/sh3eb-elf
$ cd ~/opt/sh3eb-elf
记一下,我们将要编译的交叉编译器就叫 sh3eb-elf。
然后我们在其中建立两个子目录用于编译 binutils 和 GCC。整个的编译过程都会在这两个文件夹中完成。如果你不幸要从头再来,你需要把这两个文件夹删除再重新建立。
$ mkdir build-binutils build-gcc
最后,把你下载好的 binutils 和 GCC 的源码解压到 ~/opt/sh3eb-eif 中。完成之后文件夹中应该会有四个文件夹,分别是上面步骤中建立的两个文件夹 build-binutils 和 build-gcc,以及视你下载的版本而定的两个源码文件夹,比如我的就是 binutils-2.29 和 gcc-7.2.0。记得修改下面用到的命令行中的相应部分,如果你选了和我不同的版本。
第二部分:直抵前线步骤 3:兵戎初接(编译 Binutils)binutils 的编译过程相当经典——先 configure 再 make。注意一下(后面的教程中也一样),如果你给 make 传递了 -j 参数(不要忘了在后面跟一个数字,代表线程数,例如 -j8),就可以获得多线程的加速(【译注】线程数大致可以选 CPU 核心数的 2~4 倍)。
$ cd build-binutils
$ ../binutils-2.29/configure --target=sh3eb-elf --prefix="$HOME/opt/sh3eb-elf/" --disable-nls --disable-werror
你可能会注意到 --target 参数,顾名思义,它制定了目标架构(我们的计算器用的是一个 SuperH-3 处理器)。你大可不必担心 SH4 ,因为 SH4 兼容 SH3(反之则不然)。--disable-nls 选项强制编译器使用英语(这也是一个古已有之的好习惯),而 --disable-werror 选项关闭了编译器的 -Werror 选项,反正你也不会去用。
然后我们就可以用 make 来编译和安装了。
$ make
$ sudo make install
如果你遇到了编译错误,检查一下你的依赖项以及它们对应的头文件是否被正确安装了。如果还是不行,就只能回到步骤 1 中重新选择 binutils 和 GCC 的版本了。
步骤 4:装甲压境(编译 GCC)现在你已经装好了 Binutils 了,它的可执行文件应该会出现在 ~/opt/sh3eb-elf/bin 下。如果我们想要使用它们,就需要将这个目录加入系统的环境变量 PATH(运行命令时会从其记录的目录中寻找程序)。下面这个命令可以帮你临时完成这个工作。
$ export PATH=$PATH:$HOME/opt/sh3eb-elf/bin
如果你需要使其永久生效,则应将上述命令加入你的 Shell 配置文件如 ~/.bash_profile。
$ echo 'export PATH=$PATH:"$HOME/opt/sh3eb-elf/bin"' >> ~/.bash_profile
$ source ~/.bash_profile
接下来的工作和上一步骤差不多。我们首先运行配置脚本,然后编译。你可以用 --enable-languages 选项来指定编译你需要的语言的编译器。一般来讲,C 和 C++ 足够了,但是如果你打算用 Ada,Go 或者 Fortran 的话也可以把它们加入进来。不过不要贪杯,除非你可以忍受漫长的编译时间(译者:何况连能用的库都没有……)。
$ cd ~/opt/sh3eb-elf/build-gcc
$ ../gcc-5.5.0/configure --target=sh3eb-elf --prefix="$HOME/opt/sh3eb-elf" --disable-nls --enable-languages=c,c++ --without-headers
选项的组织和之前类似。如果配置因为丢失头文件而失败,安装好它们之后重新运行配置脚本。如果一切顺利,那么你就已经为这场战役中最漫长的部分做好了准备(别忘了用 -j 选项来指定线程数)。
$ make all-gcc
$ sudo make install-gcc
如果你遇到了你解决不了的编译错误……抱歉,只能从步骤 1 重来了。
步骤 5:空中支援(安装 libgcc)最后的一大任务是编译 libgcc,你需要用它来编译你的程序。
$ make all-target-libgcc
$ sudo make install-target-libgcc
如果编译又失败了……抱歉,还是只能从步骤 1 重新开始(都到了这一步,确实让人沮丧)。
祝贺!你的编译器已经安装完成,可以使用了。你可以运行一些命令来检查一下它……
$ sh3eb-elf-as --version
GNU assembler (GNU Binutils) 2.29 [...]
$ sh3eb-elf-gcc --version
sh3eb-elf-gcc (GCC) 5.5.0 [...]
$ sh3eb-elf-gcc -print-file-name=libgcc.a
/Users/mike/opt/sh3eb-elf/lib/gcc/sh3eb-elf/5.5.0/libgcc.a
如果你得到了类似的输出,那么你就可以……嗯,继续看下去了!这教程还没有结束。
第三部分:占地为王步骤 6:全副武装(Add-in 包装器)你可能会对我所谈论的 ELF 文件格式感到困惑,因为计算器上只能使用 g1a……不过到现在为止,g1a 还只是 CASIO 专有的格式,GCC 对如何生成它一无所知。所以,我们要使用包装器(Wrapper)来将 ELF 文件打包为 g1a 格式文件。我们已经将它上传到一个 Git 仓库。你可以使用以下命令来将它安装到你的编译器所在目录(【译注】可能会有权限问题,需要使用 sudo 来运行命令)。
$ cd ~/opt/sh3eb-elf
$ git clone "https://Lephenixnoir""@""bitbucket.org/Lephenixnoir/add-in-wrapper.git"
$ cd add-in-wrapper
$ make
$ cp build/g1a-wrapper ~/opt/sh3eb-elf/bin
你现在已经可以给计算器开发程序了!让我们一起来看看具体应该怎么做……
步骤 7:号令天下(项目环境)我们将会创建一个模板工程来供你重复使用。因为这些与 GCC 的编译过程无关,所以我建议你在别的文件夹里面做这事(比方说 ~/my-awesome-project 就很棒)。不论如何,你都可以暂时关掉终端了。
我假定你已经有一定的有 C 语言程序设计的经验,所以我不会再教你如何组织一个项目……你只需要这些文件就可以了(译者:下载速度感人……)。 你一定已经迫不及待地想用你的新工具来编译它了。一个不错的选择是(【译注】当然用 CMake 什么的就更舒服了,过几天我可能会写一下怎么操作)……
$ sh3eb-elf-gcc -m3 -mb -ffreestanding -nostdlib -T addin.ld crt0.s addin.c -o addin.elf -I include -lgcc -L .
这里对这些选项做些说明:
-m3 和 -mb 选项表明我们想要生成 SH3 上的二进制(SH3 还是有很多变种的,所以我们仍然需要指定一下),并使用大端模式(这和多字节变量每个字节在内存中的顺序相关);
ffreestanding 选项要求编译后的代码不会依赖任何特定的操作系统(而是由 GCC 提供一些东西);
-I 是包含目录标记(我们有一些头文件在 include 目录中);
-nostdlib 表示我们不会使用标准库(其实它已经被包含在 libfx.a 中了);
-O2 是优化选项(【译注】运行速度能快很多)。
这些编译选项都是在编译期使用的。由于我们的项目比较简单,所以我们一次性将它编译完成。如果你有一个大项目,最好逐个文件编译完成后再进行链接。下面这些是链接选项:
-T addin.ld 选项告诉编译器,链接时需要参考 addin.ld 文件中编写的规则;
-L . 和 lfx 选项表明我们需要链接当前目录中的 libfx.a 文件;
-lgcc 表示我们将要使用 libgcc(你总是需要它的!);
当然 -o addin.elf 表明了我们想要的输出文件。
注意文件夹中的 crt0.s 文件是另一种源代码文件,它包含了基础代码。别忘了把它也编译了(这里我们把它和 addin.c 同时编译了)。编译器输出的文件是用于 SH3 架构的 ELF 文件(经典的 Linux 格式),这正是我们一开始所期望的。
步骤 8:强强联合(g1a 格式文件的生成)使用 ELF 格式的文件有很多好处,不过我们想要的只是纯二进制文件。为此,我们将使用 Binutils 中的一个工具 objcopy 来改变格式。简而言之,就是要将 ELF 删除而只留下纯的二进制部分。
$ sh3eb-elf-objcopy -R .comment -R .bss -O binary addin.elf addin.bin
现在我们只需要给二进制添加 add-in 文件头就可以了——这也正是 Add-in Wrapper 将要做的。你可以使用 g1a-wrapper --help 命令来查看这个工具所支持的一些选项,不过现在我们只是简单地把二进制包装一下再加个图标。
$ g1a-wrapper addin.bin -o addin.g1a -i icon.bmp
大功告成!现在你只要把这个 add-in 传送到你的计算器上就好了……这里有一些好用的祖传工具可以使用:
感谢阅读。