当前位置:嗨网首页>书籍在线阅读

17-交叉开发环境

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

2.3.8 交叉开发环境

开发嵌入式系统应用和设备驱动之前,需要一套工具(编译器、实用工具等)来生成适合目标系统的二进制可执行文件。考虑一个在桌面PC上编写的简单应用,比如传统的“Hello World”。你在电脑上编写好代码后,会使用电脑操作系统自带的编译器(通常是GNU的gcc编译器)来编译代码,以生成一个可执行的二进制镜像文件。这个可执行文件的格式与编译代码的电脑兼容,可以在该电脑上运行。这被称为本地(native)编译。也就是说,使用本机系统中的编译器生成可以在本机上运行的程序。

需要注意的是,本地编译并不意味着我们就能知道用于编译和运行程序的系统架构。其实,如果你有一个可以在目标板上运行的工具链,就可以在目标板上本地编译生成适合此目标板架构的应用程序。实际上,要对一个新的嵌入式内核和定制单板进行压力测试,一个好办法就是在上面反复编译Linux内核。

在交叉开发环境中开发软件要求编译器运行于开发主机上,但生成的二进制可执行文件的格式与开发主机不兼容,不能在上面运行。这类工具存在的主要原因是,在资源(一般指内存大小和CPU性能)受限的嵌入式系统上本地开发和编译代码常常是不现实或不可能的。

这种开发方式隐藏着很多陷阱,嵌入式开发的新手稍不留神就会中招。当编译一个程序时,编译器一般都知道怎样找到所需要的头文件和正确编译代码必需的程序库。为了说明这些概念,我们再看一下“Hello World”。代码清单2-4中的示例代码是使用下面的命令行进行编译的:

16.png 在代码清单2-4中,我们看到这个程序代码包含了一个头文件stdio.h。这个文件和我们在 gcc 命令行中指定的文件hello.c不在同一个目录中。那么,编译器是如何找到它的呢?另外,函数 printf() 也不是在文件hello.c中定义的。因此,编译hello.c后,它会包含一个对此符号的未解析的引用(unresolved reference)。链接器在链接时是怎样解析这个引用的呢?

编译器使用一些默认的搜索路径来定位头文件。在代码中引用某个头文件时,编译器在默认的几个搜索路径中查找这个文件。类似地,链接器也是以这种方式来解析对外部符号 printf() 的引用。链接器知道默认在C库(libc-*)中搜索未解析的引用,并且知道在系统中的哪些位置可以找到这些程序库。再说明一下,这种默认行为是内置于工具链中的。

现在假设你为某个采用Power架构的嵌入式系统编写应用程序。显然,你需要一个交叉编译器,用于生成兼容Power架构处理器的二进制可执行文件。如果你使用交叉编译器,并采用类似的编译命令来编译前面的hello.c程序,在解析对外部符号 printf() 的引用时,链接器很可能会意外地将二进制可执行文件链接到一个x86版本的C库。当然,由于生成的可执行程序混合了Power架构和x86二进制指令,如果运行这个错误的混合体[17],其结果是可以预见的,那就是系统崩溃!

[17] 实际上,这可能都不会编译或链接成功,更不用说运行了。我们只是想说明这个问题。

摆脱这个困境的方法是指引交叉编译器在非标准路径中进行查找,以使用针对目标架构的头文件和程序库。我们将在第12章中详细讨论这个主题。这个例子旨在说明两种开发环境的区别,即本地开发环境和嵌入式系统所需的交叉编译开发环境。这只是交叉开发环境复杂性的一个方面。交叉调试中也会出现相同的问题和解决方案,从第14章开始,你会了解到这些内容。正确地搭建交叉开发环境对于成功至关重要,你将在第12章中看到,这不仅仅涉及编译器,还包括其他很多内容。