前言
文章基于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 的
buildroot 了。在开始讲解如何制作交叉编译器之前,我们首先来了解一下 C运行库。
glibc是gnu发布的libc库,也即c运行库。glibc是linux 系统中最底层的api(应用程序开发接口),几乎其它任何的运行库都会倚赖于glibc。glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了 许多其它一些必要功能服务的实现,主要的如下:
string,字符串处理
signal,信号处理
dlfcn,管理共享库的动态加载
direct,文件目录操作
elf,共享库的动态加载器,也即interpreter
iconv,不同字符集的编码转换
inet,socket接口的实现
intl,国际化,也即gettext的实现
io
linuxthreads
locale,本地化
login,虚拟终端设备的管理,及系统的安全访问
malloc,动态内存的分配与管理
nis
stdlib,其它基本功能
gcc 是编译器,基本上 Linux 下所有的程序(包括内核)都是 gcc 编译的,libc 当然也是。gcc 和 libc 是互相依赖的两个软件,它们合作的方式类似 Linux 系统的 "自举"。先在一个可以运行的带有老 libc 和
gcc 的系统上,用老 gcc 编译出一个新版本的 gcc + 老 libc,再用这个新 gcc 编译出一个新 gcc + 新
libc,再用这套新的组合编译整个新系统。
PC上常用的标准库glibc是一个非常宠大而完整的库,但早期对于嵌入式系统来说,由于Flash和RAM的 存储空间有线,其体积显得过于大了一些。uClibc的出现就是为了解决这个问题,uClibc尽可能的兼容
Glibc,大多数应用程序可以在很小或完全不修改的情况下就可能使用uClibc替代glibc。通过uClibc来代 替Glibc,可以在不改变应用程序功能的前提下,大大减少发布文件的大小,无论应用程序以静态链接来 编译,还是以动态链接形式编译。
uClibc比一般用于Linux发行版的C库GNU C Library (glibc)要小得多,glibc目标是要支持最大范围的硬件和内核平台的所有C标准,而uClibc专注于嵌入式Linux.很多功能可以根据空间需求进行取舍。现在uClibc更多运行于标准的以及无MMU的Linux系统上,支持i386,x86 64,ARM (big/little endian), AVR32,Blackfin,h8300,m68k,MIPS (big/little endian), PowerPC,SuperH (big/little endian),
SPARC,和v850等处理器。
由于当前嵌入式系统硬件性能的提升,用于存储程序的Flash空间和用于运行程序的RAM空间都有了大幅 提升,为了保证程序更大的兼容性,uClibc也逐步退出了历史的舞台了。
uClibc早期官网: uClibc最新官网:
uClibc h ttps://uclibc-ng.org/
EGLIBC(Embedded GLIBC,缩写为EGLIBC)是glibc的原创作组织FSF所新推出的glibc的一种变体, 目的在于将glibc用于嵌入式系统。它是GNU C 库(glibc)的一个分支,也采用GNU宽通用公共许可证
(LGPL)发布。它希望能应用于嵌入式系统,但它的源代码与可执行文件仍然保持与glibc一致。它的作 者宣称它不是glibc的一个分支,而是用来容纳glibc核心开发者拒绝采纳的patch。
2009年5月6日,因为与glibc核心开发者之间对程序发展方向的争议,Debian开发者宣布将要采用EGLIBC来取代glibc。Ubuntu自9.10后也采用了EGLIBC,Ark Linux也使用它。2014年初,官网上宣布,eglibc已经停止开发,因为现在的目标是在glibc上直接解决问题(goals are now being addressed directly in GLIBC),Debian开发者也恢复到使用glibc了。
在做一些单片机的裸机程序开发时,有时候最想要的是实现一个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库一般在制作单片机裸机开发的交叉编译器时,使用得比较多。
Crosstool早期是个很不错的交叉编译器制作工具,但是后来完善得不够好,于是有人弄出了个更好的
—— crosstool-ng(crosstool Next Generation)。其特点如下:
支持menuconfig(类似于Linux内核配置) 支持众多的架构
可选多种不同的C库等模块提供示例配置
支持多种主机编译环境:各种Linux发行版,Cygwin等。
接下来,我们学习了解一下如何使用 crosstool-ng 来制作一个ARM交叉编译器。
首先我们到 Crosstool-NG 的官方站点(crosstool-NG)下载其软件源码压缩包,并解压缩源码。
接下来进入到源码路径下,开始Linux系统下源码安装的三部曲: ./configure、make、 make install
。 这里在configure 时通过 --prefix 选项指定将编译生成的文件安装到当前路径下即可。在进行
./configure 时可能会提示 help2man、 libtool 找不到,这可能是系统没有安装或者安装的版本过低导致的,直接使用 sudo apt install 命令安装相关系统命令即可。
上面命令成功编译安装之后,可执行程序将会放到 install 文件夹下,接下来我们可以测试 ct-ng 命令是否能够成功执行。接下来我们将会使用该程序来制作交叉编译器。
在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 and misc options 选项中,我们主要要修改如下几个选项,修改指定下载的软件包存放路径
${PWD}/tarballs 和 交叉编译器的安装路径 /opt/xtools/cortexA7:
在 Target options 选项中,我们主要修改 “ Floating point” 选项,因为 iMX6ULL处理器带有 FPU,这里为了保持兼容性,选择 softfp (FPU)。
在 Toolchain options 选项中,如果想拷贝该交叉编译器给别的机器使用,则可以选中"Build Static Toolchain",另外修改 "Tuple's vendor string" 选项中指定交叉编译器名称。
在 Operating System 选项中,因为我们移植的Linux内核目标版本为 5.10.x, 所以这里内核的版本选择要跟开发板上移植的版本保持一致,否则今后编译Linux内核时可能会出现兼容性问题。在这里, crosstool-NG的默认内核版本较低,这里需要修改配置为我们想要的版本。下面的这些选项配置,依赖Paths and misc options 菜单中的 [*] Try features marked as EXPERIMENTAL选项。
"Source of linux" 选择 (Custom location)
"Custom source location" 里设置 Linux路径为 (${PWD}/tarballs/linux-5.10.tar.xz) ,接下来我们将会手动下载相应的Linux内核源码压缩包到这里;
"Version of linux" 里选择 (newer than anything below) ;
在 C-library 选项中,C library 选择 (glibc) ,其他使用默认
剩余的其它选项,我们就不作任何修改采用默认配置。关于 C compiler 编译器里的相关选项,大家也可以了解一下。
配置完成后回到主菜单,使用 Tab键 切换到 < Exit > ,然后选择保存退出即可。交叉编译器配置完成之后,接下来我们就准备开始交叉编译器的编译过程。
在前面的配置中,我们计划将交叉编译器安装到系统的 /opt/xtools 路径下,这里我们首先需要使用
root 权限创建这个文件夹,并给所有其他用户 写 权限。
CrossTool-NG在编译过程中,会下载制作交叉编译器所需要的软件源码包,但有些软件包的下载地址可 能已经失效,这时我们可以自己找到相关软件的相应版本软件包,然后手动下载到指定的压缩包存放路 径下,如前面配置中指定的 ${PWD}/tarballs 。下面是一些已知的失效文件,我们提前手动下载好,其它所需要的软件包将会在开始编译后自动下载。
接下来我们就开始交叉编译的编译制作过程,这个过程的时间依赖PC的性能。我的Linux服务器处理器 是Intel(R) Xeon(R) CPU E31235 @ 3.20GHz,4核8线程,所以我这里使用 ct-ng build.8 命令用8个进程同时编译。
交叉编译器编译完成之后,我们可以使用下面命令查看制作好的交叉编译器相关版本信息:
接下来我们使用制作好的交叉编译器,交叉编译之前写好的 hello.c 测试程序,并放到 ARM 开发板上运行测试。需要注意的是因为新制作的交叉编译器跟开发板上运行的C运行库版本不一致,这里必须加上 -
static 进行静态链接,这样编译生成的程序才能在开发板上运行。
ARM 开发板上下载运行测试:
版权声明
本文档所有内容文字资料由凌云实验室郭工编著,主要用于凌云嵌入式Linux教学内部使用,版权归属 作者个人所有。任何媒体、网站、或个人未经本人协议授权不得转载、链接、转帖或以其他方式复制发布/发表。已经授权的媒体、网站,在下载使用时必须注明来源,违者本人将依法追究责任。
Copyright (C)2021 凌云物网智科实验室·郭工
Author: GuoWenxue [email protected]