当编译器运行在一个为另一系统产生可执行程序的系统上时,就会出现交叉编译——当目标系统没有编译工具的本地设置时,或者当主机系统更快或具有更多资源时,这是一个重要的概念。在这篇 how-to 文章中,Peter Seebach 讨论了交叉编译环境的初始设置(使用 Sharp Zaurus 手持计算机作为焦点),并且介绍了工具的安装、基本编译、在 Sharp Zaurus 手持计算机上安装程序,以及像创建使用 configure 脚本的程序这样的高级问题。
通常,程序是在一台计算机上编译,然后再分布到将要使用的其他计算机上。当主机系统(运行编译器的系统)和目标系统(产生的程序将在其上运行的系统)不兼容时,该过程就叫做 交叉编译。
除了兼容性这个明显的好处之外,交叉编译还由于以下两个原因而非常重要:
- 当目标系统对其可用的编译工具没有本地设置时。
- 当主机系统比目标系统要快得多,或者具有多得多的可用资源时。
在这篇文章中,我将使用手持计算机的 Sharp Zaurus 系列作为焦点,讨论交叉编译环境的初始设置。我将介绍工具的安装、基本编译问题、如何在手持计算机上安装程序,以及像创建使用 configure
脚本的程序这样的高级问题。如果按照文章的组织顺序阅读,您将最大地从本文受益,因为本文类似于一个教程,每一步都以逻辑顺序相连。
首先,我将给出交叉编译的一个简洁的概览。
理解交叉编译
如果您熟悉交叉编译环境,则可以跳过这一节。但是如果不了解交叉编译,则请继续往下阅读。
我使用的开发系统是 x86 体系结构的 Linux 系统。我在 SuSE Linux 8.2 上测试了这些指令。测试的目标系统是 Sharp Zaurus SL-5600 和 C700。本文假设您具有 Unix 开发实践的工作背景,并使用过命令行。
正如我前面提到的,当编译器运行在一个为另一个系统产生可执行程序的系统上而且两个系统使用不同的操作环境时就会出现交叉编译。另外,当目标系统不具有它自己的编译工具时,或者当开发者可以平衡主机系统潜在更好的性能或更多的资源时,交叉编译是有用的。
当提到交叉编译器时,我不仅仅是指将一种编程语言的代码转换成对象代码的软件,还指其他必要的开发工具:
- 一个 汇编器,它是编译器工具链后端的一部分。
- 一个 链接器,它是编译器工具链后端的另一部分。
- 用于处理可执行程序和库的一些基本工具,比如strings。
例如,strings 实用工具(它输出对象模块的文本字符串)可能是有用的,但是主机环境版本对目标环境二进制系统并不怎么有用。Zaurus 的交叉开发工具中包含 strings。
在 Zaurus 上本地运行编译器是可能的,但是系统的局限性阻碍有效的工作,这也是使交叉编译为开发者带来福音的另一个原因。典型台式机的显示器和键盘非常有利于编辑工作。另外,台式机的内存、处理器和存储容量也更能满足编译的资源需要。
现在,让我们来安装所需的工具。
|
可用的开发工具
Java 技术。Zaurus 的两个特定于 Java 的开发工具包含一个关于 Zaurus 的 Java 编程和 ZaurusBuilder 的 PDF 指南。ZaurusBuilder 是一个 JBuilder IDE 插件向导,负责将 Java 应用程序打包成一个 IPK 分布文件以进行安装。Zaurus Java FAQ 回答有关使用 Java 进行Zaurus 开发的问题。
Qt/C++。 Qt 是一个 C++ 工具包,用于为 PDA 创建图形用户界面。这里的产品包括一个 Qtopia 的开放源代码 SDK(一个可移动的应用环境)、一个用于设置和使用 Qt/E(嵌入的 C++ 工具包)和 Qtopia 的 PDF 编程指南,以及免费版本的 Qt/E。
Linux。Zaurus 源代码是 Embedix Plus 和 Qtopia 环境。Embedix Plus 集成了来自 Lineo 的 Embedix Linux(内核 2.4.x)、来自 Trolltech 的 Qt/Embedded GUI 应用框架、来自 Opera 的 Opera Web 浏览器和来自 Insignia 的 Jeode JVM。还有一篇 how-to 文章,向您展示如何设置 Linux 交叉编译器。另外还有 Zaurus 上的 Linux 命令的开放源代码,以及一个到利用 Linux 对 Zaurus 进行开发的参考资料的链接。
OpenZaurus 项目。OpenZaurus 项目背后的初始目的是创建一个 ROM 映象(内核 + root 文件系统),这更接近于开发者想要的。该项目使用 Sharp ROM 作为基础,然后再进行改造、修复 bug、添加和减少条目,以使软件包更加开放。OpenZaurus 现在是一个从新从源代码创建的基于 debian 的嵌入式分布。
编译器和其他条目。Zaurus 开发者有许多免费的和商业的编译器和其他 SDK 可用。想要更改 Zaurus 背景、图标和环境的外观吗?请尝试 themes section。
用户组。是的,一个用户组,加上它的大量有经验的输入,可以成为一个工具。并且还有一个非官方的 Zaurus 开发 FAQ,其中有许多有用的信息。
在 参考资料部分可以找到到这些资源的链接。
|
|
安装工具
- 开始,您必须下载几个软件包。可以从 Sharp 的 Web 站点(在 参考资料 部分有到该站点的链接)得到这些软件包,并且必须下载为 RPM。您需要以下主要的软件包:
- 交叉编译器(gcc)。
- 库(glibc)。
- 包含文件(头文件)。
- “其他工具”——一个包含交叉开发环境中常用工具的软件包。
- 接下来,安装 RPM。要做这一点,需要 root 权限。建议的安装方法是为每个文件重复
rpm -Uvh filename.rpm
。
文件安装在 /opt/Embedix 目录中。该目录有一个稍微不寻常的结构——实际的二进制文件安装在 /opt/Embedix/tools/bin 目录中,具有到安装在 /opt/Embedix/tools/arm-linux/bin 目录中的这些二进制文件的符号链接。例如,/opt/Embedix/tools/arm-linux/bin/gcc 是到 /opt/Embedix/tools/bin/arm-linux-gcc 的一个符号链接。二者都可以使用。
- 编译一个简单的测试程序,快速测试已经正确地安装了工具。我推荐传统的 "Hello, world!" 程序,这易于测试:
清单 1. 测试正确的安装
#include <stdio.h>
int main(void) {
printf("Hello, world!");
return 0;
}
|
- 将该测试程序保存在一个叫做 hello.c 的文件中,并编译它。到编译器的两条路径都可以工作——我喜欢使用
/opt/Embedix/tools/arm-linux/bin/gcc。
编译完程序之后,利用 file 命令检查输出文件的类型。
清单 2. 使用 file 命令检查输出的类型
$ /opt/Embedix/tools/arm-linux/bin/gcc -o hello hello.c
$ file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (ARM),
dynamically linked (uses shared libs), not stripped
|
您可能对这一简短的偏离主题感兴趣。还记得我什么时候提到过 strings 程序吗?我们来试一试该程序。
首先, 在您的二进制系统上运行本地 strings 程序:
清单 3. 运行本地 strings 程序
接下来,在相同的二进制系统上运行 ARM 版本:
清单 4. ARM 版本
$ /opt/Embedix/tools/arm-linux/bin/strings hello
|
在我的测试系统上,这些产生了稍微不同的结果。特定于 ARM 的 strings 程序发现一个特定于 i386 的版本未发现的新字符串。
现在,我们在目标系统上测试该程序。
在目标系统上安装应用程序
将文件移动到目标系统有几种方式:
- 使用标准的 Zaurus sync 软件进行转移。
- 复制到介质上。
- 通过网络移动。
使用无线卡或以太网适配器可能是移动文件最容易的方式。如果这样不行,将文件复制到 CompactFlash 或 SecureDigital 介质上也是相当方便的。不幸的是,在发布本文时,Sharp 还未正式在 Linux 下支持 sync,但是复制到介质随处可用。
根据您的 Zaurus 上的 ROM 版本的不同,系统可能不识别或安装 ext2fs 卡。因此,通过 FAT16 格式化的卡复制文件可能更实用一些。CF 和 SD 卡一般在出厂前已经这样格式化了,所以它们可以开箱即用。
( 简要的术语提示: ext2fs 卡是为 Linux 文件系统格式化过的存储卡。 FAT16格式化的卡是为老式的 MS-DOS 文件系统格式化过的存储卡。 CF 卡是 CompactFlash,这是一些数码相机使用的一种介质标准。多数 CF 设备是存储设备,但是 CF Ethernet、无线和串行适配器也存在。 SD(或者叫做 Secure Digital)卡是 MultiMediaCard 技术与我们的目的之间的一个联系,SD 卡就像一个永久保存10%空间的 MMC 卡。)
就将您的可执行文件复制到一个已安装的卡。当将该卡移动到 Zaurus 时,它就会自动地安装为 /usr/mnt.rom/cf。现在您就可以从卡运行应用程序了。
清单 5. 从卡运行应用程序
$ /usr/mnt.rom/cf/hello
Hello, world!
|
现在您已经尝试了该程序,不过您可能更喜欢 Zaurus 的 ipkg 二进制软件包格式。ipkg 文件只是一个包含三个其他文件的 tar 文件:
- 第一个文件,即 data.tar.gz,是一个压缩的 tar 文件,其中包含将被安装到正确目录结构中的多个文件。
- 第二个文件,即 control.tar.gz,是一个压缩的 tar 文件,其中包含安装脚本和信息。
- 第三个文件,即 debian-binary,是一个纯文本文件,其中包含字符串“2.0”。该文件目前不真正做任何事情,但是一些工具期望该文件可用。
尽管有一个方便的脚本可为您做这项工作,但是您也可以手动创建 ipkg 文件。为防万一您确实想要手动创建一个 ipkg 文件,所以我将简要讨论这种软件包的格式。利用您将使用的标准软件安装器,该软件包将正确地安装 "hello" 程序。
- 创建一个名叫 h 的新目录用于保存文件。名称并不一定要叫 h,但是因为后面老要键入它,所以使用一个简短的名称。
- 创建一个名叫 h/opt/QtPalmtop/bin 的子目录,并将您的 "hello" 可执行文件复制到该目录。这对于显示所安装的程序是一个好目录。
- 创建一个名叫 h/CONTROL 的目录。该目录形成 control.tar.gz 文件的内容;h 中的其他东西都放入 data.tar.gz 文件中。在该目录中,您只需要一个文件,叫做 control。将以下数据放入该文件中:
清单 6. 将要放入 control 文件中的信息
Package: hello
Priority: optional
Section: Misc
Version: 1.0
Architecture: arm
Maintainer: Your name (your.address@example.com)
Depends: libc6
Description: Just as stores have greeters, so to do PDAs have greeters.
This is a longer description, separate from the first one, which
can be multiple lines long, with each line indented by a single space.
|
还有其他可以放入到 CONTROL 目录中的文件,但是您并不需要这些文件。请参考 参考资料,在 Zaurus 开发者文档中了解该主题的附加信息。
- 运行
ipkg_build.sh h。
如果前面所做的每一件事情都正确,您应该得到一个名叫 hello_1.0_arm.ipk 的文件。将该文件复制到您的 Zaurus ——不管如何复制都无所谓——并在 Zaurus 上运行 Add/Remove Software 程序。
- 安装 hello,即您应该在软件包列表中看到的版本 1.0 文件。现在就可以从命令行运行它了;它已经被复制到 /opt/QtPalmtop/bin 目录中了。如果您将它安装到一个介质插槽中而不是内部闪存中,它将在该卡上的 QtPalmtop/bin 目录中。例如,如果安装到 SD,它将被安装为 /usr/mnt.rom/card/QtPalmtop/bin/hello。
ipkg_build.sh
shell 脚本对产生错误消息比较有效,但是它们可能会有些容易混淆。实际上,如果您在 control 文件中放入注释(我就是这样做的!),# 字符就会被解释为一个字段名称,从而产生以下错误消息:
清单 5. 啊呀!请不要在 control 文件中放入注释
*** Error: The following fields in CONTROL/control are missing a ':'
###
ipkg-build: This may be due to a missing initial space for a multi-line field value
|
当我第一次看到该消息时有些被搞胡涂了。
复杂编译
一旦您让编译器创建了在目标系统上正确运行的可执行程序,您就会开始遇到令人激动的问题了。
使用 autoconf
的程序可能不支持交叉创建。当已编译的二进制文件被使用在创建过程中时,就会出现惟一真正固定不变的情况。不幸的是,对于使用 autoconf
的程序,这是一种相当常见的情况。
对于一个相当标准的没有太多依赖性的程序,您可能只是忽略选择 C 编译器。FIGlet 是一个程序,用于从平常的文本制造出大字母,如图 1 所示,通过更改 makefile 中的 CC=...
行可以创建该程序。但是,安装稍微有些有趣。
图 1. 文本 "like this" 的 FIGlet 输出样例
FIGlet 想要找到一些数据文件,所以您必须安装数据文件和程序。makefile 文件中对应的行是 DEFAULTFONTDIR = /usr/local/share/figlet。
(注意,有两行设置 DEFAULTFONTDIR;
在设置时请确保只使用其中一行。)
一旦利用更改的 makefile 创建了 figlet
,就可以创建它的一个软件包了。创建一个新的目录(这里叫做 f)。
这时,创建一个二进制目录和一个数据目录。二进制目录是 f/opt/QtPalmtop/bin,而数据目录是 f/usr/local/share/figlet。将 figlet、chkfont 和 showfigfonts 复制到 f/QtPalmtop/bin 中;然后将 fonts/* 复制到 f/usr/local/share/figlet 中。从上一个项目复制到 CONTROL 目录,并编辑 control 文件以给出软件包的名称和版本。同样, build-ipkg.sh
可以为您完成这份苦差使。
有些程序可能需要更多的技巧。例如,要创建 pdksh,
您使用 CC=/opt/Embedix/tools/bin/arm-linux-gcc sh configure。
该创建过程犯了一个小小的错误——它将两个宏 SIZEOF_INT
和 SIZEOF_LONG
都定义为 0。它们应该是 4。
这些宏是通过试图编译和运行输出 sizeof(int)
的测试程序而定义的,但是当然,arm-processor 程序不运行在 x86 主机上,所以这些测试失败。在这种情况下,您可以只是编辑 confdefs.h 文件,更改宏,然后继续。只要知道出了什么错就不难纠正,但是出了什么错并不是那么明显。当它确定一个类型的大小为 0 时,让配置脚本以重大错误异常终止可能还要好一些。
使用一个聪明的 hack, autoconf
的一些最近版本可以解决该问题。只是在 configure.in 文件上重新运行一个当前的 autoconf
就可以帮助解决一些问题,尤其是当软件包过期时, 但是在老的 configure.in 文件中有时会有一些腐旧的东西。
在比较一般的情况下,您可能只是要手动编辑配置脚本或设置程序,以迫使它们获得正确的结果。在极端的情况下,可能有必要试图进行费事的程序“挖口”,比如在一台安装了本地开发工具的 Zaurus 上本地运行配置脚本,然后将所有文件移回桌面开发系统 ,更改编译路径,并从这里启动。
结束语
很多开发者还在犹豫是否尝试交叉编译,因为它听起来比实际要难得多。本文通过给出交叉编译的一个概览,帮助您开始了解交叉编译技术。本文提供了一些例子,讲述设置一个系统以实现交叉编译的程序。本文还提供了一些参考资料,以帮助您设置一个为 Sharp Zaurus 手持计算机进行开发的系统。
参考资料
关于作者