前言
本文基于武汉新路遥科技有限公司与武汉万向奥科电子有限公司联合推出的HD-IMX6ULL-MB系列开发板的测试验证。该开发板基于NXP iMX6ULL系列Cortex-A7高性能处理器,适合人机界面工业4.0扫描仪、车载终端、便携式医疗设备等一系列创新产品的快速开发。
制作自己的交叉编译器
早期(2009年之前),当我们开发嵌入式系统时,我们做的第一件事就是制作自己的交叉编译器。在制作交叉编译器时,需要自己下载gcc、glibc、binutils等相关工具的源码,然后将源码一一编译安装。在制作交叉编译器的过程中最痛苦的就是各个软件之间的版本依赖关系。例如,gcc 4.6.2 依赖于glibc 2.13。如果选择gcc 4.7,编译制作可能会失败,然后再尝试新版本。编译直到找到合适的版本。
后来,为了方便交叉编译器的制作,很多组织或个人开始编写这些用于制作交叉编译器的脚本或框架,并测试和解决这些软件版本之间的依赖关系。当时最知名的就是基于glibc的crosstool和基于uclibc的
构建根目录。在开始讲解如何制作交叉编译器之前,我们先来了解一下C运行时库。
1. 嵌入式C运行时库
1.1glibc库
glibc是gnu发布的libc库,也是c运行时库。 glibc是Linux系统中最底层的API(应用程序编程接口),几乎所有其他运行时库都依赖于glibc。除了封装Linux操作系统提供的系统服务之外,glibc本身还提供了许多其他必要的功能服务的实现,主要如下:
字符串,字符串处理
信号、信号处理
dlfcn,管理共享库的动态加载
直接、文件目录操作
elf,共享库的动态加载器,也称为解释器
iconv,不同字符集的编码转换
inet,套接字接口实现
intl,国际化,即gettext的实现
io
Linux线程
区域设置、本地化
登录、虚拟终端设备管理、系统安全访问
malloc,动态内存分配和管理
尼斯
stdlib、其他基本函数
gcc 是一个编译器。基本上Linux下的所有程序(包括内核)都是由gcc编译的,当然libc也被编译。 gcc 和libc 是两个相互依赖的软件。他们的合作方式类似于Linux系统的“引导程序”。首先使用旧的libc 创建一个工作目录并
在gcc系统上,使用旧的gcc编译新版本的gcc+旧的libc,然后使用这个新的gcc编译新的gcc+新版本
libc,然后使用这个新的组合来编译整个新系统。
1.2 uClibc库
PC上常用的标准库glibc是一个非常庞大且完整的库。然而,对于早期的嵌入式系统来说,由于Flash和RAM的有线存储空间,其尺寸太大。 uClibc的出现就是为了解决这个问题。 uClibc 尽可能兼容
Glibc,大多数应用程序可以使用uClibc 作为glibc 的替代品,只需很少或无需修改。通过用uClibc替换Glibc,无论应用程序是使用静态链接还是动态链接编译,都可以在不改变应用程序功能的情况下大大减小发布文件的大小。
uClibc 比Linux 发行版中常用的GNU C 库(glibc) 小得多。 glibc 旨在支持最广泛的硬件和内核平台的所有C 标准,而uClibc 则专注于嵌入式Linux。许多功能可以根据空间需求进行权衡。现在uClibc更多地运行在标准和无MMU的Linux系统上,支持i386、x86 64、ARM(大/小端)、AVR32、Blackfin、h8300、m68k、MIPS(大/小端)、PowerPC、SuperH(大/小端)字节序),
SPARC、v850等处理器。
由于当前嵌入式系统硬件性能的提高,用于存储程序的Flash空间和用于运行程序的RAM空间都得到了很大的提高。为了保证程序有更大的兼容性,uClibc已经逐渐退出历史舞台。
uClibc早期官网: uClibc最新官网:
https://www.uclibc.org/h ttps://uclibc-ng.org/
1.3 elibc库
EGLIBC(Embedded GLIBC,缩写为EGLIBC)是glibc原创组织FSF推出的glibc新变种。目的是将glibc用于嵌入式系统。它是GNU C 库(glibc) 的一个分支,也根据GNU Lesser General Public License 获得许可
(LGPL) 发布。它希望应用于嵌入式系统,但其源代码和可执行文件与glibc保持一致。它的作者声称它不是glibc 的一个分支,而是用于容纳核心glibc 开发人员拒绝采用的补丁。
2009年5月6日,由于与glibc核心开发者就程序的开发方向存在争议,Debian开发者宣布将使用EGLIBC来取代glibc。 Ubuntu从9.10开始也采用了EGLIBC,Ark Linux也使用它。 2014年初,官网宣布eglibc已经停止开发,因为目标现在正在直接在GLIBC中解决(goals now beaddressed direct in GLIBC),Debian开发者也重新开始使用glibc。
1.4 newlib库
在做一些单片机的裸机程序开发时,有时候最想要的就是实现一个printf打印功能,及时输出各种信息。排除底层设备驱动,printf本身的实现就够麻烦的了。相关代码最好保存一下,不然就浪费时间了。此外,还有一些函数如strlen和strcpy。我们不愿意自己写,既麻烦又低效。如果我们可以使用现有的代码或库那就更好了。
Newlib满足了这个需求。它是一个用于嵌入式系统的C 运行时库。它最初是由Cygnus Solutions 组装的源代码集合,名为newlib,现在由Red Hat 维护。对于兼容GNU的嵌入式C运行时库,Newlib并不是唯一的选择,但就成熟度而言,newlib是最好的。 newlib架构独特,可移植性强,可重入性强,功能齐全,能够很好地满足深度嵌入式系统的需求。
Newlib库是一个开源C函数库,包括libc和libm。它支持ANSI C库标准,针对不同处理器架构进行了优化,重量轻,适用于嵌入式系统。其特点如下:
支持printf 和优化的字符串操作
支持malloc、free等内存操作
支持函数重入(不过这个支持对内存有压力,总之感觉弊大于利)
支持libm数学库(但一般不使用嵌入式浮点数,模拟成本稍高)
Newlib的函数是在单独的文件中实现的。如果不使用它们,它们将永远不会被链接。这一般不会导致目标文件突然增加。
newlib C库一般在制作裸机微控制器开发的交叉编译器时使用得比较多。
2 Crosstool-ng制作交叉编译器
Crosstool在早期是一个非常好的交叉编译生产工具,但是后来还不够完善,所以有人想出了一个更好的。
—— crosstool-ng(下一代crosstool)。其特点如下:
支持menuconfig(类似Linux内核配置),支持多种架构
可以选择多种不同的C库和其他模块来提供示例配置
支持多种主机编译环境:各种Linux发行版、Cygwin等。
接下来我们学习如何使用crosstool-ng制作ARM交叉编译器。
2.1 Crosstool-NG编译与安装
首先,我们到Crosstool-NG的官方网站(https://crosstool-ng.github.io/)下载其软件源码压缩包,并解压源码。
接下来进入源码路径,开始Linux系统下源码安装的三个步骤:/configure、make、make install。这里,可以在configure时通过--prefix选项指定将编译后的文件安装到当前路径。进行中
./configure可能会提示找不到help2man和libtool。这可能是系统没有安装或者安装的版本太低造成的。只需使用sudo apt install 命令安装相关系统命令即可。
上述命令编译安装成功后,可执行程序将被放置在install文件夹中。接下来我们可以测试ct-ng命令是否能够成功执行。接下来我们将使用这个程序来制作一个交叉编译器。
2.2 交叉编译器配置
在Crosstool-NG的安装路径中,有很多参考的交叉编译器示例配置。我们不需要自己设置所有选项。
0 启动配置,可以根据示例配置进行修改。
由于i.MX6ULL是ARM CortexA7核心处理器,但上述示例配置中并没有针对该架构的相关配置,所以我们在A8的基础上进行修改。两种架构大致相同。我们复制ARM CortexA8 的示例配置并将其命名为.config。接下来的ct-ng menuconfig会默认读取这个配置文件。
接下来使用export命令导出ct-ng命令所在的路径。如果使用SecureCRT远程登录Linux服务器,还需要使用export TERM=vt100命令配置TERM环境变量,否则后续配置可能无法进入。接下来,执行ct-ng menuconfig来配置交叉编译器生产。
下面是Crosstool-NG的配置界面,接下来我们需要在这里进行修改。配置过程中,使用上下方向键选择对应的选项,使用TAB键选择最下面或者:在Paths和misc options选项中,我们主要需要修改以下选项,修改指定下载的软件包的存储。路径${PWD}/tarballs和交叉编译器安装路径/opt/xtools/cortexA7:在Target options选项中,我们主要修改“Floating point”选项,因为iMX6ULL处理器有FPU,这里为了保持兼容性,选择软FP(FPU)。在工具链选项中,如果要复制交叉编译器以在其他机器上使用,可以选择“构建静态工具链”并修改“元组供应商字符串”选项以指定交叉编译器名称。在Operating System选项中,由于我们要移植的Linux内核的目标版本是5.10.x,所以这里选择的内核版本必须与开发板上移植的版本一致,否则在编译Linux内核时可能会出现兼容性问题未来。这里crosstool-NG默认的内核版本较低,需要将配置修改为我们想要的版本。以下选项配置依赖于“路径和其他选项”菜单中标记为“实验”选项的[*] 尝试功能。 “Linux 源” 选择(自定义位置)“自定义源位置”并将Linux 路径设置为(${PWD}/tarballs/linux-5.10.tar.xz)。接下来我们手动下载对应的Linux内核源码压缩包到此结束;在“Linux 版本”中,选择(比下面的任何版本都新);在C-library选项中,选择C库(glibc),其他其余选项使用默认值。我们将使用默认配置,不做任何修改。您还可以了解C编译器中的相关选项。配置完成后,返回主菜单,使用Tab键切换到Exit,然后选择Save退出。交叉编译器配置完成后,我们就准备开始交叉编译器的编译过程了。 2.3 交叉编译器编译在前面的配置中,我们计划将交叉编译器安装到系统的/opt/xtools路径下。这里我们首先需要以root权限创建这个文件夹,并赋予所有其他用户写入权限。在编译过程中,CrossTool-NG会下载制作交叉编译器所需的软件源码包,但部分软件包的下载地址可能已经过期。这种情况下,我们可以自己找到相关软件对应的版本包,然后手动下载到指定的压缩包存放路径,比如前面配置中指定的${PWD}/tarballs。以下是一些已知的无效文件。我们提前手动下载它们。编译开始后会自动下载其他需要的软件包。接下来我们开始交叉编译编译和制作过程。此过程的时间取决于PC 的性能。我的Linux服务器处理器是Intel(R) Xeon(R) CPU E31235 @ 3.20GHz,4核8线程,所以我使用ct-ng build.8命令同时编译8个进程。交叉编译器编译完成后,我们可以使用如下命令查看准备好的交叉编译器的相关版本信息: 2.4 交叉编译器测试接下来我们使用准备好的交叉编译器,交叉编译hello.c测试程序之前写好的,放到ARM开发板上运行测试。需要注意的是,由于新制作的交叉编译器与开发板上运行的C运行时版本不一致,所以这里必须添加-static进行静态链接,这样编译出来的程序才能在开发板上运行。下载并在ARM开发板上运行测试:版权声明。本文档所有文字资料均由凌云实验室郭工整理。主要用于凌云嵌入式Linux教学内部使用。版权归作者个人所有。未经本人授权,任何媒体、网站或个人不得转载、链接、转贴或以其他方式复制/发布。授权媒体、网站下载、使用时必须注明来源。违法者将依法追究责任。版权所有(C)2021 凌云物联网智能实验室郭工作者: 郭文学guowenxue@gmail.com