使用自由软件构建交叉编译、汇编和连接工具, Cross-build gcc for mips.

唉,好郁闷,做的东西自己都不懂多少。我 GDB 用的都不熟,还要给 GDB 添加新的 target。自定义 elf 文件的 BFD support, target dependent, disassembly function, 好几千行的代码啊,弄不好得上万行,GDB 那么多文件,看得我头都晕了。

因为我们的芯片和 MIPS 关系比较密切,就先编了个 MIPS 的交叉编译和调试环境玩玩。

Concepts:

交叉编译,就是在一个平台上生成另一个平台上的可执行代码。这里的平台包含两个概念:Architecture 和 OS,x86 Linux平台是Intel x86 arch 和Linux for x86 OS 的统称,Sparc SunOS 4.x 就是 Sparc arch 和 SunOS 4.x(or Solaris 1.x) 的统称。生成交叉编译器后就可以在主机上生成目标机的代码,就比如说显然低端手机上不可能装一个操作系统,更不用说编译器了,但手机上仍然需要安装软件才能提供服务。就需要使用交叉编译器,在 PC 上编译好程序以后将二进制文件装载到手机的内存中去(有可能直接烧进去,或者使用 bootloader 加载)。这个时候,PC 就是主机,手机就是目标机。这个程序在主机上是不可能直接运行的,因为它的代码指令是目标机的指令,很可能和主机的指令集完全不兼容(不然要交叉编译器干吗?)。

调试环境,程序编译成可执行文件之后肯定需要调试,但是代码在主机上无法运行,如何调试?常用的有几种办法:1.把二进制文件加载到目标机上,打印出调试变量。2.把二进制文件加载到目标机上,通过串口或者网口向管理程序交流,实现在线调试。3.把二进制文件放到主机的模拟器软件上运行,进行调试。其实还有一些其它的方法,比如 ICE(in-circuit emulator),但是代价比较高。这几种方法一般都是混合使用的,很难一种就完全满足要求,就连 Nokia 出的手机模拟器也是漏洞百出的,必须得在板子上运行之后才知道代码有没有错误。

GDB 是支持远程调试的,可以通过 serial port, tcp, udp 和 pipe 与目标机进行交流,使用的语言叫做 RSP(Remote Serial Protocol),同样这需要目标机使用同样的语言和它交流,这样就需要一个翻译。这个目标机上的翻译程序就是 stub(裸机和简单操作系统) 或者 gdbserver(可以运行gdb的操作系统)。但是光有这些还不行,因为 GDB 必须知道目标机的结构(寄存器,字长等等),二进制文件的格式(elf,pe,寄存器代号),操作指令才可以实现远程调试,不然它怎么知道寄存器名对应的是哪个?怎么加载函数的符号表来设置断点?怎么反汇编地址内容?唉,我现在要做的就是把这些添加到 GDB 中去,然后把 GDB 和模拟器用 RSP 连接起来。其实我们的模拟器就支持直接调试的,但是客户不一定喜欢字符界面啊,要做 IDE 的话,而且以后还要支持在线调试,还是用 GDB 方便,毕竟 GDB 可以和很多 IDE 集成。

Practice:

我们的编译器是改的 openCC,我也不懂。这里编的是GCC,它已经支持了很多平台,arm, mips, sparc, powerpc...,通用性更强些。

编译工具需要好几个部分,这就有个编译顺序问题,应该先编什么?显然应该从最底层编起,顺序大致应该是:

binutils, gcc-cross, library, gdb.

其实 GDB 无所谓了,放在什么时候编都一样。但 binutils 必须放在 gcc-cross 前面编(如果不能使用主机上的as, ar, ld, nm 这些二进制工具的话),而 lib 也得看需要不需要 gcc-cross,如果直接使用主机上的编译器编译的话,就放在前面编,如果不能使用,就放后面编。glibc 太大了,很多时候会使用其它的 lib 代替,比如 uclibc, newlib 等等,特别是目标机操作系统有要求时。

编译过程都不难,无非是 wget,tar,configure,make,make install。但是有很多情况下过程不会那么顺利,尤其当不了解整个工具链的要求时,碰到编译不过最好找个懂的人问问,是缺少 lib 啊,还是配置问题。

我用的版本都比较新,binutils-2.17,gcc-core-4.1.0,newlib-1.15.0,gdb-6.6,最重要的就是 configure 啦,随便写一下,编译最好在新建目录里。

/src/binutils-2.17/configure --prefix=/program --target=mips-elf
/src/gcc-core-4.1.0/configure --prefix=/program --target=mips-elf --with-newlib --enable-languages=c
/src/newlib-1.15.0/configure --prefix=/program --target=mips-elf
/src/gdb-6.6/configure --prefix=/program --target=mips-elf

要注意的是,如果编译 gcc-cross 时候加 --with-newlib 的话,要到主机编译器能找到的地方,比如对应的 lib 目录下建两个软连接,分别指向解压后的 newlib 根目录下的 newlib 和 libgloss,而且必须先编译出 gcc for mips-elf 后再编译 newlib for mips-elf。编译成功的 gcc 安装到 prefix/bin 目录后,会加上 mips-elf- 前缀。最好不要使用 --program-prefix 选项,这样会造成后续编译自动寻找工具的麻烦。最好将 prefix/bin export 到 PATH 里去。

其它还好,最头痛的是 gcc,时间很长,用 core 也得编10多分钟,还是在服务器上,配置也繁,比如 target 选 mips-linux-gnu 就是死活编不过去,选 xtensa-elf 也是编不过去,肯定是少什么或者设置不对,但就是不知道少的是什么,还是没有经验啊。要是再加上操作系统的东西,编译 gcc 前面还要先编 kernel headers, glibc headers,gcc 可能需要编两遍,还有 glibc, kernel,更麻烦。反正我现在至少不用费那个劲,现成的编译器已经有了,只是需要 map 一些东西到 gdb 而已。

GDB 自带 mips, arm 这些程序的模拟器,不知道功能怎么样,但至少可以用。但是编译时候的参数还有程序中函数入口等等实在是太烦了,写和调试嵌入式系统的代码那叫一痛苦。

Copyright © 2005-2007 Solrex Yang. All rights reserved.

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注