最新要闻
- 外交部:中国
- 天天关注:海关回应日本进口蜜瓜138元一瓣:没有的事儿
- 丹麦特斯拉车主在家充电6小时 赚了17美元 全球即时
- 每日快讯!紫光公开嵌入式多层SeDRAM内存:带宽、能效遥遥领先
- 特斯拉完胜 比亚迪彻底狂飙 车企半年考交卷了
- 热消息:安兔兔6月安卓手机性能榜出炉:vivo X90s一骑绝尘
- 焦点快播:别了,华夏人寿 注册资本565亿的瑞众人寿来了
- 今日要闻!海贼王中明哥几集被路飞打败_海贼王明哥和路飞他们打从第几集到第几集
- “小米汽车”非官方最新手绘图出炉:结合AIGC 造型更科幻|快消息
- 【全球独家】日本开卖500元方形西瓜:并不能吃
- 世界新消息丨保时捷卡宴的亲弟弟来了!新款大众途锐上市 售58.78万起
- 售价超过30万元/吨 国内单体最大碳酸锂项目投产:减少进口依赖-天天微资讯
- 全球快资讯:英国首推水葬:沸水煮遗体4小时 比火葬更环保对遗体更温和
- 北京一旅行社导游热射病去世
- 快递电子运单调查|半数快递面单未隐藏消费者个人信息
- 环球最资讯丨在地球上 我们也有了“空间站”!全球首创
手机
光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯
搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注
- 光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯
- 搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注
- 视点!美国首位女总统即将诞生?拜登恐怕要提前下岗,美政坛迎来变局?
- 当前速递!用理想仪器实现更好的颗粒 德国新帕泰克亮相CPHI & PMEC China获好评
- 微粒贷怎么申请开通 开通方法如下
- 焦点简讯:心疼!这位40岁的云南缉毒警,已是满头白发
家电
掌握嵌入式Linux编程2工具链-世界聚焦
2工具链
工具链是嵌入式Linux的第一个元素,也是你项目的起点。你将用它来编译所有将在你的设备上运行的代码。你在这个早期阶段做出的选择将对最终结果产生深远的影响。你的工具链应该能够通过使用处理器的最佳指令集来有效地利用你的硬件。它应该支持你所需要的语言,并对便携式操作系统接口(POSIX)和其他系统接口有一个坚实的实现。你的工具链应该在整个项目中保持不变。换句话说,一旦你选择了你的工具链,就一定要坚持下去。在一个项目中以不一致的方式改变编译器和开发库会导致微妙的错误。也就是说,当发现安全缺陷或错误时,最好还是要更新你的工具链。获得工具链可以像下载和安装TAR文件一样简单,也可以像从源代码构建整个东西一样复杂。在本章中,我采取了后一种方法,在crosstool-NG的工具的帮助下,我可以向你展示创建工具链的细节。稍后,在第6章 "选择构建系统 "中,我将转向使用构建系统生成的工具链,这是更常用的获得工具链的方法。当我们读到第14章 "从BusyBox runit开始 "时,我们将下载一个预先构建好的Linaro工具链来和Buildroot一起使用,这样可以节省一些时间。
技术要求
基于Linux的主机系统,安装有autoconf, automake, bison, bzip2, cmake, flex, g++, gawk, gcc, gettext, git, gperf, help2man, libncurses5-dev, libstdc++6, libtool, libtool-bin, make, patch, python3-dev, rsync, texinfo, unzip, wget, and xz-utils或其对应物。我推荐使用Ubuntu 20.04 LTS或更高版本,因为本章的练习在写作时都是在该Linux发行版上测试的。以下是在Ubuntu 20.04 LTS上安装所有必要软件包的命令:
$ sudo apt-get install autoconf automake bison bzip2 cmake \ flex g++ gawk gccgettext git gperf help2man libncurses5-dev libstdc++6 libtool \ libtool-bin makepatch python3-dev rsync texinfo unzip wget xz-utils
本章的所有代码都可以在本书GitHub仓库的Chapter02文件夹中找到:https://github.com/PacktPublishing/Mastering-Embedded-Linux-Programming-Third-Edition。
(相关资料图)
工具链的介绍
工具链是一套将源代码编译成可执行文件的工具,可以在你的目标设备上运行,包括编译器、链接器和运行时库。最初,你需要工具链来构建嵌入式Linux系统的其他三个要素:引导程序、内核和根文件系统。它必须能够编译用汇编、C和C++编写的代码,因为这些都是基础开源包中使用的语言。
通常情况下,Linux的工具链是基于GNU项目的组件(http://www.gnu.org),在写这篇文章时,大多数情况下仍是如此。然而,在过去的几年中,Clang编译器和相关的低级虚拟机(LLVM)项目(http://llvm.org)已经发展到了现在可以替代GNU工具链的程度。LLVM和基于GNU的工具链之间的主要区别是许可;LLVM用BSD许可,而GNU用GPL。
Clang也有一些技术上的优势,比如更快的编译和更好的诊断,但GNU GCC的优势在于与现有代码库的兼容性以及对各种架构和操作系统的支持。虽然花了一些年的时间,Clang现在可以编译嵌入式Linux所需的所有组件,是GNU的可行的替代方案。要了解更多的情况,请看https://www.kernel.org/doc/html/latest/kbuild/llvm.html。
关于如何使用Clang进行交叉编译,在https://clang.llvm.org/docs/CrossCompilation.html,有很好的描述。如果你想把它作为嵌入式Linux构建系统的一部分,EmbToolkit(https://embtoolkit.org)完全支持GNU和LLVM/Clang工具链,而且很多人正在努力将Clang用于Buildroot和Yocto项目。我将在第6章 "选择构建系统 "中介绍嵌入式构建系统。同时,本章主要讨论GNU工具链,因为它仍然是Linux中最流行和最成熟的工具链。
标准的GNU工具链由三个主要部分组成:
Binutils: 一套二进制实用程序,包括汇编器和链接器。它可以在http://gnu.org/software/binutils。
GNU编译器集合(GCC): 这些是C和其他语言的编译器,根据GCC的版本,包括C++、Objective-C、Objective-C++、Java、Fortran、Ada和Go。它们都使用一个共同的后端,产生汇编代码,并将其输入到GNU汇编器中。它可以在http://gcc.gnu.org/。
C库: 基于POSIX规范的标准化应用程序接口(API),它是应用程序进入操作系统内核的主要接口。应用程序的主要接口。正如我们在本章后面将看到的,有几个C语言库需要考虑。
除了这些,你还需要一份Linux内核头文件的副本,其中包含直接访问内核时需要的定义和常量。现在,你需要它们来编译C语言库,但是你以后在编写程序或编译与特定Linux设备交互的库时也需要它们,例如,通过Linux帧缓冲器驱动来显示图形。这不是简单的在你的内核源代码的include目录下复制头文件的问题。这些头文件只是为了在内核中使用,并且包含一些定义,如果在原始状态下用来编译普通的Linux应用程序,会引起冲突。
相反,你需要生成一套经过消毒的内核头文件,我在第5章 "构建根文件系统 "中已经说明了这一点。内核头文件是否由你将要使用的Linux的确切版本生成,通常并不重要。因为内核接口总是向后兼容的,所以只需要头文件来自与你在目标上使用的内核相同或更早的内核。大多数人认为GNU调试器(GDB)也是工具链的一部分,而且通常在这时就已经建立了。我将在第19章 "使用GDB进行调试 "中讨论GDB。
工具链的类型
对于我们的目的,有两种类型的工具链:
- 原生
这种工具链与它生成的程序在同一类型的系统(有时是同一实际系统)上运行。这是台式机和服务器的通常情况,它在某些类别的嵌入式设备上也开始流行。例如,运行Debian for ARM的Raspberry Pi就有自我托管的本地编译器。
- 交叉
这种工具链在与目标系统不同的类型上运行,允许在快速的桌面PC上进行开发,然后加载到嵌入式目标上进行测试。几乎所有的嵌入式Linux开发都是使用交叉开发工具链完成的,部分原因是大多数嵌入式设备由于缺乏计算能力、内存和存储空间而不太适合进行程序开发,但也因为它使主机和目标环境分离。当主机和目标机使用相同的架构时,例如x86_64,后一点尤其重要。在这种情况下,在主机上进行本地编译并简单地将二进制文件复制到目标机上是很诱人的。这在一定程度上是可行的,但很可能主机发行版比目标版更经常地收到更新,或者为目标版编写代码的不同工程师对主机开发库的版本略有不同。随着时间的推移,开发系统和目标系统将出现分歧,你将违反工具链应该在项目的整个生命周期内保持不变的原则。如果你能确保主机和目标构建环境是同步的,你就能使这种方法发挥作用。然而,更好的方法是保持主机和目标的分离,而交叉工具链就是这样做的方法。
然而,有一个支持本地开发的反驳理由。交叉开发会造成交叉编译你的目标所需的所有库和工具的负担。我们将在后面的 "交叉编译的艺术 "一节中看到,交叉开发并不总是简单的,因为许多开源包并不是被设计成以这种方式构建的。集成构建工具,包括Buildroot和Yocto项目,通过封装规则来帮助你交叉编译一系列你在典型的嵌入式系统中需要的软件包,但是如果你想编译大量的额外软件包,那么最好是原生编译它们。例如,使用交叉编译器为Raspberry Pi或BeagleBone构建一个Debian发行版将是非常困难的。相反,它们是被本地编译的。
从头开始创建一个本地构建环境并不容易。首先,你仍然需要交叉编译器来在目标上创建本地构建环境,然后用它来构建软件包。然后,为了在合理的时间内进行本地构建,你将需要一个由配置良好的目标板组成的构建农场,或者你可以使用Quick EMUlator(QEMU)来模拟目标。
CPU架构
工具链必须根据目标CPU的能力来构建,这包括以下内容:
- CPU架构: ARM、无互锁流水线阶段的微处理器(MIPS Microprocessor without Interlocked Pipelined Stages)、x86_64,等等。
- Big-或little-endian操作: 有些CPU可以在两种模式下运行,但每种模式下的机器代码是不同的。
- 浮点支持: 并非所有版本的嵌入式处理器都实现了硬件浮点单元,在这种情况下,工具链必须被配置为调用软件浮点库。
- 应用二进制接口(Application Binary Interface): 用于在函数调用之间传递参数的调用惯例。
在许多体系结构中,ABI在整个处理器系列中是不变的。值得注意的例外是ARM。ARM 架构在 2000 年代末过渡到扩展应用二进制接口 (EABI),导致以Extended Application Binary Interface前的 ABI 被命名为旧应用二进制接口 (OABI Old Application Binary Interface )。虽然OABI现在已经过时了,但你会继续看到对EABI的引用。从那时起,根据浮点参数的传递方式,EABI已经一分为二。最初的EABI使用通用(整数)寄存器,而较新的Extended Application Binary Interface Hard-Float(EABIHF)使用浮点寄存器。EABIHF的浮点运算速度明显更快,因为它不需要在整数和浮点寄存器之间进行复制,但它与没有浮点单元的CPU不兼容。因此,你必须在两个不兼容的ABI之间做出选择;你不能混合两者,因此你必须在这个阶段做出决定。GNU在工具链中的每个工具的名称上使用了一个前缀,它标识了可以生成的各种组合。它由一个用破折号隔开的三或四个组件组成,如下面所述:
- CPU: 这是CPU架构,如ARM、MIPS或x86_64。如果CPU有两种endian模式,可以通过添加el来区分小端模式或eb来区分大端模式。很好的例子是小字节的MIPS,mipsel,和大字节的ARM,armeb。
- 供应商: 这标识了工具链的提供者。例子包括buildroot、poky,或者只是未知。有时它被完全忽略了。
- 内核: 对于我们的目的,它总是linux。
- 作业系统: 用户空间组件的名称,可能是gnu或musl。ABI也可以附加在这里,所以对于ARM工具链,你可以看到gnueabi、gnueabihf、musleabi或musleabihf。
你可以通过使用gcc的-dumpmachine选项找到构建工具链时使用的元组。例如,你可以在主机上看到以下内容:
$ gcc -dumpmachinex86_64-linux-gnu
这个元组表示CPU为x86_64,内核为linux,用户空间为gnu。
重要提示:
当一个本地编译器安装在机器上时,通常会在工具链中创建没有前缀的工具链接,这样你就可以用gcc命令调用C编译器。下面是一个使用交叉编译器的例子:
$ mipsel-unknown-linux-gnu-gcc -dumpmachine
mipsel-unknown-linux-gnu-表示小-endian MIPS的CPU,未知的供应商,linux的内核,和gnu的用户空间。
选择C库
Unix操作系统的编程接口是由C语言定义的,现在由POSIX标准定义。C库是该接口的实现;它是Linux程序进入内核的门户,如下图所示。即使你用其他语言编写程序,也许是Java或Python,各自的运行时支持库最终都要调用C库,如图所示:
每当C库需要内核的服务时,它将使用内核的系统调用接口在用户空间和内核空间之间转换。可以绕过C库,直接进行内核系统调用,但是这很麻烦,几乎没有必要。有几个C库可以选择。主要的选择有以下几种:
glibc: 这是标准的GNU C库,可以在https://gnu.org/software/libc。它很大,而且直到最近还不是很好配置,但它是POSIX API的最完整的实现。它的许可证是LGPL 2.1。
musl libc: https://musl.libc.org。musl libc库相对较新,但作为GNU libc的小型和符合标准的替代品,它已经得到了很多关注。对于内存和存储空间有限的系统来说,它是一个不错的选择。它有一个MIT许可证。
uClibc-ng: https://uclibc-ng.org。u实际上是一个希腊的mu字符,表示这是微控制器的C库。它最初是为了与uClinux(用于没有内存管理单元的CPU的Linux)一起使用而开发的,但后来被改编为可用于完整的Linux。uClibc-ng库是原uClibc项目(https://uclibc.org)的,不幸的分支是该项目已经年久失修了。两者都是以LGPL 2.1授权的。
eglibc: http://www.eglibc.org/home。现在已经过时了,eglibc是glibc的分支,做了一些修改,使其更适合于嵌入式使用。在其他方面,eglibc增加了配置选项和对glibc没有覆盖的架构的支持,特别是PowerPC e500 CPU内核。在2.20版本中,eglibc的代码库被合并回glibc中。eglibc库不再被维护。
那么,该选择哪一个呢?我的建议是,只有当你使用uClinux时才使用uClibc-ng。如果你的存储空间或内存非常有限,那么musl libc是一个不错的选择,否则就使用glibc:
你对C库的选择可能会限制你对工具链的选择,因为并不是所有预置的工具链都支持所有的C库。
寻找工具链
对于你的交叉开发工具链,你有三个选择:你可以找到符合你需求的现成的工具链;你可以使个由嵌入式构建工具生成的工具链,这在第6章,选择构建系统中有所涉及;或者你可以按照本章后面的描述,自己创建。预制的交叉工具链是一个有吸引力的选择,因为你只需要下载和安装它,但是你被限制在那个特定的工具链的配置上,而且你要依赖于你得到它的人或组织。最有可能的是,这将是其中之一:
- SoC或电路板供应商。大多数供应商提供Linux工具链。
- 致力于为某个特定架构提供系统级支持的联盟。例如,Linaro, (https://www.linaro.org)有针对ARM架构的预建工具链。
- 第三方Linux工具供应商,如Mentor Graphics、TimeSys或MontaVista。
- 桌面Linux发行版的交叉工具包。例如,基于 Debian 的发行版有用于交叉编译 ARM、MIPS 和 PowerPC 目标的软件包。
- 集成的嵌入式构建工具产生的二进制SDK。Yocto项目在http://downloads.yoctoproject.org/releases/yocto/yocto-[版本]/toolchain有一些例子。
- 已经找不到的论坛的链接。
在所有这些情况下,你必须决定所提供的预建工具链是否符合你的要求。它是否使用你喜欢的C库?供应商是否会给你提供安全修复和错误的更新,请记住我在第一章 "起步 "中对支持和更新的评论。如果你的答案是否定的,那么你应该考虑创建你自己的。不幸的是,建立工具链不是一件容易的事。如果你真的想自己来做这件事,可以看看Cross Linux From Scratch(https://trac.clfs.org)。在那里,你可以找到关于如何创建每个组件的分步说明。一个更简单的选择是使用crosstool-NG,它将这个过程封装成一组脚本,并有菜单驱动的前端。不过,你仍然需要相当程度的知识,只是为了做出正确的选择。使用Buildroot或Yocto项目这样的构建系统更简单,因为它们会在构建过程中生成一个工具链。这是我的首选方案,正如我在第6章 "选择构建系统 "中所示。随着crosstool-NG的兴起,建立你自己的工具链当然是一个有效和可行的选择。接下来让我们看看如何做到这一点。
使用crosstool-NG构建工具链
参见:https://crosstool-ng.github.io/docs/install/
几年前,Dan Kegel写了一套用于生成跨开发工具链的脚本和makefiles,并称之为crosstool(http://kegel.com/crosstool/)。2007年,Yann E. Morin在此基础上创建了下一代的crosstool,即crosstool-NG(https://crosstool-ng.github.io)。今天,它是迄今为止从源头上创建独立的交叉工具链的最方便的方法。在本节中,我们将使用crosstool-NG来为BeagleBone Black和QEMU。
安装crosstool-NG
在你从源代码构建crosstool-NG之前,你首先需要在你的主机上安装本地工具链和一些构建工具。关于crosstool-NG的完整构建和运行时依赖项,请参见本章开头的技术要求部分。
为BeagleBone Black构建工具链
Crosstool-NG可以建立许多不同组合的工具链。为了使初始配置更容易,它附带了一套涵盖许多常见使用情况的样本。使用bin/ct-ng list-samples来生成这个列表。BeagleBone Black有一个TI AM335x SoC,它包含一个ARM Cortex A8内核和VFPv3浮点单元。由于BeagleBone Black有大量的RAM和存储空间,我们可以使用glibc作为C库。最接近的样本是arm-cortex_a8-linux-gnueabi。你可以通过在名称前加上show-来查看默认配置:
$ bin/ct-ng show-arm-cortex_a8-linux-gnueabi[G...] arm-cortex_a8-linux-gnueabi Languages : C,C++ OS : linux-5.16.9 Binutils : binutils-2.38 Compiler : gcc-11.2.0 C library : glibc-2.35 Debug tools : duma-2_5_15 gdb-11.2 ltrace-0.7.3 strace-5.16 Companion libs : expat-2.4.1 gettext-0.21 gmp-6.2.1 isl-0.24 libelf-0.8.13 libiconv-1.16 mpc-1.2.1 mpfr-4.1.0 ncurses-6.2 zlib-1.2.12 Companion tools :
这与我们的要求很接近,除了它使用eabi二进制接口,以整数寄存器传递浮点参数。我们更倾向于使用硬件浮点寄存器来实现这一目的,因为这样可以加快有浮点和双倍参数类型的函数调用速度。你以后可以改变配置,所以现在你应该选择这个目标配置:
$ bin/ct-ng arm-cortex_a8-linux-gnueabi CONF arm-cortex_a8-linux-gnueabi## configuration written to .config#***********************************************************Initially reported by: Yann E. MORINURL: http://ymorin.is-a-geek.org/***********************************************************Now configured for "arm-cortex_a8-linux-gnueabi"
使用配置菜单命令menuconfig审查配置并进行修改:
$ bin/ct-ng menuconfig
该菜单系统是基于Linux内核的menuconfig,因此,任何配置过内核的人都会熟悉用户界面的导航。如果没有,请参阅第4章,配置和构建内核,了解menuconfig的描述。
建议你做三个配置上的改变:
- 在Paths and misc options中,禁用Render the toolchain read-only(CT_PREFIX_DIR_RO).(ct_prefix_dir_ro)。
- Target options | Floating point,选择硬hardware (FPU) (CT_ARCH_FLOAT_HW)。
- Target options | neon,选择Use specific FPU。
如果你想在工具链安装后将库添加到工具链中,第一个选项是必要的,我将在后面的用库链接部分介绍。第二个选择eabihf二进制接口,原因在前面已经讨论过了。第三项是成功构建Linux内核所需要的。括号里的名字是存储在配置文件中的配置标签。当你做了修改后,退出menuconfig菜单,并像以前一样保存配置。现在你可以使用crosstool-NG来获取、配置,并按照你的规范来构建组件,输入以下命令:
$ bin/ct-ng build
构建将花费大约半小时,之后你会发现你的工具链出现在~/x-tools/arm-cortex_a8-linux-gnueabihf。 如果出现下载zlib失败,参考https://github.com/crosstool-ng/crosstool-ng/issues/1337,手动下载拷贝即可。
为QEMU构建工具链
在QEMU目标上,你将模拟具有ARM926EJ-S处理器内核的ARM-versatile PB评估板,它实现了ARMv5TE指令集。你需要生成符合规范的crosstool-NG工具链。该程序与BeagleBone Black的程序非常相似。你首先要运行bin/ct-ng list-samples来找到一个好的基础配置来工作。没有精确的配置,所以使用通用的目标,arm-unknown-linux-gnueabi。你选择它,如图所示,先运行distclean以确保没有以前构建时留下的工件:
$ bin/ct-ng distclean$ bin/ct-ng arm-unknown-linux-gnueabi
与BeagleBone Black一样,你可以查看配置并进行修改使用配置菜单命令bin/ct-ng menuconfig。只有一个改动是必要的:在Paths and misc options中,禁用Render the toolchain read-only(ct_prefix_dir_ro)。现在,用这里的命令构建工具链:$ bin/ct-ng build和以前一样,构建将需要大约半小时。工具链将被安装在~/x-tools/arm-unknown-linux-gnueabi。
工具链的剖析
ARM Cortex A8工具链在~/x-tools/arm-cortex_a8-linux-gnueabihf/bin目录下。在那里,你会发现交叉编译器,arm-cortex_a8-linux-gnueabihf-gcc。为了使用它,你需要用以下命令将该目录添加到你的路径中:$ PATH=~/x-tools/arm-cortex_a8-linux-gnueabihf/bin:$PATH
简单的helloworld程序:
#include #include int main (int argc, char *argv[]){ printf ("Hello, world!\n"); return 0;}
你可以这样编译它:
$ arm-cortex_a8-linux-gnueabihf-gcc helloworld.c -o helloworld
你可以通过使用file命令打印文件的类型来确认它已经被交叉编译了:
$ file helloworld helloworld: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 5.16.9, with debug_info, not stripped
了解你的交叉编译器
可以通过查询gcc发现很多东西。例如,要找到版本,你可以使用--版本:
$ arm-cortex_a8-linux-gnueabihf-gcc --versionarm-cortex_a8-linux-gnueabihf-gcc (crosstool-NG 1.25.0) 11.2.0Copyright (C) 2021 Free Software Foundation, Inc.This is free software; see the source for copying conditions. There is NOwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
要知道它是如何配置的,请使用-v:
$ arm-cortex_a8-linux-gnueabihf-gcc -vUsing built-in specs.COLLECT_GCC=arm-cortex_a8-linux-gnueabihf-gccCOLLECT_LTO_WRAPPER=/home/andrew/x-tools/arm-cortex_a8-linux-gnueabihf/libexec/gcc/arm-cortex_a8-linux-gnueabihf/11.2.0/lto-wrapperTarget: arm-cortex_a8-linux-gnueabihfConfigured with: /opt/crosstool/.build/arm-cortex_a8-linux-gnueabihf/src/gcc/configure --build=x86_64-build_pc-linux-gnu --host=x86_64-build_pc-linux-gnu --target=arm-cortex_a8-linux-gnueabihf --prefix=/home/andrew/x-tools/arm-cortex_a8-linux-gnueabihf --exec_prefix=/home/andrew/x-tools/arm-cortex_a8-linux-gnueabihf --with-sysroot=/home/andrew/x-tools/arm-cortex_a8-linux-gnueabihf/arm-cortex_a8-linux-gnueabihf/sysroot --enable-languages=c,c++ --with-cpu=cortex-a8 --with-fpu=neon --with-float=hard --with-pkgversion="crosstool-NG 1.25.0" --enable-__cxa_atexit --disable-libmudflap --disable-libgomp --disable-libssp --disable-libquadmath --disable-libquadmath-support --disable-libsanitizer --disable-libmpx --disable-libstdcxx-verbose --with-gmp=/opt/crosstool/.build/arm-cortex_a8-linux-gnueabihf/buildtools --with-mpfr=/opt/crosstool/.build/arm-cortex_a8-linux-gnueabihf/buildtools --with-mpc=/opt/crosstool/.build/arm-cortex_a8-linux-gnueabihf/buildtools --with-isl=/opt/crosstool/.build/arm-cortex_a8-linux-gnueabihf/buildtools --enable-lto --enable-threads=posix --enable-target-optspace --enable-plugin --enable-gold --disable-nls --disable-multilib --with-local-prefix=/home/andrew/x-tools/arm-cortex_a8-linux-gnueabihf/arm-cortex_a8-linux-gnueabihf/sysroot --enable-long-longThread model: posixSupported LTO compression algorithms: zlibgcc version 11.2.0 (crosstool-NG 1.25.0)
值得注意的是以下内容:
- --with-sysroot=/home/frank/x-tools/arm-cortex_a8-linux-gnueabihf/arm-cortex_a8-linux-gnueabihf/sysroot: 这是默认的sysroot目录;请看下面的解释。
- --enable-languages=c,c++: 使用这个,我们同时启用了C和C++ 。
- --with-cpu=cortex-a8: 代码是为ARM Cortex A8内核生成的。
- --with-float=hard:为浮点单元生成操作代码,并使用VFP寄存器作为参数。
- --enable-threads=posix:这将启用POSIX线程。这些是编译器的默认设置。你可以在gcc命令行中覆盖其中的大部分。例如,如果你想为不同的CPU编译,你可以通过在命令行中加入-mcpu来覆盖配置的设置--with-cpu,如下所示:
$ arm-cortex_a8-linux-gnueabihf-gcc -mcpu=cortex-a5 helloworld.c -o helloworld
你可以使用 --target-help 打印出可用的特定架构选项的范围,如下所示:
$ arm-cortex_a8-linux-gnueabihf-gcc --target-help
果你计划为每个目标创建工具链,那么在一开始就设置好是有意义的,因为这将减少以后出错的风险。另一方面,如果你想构建通用的工具链,并且准备在为特定目标构建时提供正确的设置,那么你应该使基础工具链通用化,这就是Yocto项目处理事情的方式。前面的例子都是遵循Buildroot的理念。
sysroot、库和头文件
工具链的系sysroot是包含库、头文件和其他配置文件的子目录的目录。它可以在配置工具链时通过 --with-sysroot= 设置,也可以在命令行中通过 --sysroot= 设置。 你可以通过使用 -print-sysroot 看到默认的系统根目录的位置:
$ arm-cortex_a8-linux-gnueabihf-gcc -print-sysroot/home/andrew/x-tools/arm-cortex_a8-linux-gnueabihf/arm-cortex_a8-linux-gnueabihf/sysroot
你会在sysroot中找到以下子目录:
- lib: 包含C库和动态链接器/加载器的共享对象,ld-linux
- usr/lib: C库的静态库存档文件,以及随后可能安装的任何其他库。
- usr/include: 包含所有库的头文件
- usr/bin: 包含在目标机上运行的实用程序,如ldd命令
- usr/share: 用来进行本地化和国际化
- sbin: 提供ldconfig工具,用于优化库的加载路径。
很明显,这些工具中的一些在开发主机上需要用来编译程序,而另一些,例如共享库和ld-linux,在运行时需要在目标上使用。
工具链中的其他工具
下面是调用GNU工具链中其他各种组件的命令列表,并附有简要说明:
- addr2line: 通过读取可执行文件中的调试符号表,将程序地址转换为文件名和数字。在对系统崩溃报告中打印出来的地址进行解码时,它非常有用。
- ar: 归档工具,用于创建静态库。
- as: 这是GNU的汇编程序。
- c++filt: 这是用来拆解C++和Java的符号。
- cpp: 这是C语言的预处理器,用于扩展#define、#include和其他类似的指令。你很少需要单独使用它。
- elfedit: 这是用来更新ELF文件的ELF头。
- g++: 这是GNU的C++前端,它假定源文件中含有C++代码。
- gcc: 这是 GNU C 前端,它假定源文件包含 C 代码。
- gcov: 代码覆盖工具。
- gdb: 这是 GNU 调试器。
- gprof: 程序剖析工具。
- ld: 这是GNU的连接器。
- nm: 它列出对象文件中的符号。
- objcopy: 用来复制和翻译对象文件。
- objdump: 用来显示对象文件的信息。
- ranlib: 它在静态库中创建或修改索引,使链接阶段更快。
- readelf: 它显示ELF对象格式的文件信息。
- size: 这列出了部分大小和总大小。
- strings: 这显示文件中的可打印字符的字符串。
- strip: 用于剥离调试符号表的对象文件,从而使其更小。通常情况下,你会剥离所有被放到目标上的可执行代码。
C库
C库不是单一的库文件。它由四个主要部分组成,共同实现POSIX API:
- libc:主C库,包含众所周知的POSIX函数,如printf、open、close、read、write,等等。
- libm: 包含数学函数,如cos、exp和log
- libpthread: 包含了所有的POSIX线程函数,名称以pthread开头的POSIX线程函数。
- librt: 拥有POSIX的实时扩展,包括共享内存和异步I/O
第一个库,libc,总是被链接进去的,但其他库必须用-l选项明确地链接。-l的参数是去掉lib后的库名。例如,通过调用sin()来计算正弦函数的程序可以用-lm链接到libm:
$ arm-cortex_a8-linux-gnueabihf-gcc myprog.c -o myprog -lm
你可以通过使用readelf命令来验证哪些库已经在这个或其他程序中被链接:
$ arm-cortex_a8-linux-gnueabihf-readelf -a code/helloworld | grep "Shared library" 0x00000001 (NEEDED) Shared library: [libc.so.6]
共享库需要运行时链接器:
$ arm-cortex_a8-linux-gnueabihf-readelf -a code/helloworld | grep "program interpreter" [Requesting program interpreter: /lib/ld-linux-armhf.so.3]
链接库
你为Linux编写的任何应用程序,无论是C语言还是C++语言,都会与lic 库链接。你甚至不需要告诉gcc或g++去做,因为它总是链接libc。其他你可能想要链接的库必须通过-l选项明确指定。
库的代码可以用两种不同的方式连接:静态的,意味着你的应用程序所调用的所有库函数和它们的依赖关系都是从库的归档文件中提取并绑定到你的可执行文件中;动态的,意味着对库文件和这些文件中的函数的引用是在代码中生成的,但实际的连接是在运行时动态进行的。
静态库
如果你正在构建小程序,静态链接会更简单,避免复制运行时库文件和链接程序。它也会更小,因为只链接你的应用程序使用的代码,而不是提供整个C库。如果你需要在存放运行库的文件系统可用之前运行程序,静态链接也很有用。
你可以通过在命令行中添加-static来静态链接所有库:
$ arm-cortex_a8-linux-gnueabihf-gcc -static helloworld.c -o helloworld-static$ ls -lh he*-rwxrwxr-x 1 andrew andrew 12K 6月 21 15:39 helloworld-rwxrwxr-x 1 andrew andrew 123 6月 26 15:58 helloworld.c-rwxrwxr-x 1 andrew andrew 2.9M 6月 26 15:58 helloworld-static
二进制文件的大小急剧增加:
静态链接从库存档中提取代码,通常命名为lib[name].a。在前面的例子中,它是libc.a,它在[sysroot]/usr/lib中:
$ export SYSROOT=$(arm-cortex_a8-linux-gnueabihf-gcc -print-sysroot)$ cd $SYSROOT$ ls -l usr/lib/libc.a-rw-r--r-- 1 frank frank 31871066 Oct 23 15:16 usr/lib/libc.a
创建静态库就像使用ar命令创建对象文件的归档一样简单。如果我有两个名为test1.c和test2.c的源文件,我想创建一个名为libtest.a的静态库,那么我将做如下操作:
$ arm-cortex_a8-linux-gnueabihf-gcc -c test1.c$ arm-cortex_a8-linux-gnueabihf-gcc -c test2.c$ arm-cortex_a8-linux-gnueabihf-ar rc libtest.a test1.o test2.o$ ls -ltotal 24-rw-rw-r-- 1 frank frank 2392 Oct 9 09:28 libtest.a-rw-rw-r-- 1 frank frank 116 Oct 9 09:26 test1.c-rw-rw-r-- 1 frank frank 1080 Oct 9 09:27 test1.o-rw-rw-r-- 1 frank frank 121 Oct 9 09:26 test2.c-rw-rw-r-- 1 frank frank 1088 Oct 9 09:27 test2.o$ arm-cortex_a8-linux-gnueabihf-gcc helloworld.c -ltest -L../libs -I../libs -o helloworld
静共享库
部署库的更常见的方式是在运行时链接的共享对象,这可以更有效地利用存储和系统内存,因为只需要加载一份代码。它还使更新库文件变得容易,而不必重新链接所有使用它们的程序。
共享库的目标代码必须是独立于位置的,这样运行时链接器就可以自由地在内存中的下一个空闲地址处找到它。要做到这一点,在gcc中加入-fPIC参数,然后用-shared选项来链接它:
$ arm-cortex_a8-linux-gnueabihf-gcc -fPIC -c test1.c$ arm-cortex_a8-linux-gnueabihf-gcc -fPIC -c test2.c$ arm-cortex_a8-linux-gnueabihf-gcc -shared -o libtest.so test1.o test2.o
这样就创建了共享库,libtest.so。要用这个库链接一个应用程序,你可以添加-ltest,就像上一节中提到的静态情况一样,但这次代码不包括在可执行文件中。相反,有一个库的引用,运行时链接器必须解决这个问题:
$ arm-cortex_a8-linux-gnueabihf-gcc helloworld.c -ltest -L../libs -I../libs -o helloworld$ MELP/list-libs helloworld [Requesting program interpreter: /lib/ld-linux-armhf.so.3]0x00000001 (NEEDED) Shared library: [libtest.so.6]0x00000001 (NEEDED) Shared library: [libc.so.6]
这个程序的运行时链接器是/lib/ld-linux-armhf.so.3,它必须存在于目标的文件系统中。链接器将在默认的搜索路径中寻找libtest.so: /lib和/usr/lib。如果你想让它也寻找其他目录中的库,你可以在LD_LIBRARY_PATH壳变量中放置用冒号分隔的路径列表:
$ export LD_LIBRARY_PATH=/opt/lib:/opt/usr/lib
共享库的版本号
共享库的好处之一是,它们可以独立于使用它们的程序而被更新。库的更新有两种类型:
- 向后兼容的方式修复错误或添加新功能的更新。
- 破坏与现有程序的兼容性的更新
GNU/Linux有版本管理方案来处理这两种情况。
每个库都有发布版本和接口号。发行版本是简单的字符串,附加在库的名称上;例如,JPEG图像库libjpeg目前的发行版本是8.2.2,所以库的名称是libjpeg.so.8.2.2。名为libjpeg.so的符号链接到libjpeg.so.8.2.2,这样,当你用-ljpeg编译程序时,就会与当前的版本链接。如果你安装了8.2.3版本,链接就会更新,你就会用这个版本的链接。现在假设9.0.0版本出现了,它打破了向后的兼容性。现在libjpeg.so的链接指向libjpeg.so.9.0.0,所以任何新的程序都会与新的版本链接,当libjpeg的接口发生变化时,可能会出现编译错误,开发人员可以修复。目标机上任何没有重新编译的程序都会以某种方式失败,因为它们仍在使用旧的接口。这时,soname的对象就有帮助了。soname编码了库建立时的接口号,运行时链接器在加载库时使用它。它的格式是:<库名>.so.<接口号>。对于libjpeg.so.8.2.2,子名是libjpeg.so.8,因为libjpeg共享库建立时的接口号是8:
$ readelf -a /usr/lib/x86_64-linux-gnu/libjpeg.so.8.2.2 | grep SONAME0x000000000000000e (SONAME) Library soname: [libjpeg.so.8]
任何用它编译的程序都会在运行时请求libjpeg.so.8,这将是目标上对libjpeg.so.8.2.2的符号链接。当libjpeg的9.0.0版本被安装时,它的子名将是libjpeg.so.9,因此有可能在同一个系统上安装两个不兼容的同一库的版本。用libjpeg.so.8..链接的程序将加载libjpeg.so.8,而用libjpeg.so.9..链接的程序将加载libjpeg.so.9。
这就是为什么,当你看/usr/lib/x86_64-linux-gnu/libjpeg*的目录列表时,你会发现这四个文件:
- libjpeg.a: 这是用于静态连接的库存档。
- libjpeg.so -> libjpeg.so.8.2.2: 这是符号链接,用于动态链接。
- libjpeg.so.8 -> libjpeg.so.8.2.2: 这是符号链接,在运行时加载库时使用。
- libjpeg.so.8.2.2: 这是实际的共享库,在编译时和运行时都使用。
前两个库只需要在主机上构建,后两个库在运行时需要在目标机上使用。虽然你可以直接从命令行中调用各种GNU交叉编译工具,但这种技术并不能超越helloworld这。为了真正有效地进行交叉编译,我们需要把交叉工具链和构建系统结合起来。
参考资料
- 软件测试精品书籍文档下载持续更新 https://github.com/china-testing/python-testing-examples 请点赞,谢谢!
- 本文涉及的python测试开发库 谢谢点赞! https://github.com/china-testing/python_cn_resouce
- python精品书籍下载 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
交叉编译的艺术
拥有有效的交叉工具链是起点,而不是终点。在某些时候,你会想开始交叉编译你在目标上需要的各种工具、应用程序和库。它们中的许多都是开源包,每一个都有自己的编译方法和自己的特殊性。有一些常见的构建系统,包括以下几种:
- 纯粹的makefiles,其中的工具链通常由make变量CROSS_COMPILE控制
- Autotools: GNU构建系统
- CMake(https://cmake.org)
即使是构建基本的嵌入式Linux系统,也需要Autotools和makefiles。CMake是跨平台的,多年来被越来越多的人采用,特别是在C++社区。在本节中,我们将介绍这三种构建工具。
简单的makefile
一些重要的软件包的交叉编译非常简单,包括Linux内核、U-Boot引导程序和BusyBox。对于每一软件包,你只需要把工具链的前缀放在make变量CROSS_COMPILE中,例如,arm-cortex_a8-linux-gnueabi-。注意后面的破折号-。因此,要编译BusyBox,你可以这样输入:
$ make CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf-
或者,你可以把它设置为shell变量:
$ export CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf-$ make
在U-Boot和Linux的情况下,你还必须将make变量ARCH设置为它们所支持的机器架构之一。
Autotools和CMake都可以生成makefile。Autotools只生成makefiles,而CMake则支持其他构建项目的方式,这取决于我们所针对的我们的目标平台(在我们的例子中严格来说是Linux)。
Autotools
Autotools这个名字是指一组工具,在许多开源项目中被用作构建系统。这些组件,连同相应的项目页面,如下所示:
- GNU Autoconf (https://www.gnu.org/software/autoconf/autoconf.html)
- GNU Automake (https://www.gnu.org/savannah-checkouts/gnu/automake/)
- GNU Libtool (https://www.gnu.org/software/libtool/libtool.html)
- Gnulib (https://www.gnu.org/software/gnulib/)
Autotools的作用是抹平软件包可能被编译的不同类型的系统之间的差异,考虑到不同版本的编译器、不同版本的库、不同位置的头文件以及与其他软件包的依赖关系。使用Autotools的软件包有一个名为configure的脚本,它检查依赖关系,并根据发现的情况生成makefiles。configure脚本还可以让你有机会启用或禁用某些功能。你可以通过运行./configure --help找到所提供的选项。要配置、构建和安装本地操作系统的软件包,你通常要运行以下三个命令:
$ ./configure$ make$ sudo make install
Autotools也能够处理交叉开发。你可以通过设置这些shell变量来影响配置的脚本的行为:
- CC: C编译器的命令。
- CFLAGS: 额外的C编译器标志。
- CXX: C++编译器命令。
- CXXFLAGS: 额外的C++编译器标志。
- LDFLAGS: 额外的链接器标志;例如,如果你在非标准的目录
下有库,你将把它添加到库搜索路径上 加入 -L 。 - LIBS: 包含一个要传递给链接器的额外库的列表;例如、 -lm表示数学库。
- CPPFLAGS: 包含C/C++预处理器的标志;例如,你可以添加-I
来搜索非标准目录中的头文件 。 - CPP:要使用的C预处理器。有时只设置CC变量就足够了,如下所示:
$ CC=arm-cortex_a8-linux-gnueabihf-gcc ./configure
在其他时候,这将导致类似这样的错误:
[…]checking for suffix of executables...checking whether we are cross compiling... configure: error: in "/home/frank/sqlite-autoconf-3330000":configure: error: cannot run C compiled programs.If you meant to cross compile, use "--host".See "config.log" for more details
失败的原因是,configure经常试图通过编译代码片段并运行它们来发现工具链的能力,如果程序已经被交叉编译,这就无法工作。
注意:当你进行交叉编译时,把 --host=
Autotools理解在编译软件包时可能涉及的三种不同类型的机器:
- Build: 构建软件包的计算机,默认为当前机器。
- Host: 程序将要运行的计算机。对于本地编译,这部分留空,默认为与Build相同的计算机。当你进行交叉编译时,把它设置为你的工具链的元组。
- Target: 程序将生成代码的计算机。你会在构建交叉编译器时设置这个。因此,为了交叉编译,你只需要覆盖主机,如下所示:
$ CC=arm-cortex_a8-linux-gnueabihf-gcc ./configure --host=arm-cortex_a8-linux-gnueabihf
最后要注意的是,默认的安装目录是
$ CC=arm-cortex_a8-linux-gnueabihf-gcc ./configure --host=arm-cortex_a8-linux-gnueabihf
例子--SQLite
$ wget http://www.sqlite.org/2020/sqlite-autoconf-3330000.tar.gz$ tar xf sqlite-autoconf-3330000.tar.gz$ cd sqlite-autoconf-3330000$ CC=arm-cortex_a8-linux-gnueabihf-gcc ./configure --host=arm-cortex_a8-linux-gnueabihf --prefix=/usr # 日志config.log$ make # 或 $ make DESTDIR=$(arm-cortex_a8-linux-gnueabihf-gcc -print-sysroot) install
你可能会发现,最后的命令会因为文件权限错误而失败。crosstool-NG工具链默认是只读的,这就是为什么在构建它时将CT_PREFIX_DIR_RO设置为y是很有用的。另一个常见的问题是,工具链被安装在系统目录下,例如/opt或/usr/local。在这种情况下,你在运行安装时将需要root权限。安装后,你应该发现各种文件已经被添加到你的工具链中:
/usr/bin: sqlite3: 这是一个SQLite的命令行接口,你可以在目标机上安装和运行。 /usr/lib: libsqlite3.so.0.8.6, libsqlite3.so.0, libsqlite3.s, libsqlite3.la, libsqlite3.a: 这些是共享和静态库。 /usr/lib/pkgconfig: sqlite3.pc: 这是软件包的配置文件,如下一节所述。 /usr/lib/include: sqlite3.h, sqlite3ext.h: 这些是头文件。 /usr/share/man/man1: sqlite3.1: 这是手册页。
现在你可以编译使用sqlite3的程序,在 链接阶段添加-lsqlite3:$ arm-cortex_a8-linux-gnueabihf-gcc -lsqlite3 sqite-test.c -o sqite-test
$ arm-cortex_a8-linux-gnueabihf-gcc -lsqlite3 sqlite-test.c -o sqlite-test
这里,sqlite-test.c是调用SQLite函数的假想程序。由于sqlite3已经被安装到系统根中,编译器会顺利找到头文件和库文件。如果它们被安装在其他地方,你将不得不添加-L
为了交叉编译一个库或包,它的依赖项首先需要被交叉编译。Autotools依靠pkg-config工具来收集关于由Autotools交叉编译的软件包的重要信息。
包的配置
包配置工具pkg-config(https://www.freedesktop.org/wiki/Software/pkg-config/)通过在[sysroot]/usr/lib/pkgconfig中保存Autotools包的数据库,帮助跟踪哪些包被安装,以及每个包需要哪些编译标志。例如,SQLite3的数据库被命名为sqlite3.pc,包含了其他需要使用它的包所需要的基本信息:
$ cat $(arm-cortex_a8-linux-gnueabihf-gcc -print-sysroot)/usr/lib/pkgconfig/sqlite3.pc# Package Information for pkg-configprefix=/usrexec_prefix=${prefix}libdir=${exec_prefix}/libincludedir=${prefix}/includeName: SQLiteDescription: SQL database engineVersion: 3.33.0Libs: -L${libdir} -lsqlite3Libs.private: -lm -ldl -lpthreadCflags: -I${includedir}
你可以使用 pkg-config 来提取信息,并将其直接反馈给 gcc。就像libsqlite3这样的库而言,你想知道库的名字(--libs)和任何特殊的C标志(--cflags):
$ pkg-config sqlite3 --libs --cflagsPackage sqlite3 was not found in the pkg-config search path.Perhaps you should add the directory containing "sqlite3.pc"to the PKG_CONFIG_PATH environment variableNo package "sqlite3" found
失败的原因是它在主机的系统根中寻找,而主机上还没有安装libsqlite3的开发包。你需要通过设置PKG_CONFIG_LIBDIR壳变量将其指向目标工具链的系统根:
$ export PKG_CONFIG_LIBDIR=$(arm-cortex_a8-linux-gnueabihf-gcc \-print-sysroot)/usr/lib/pkgconfig$ pkg-config sqlite3 --libs --cflags-lsqlite3
现在的输出是-lsqlite3。在这种情况下,你已经知道了,但通常你不会知道,所以这是有价值的技术。编译的最终命令将是如下:
$ export PKG_CONFIG_LIBDIR=$(arm-cortex_a8-linux-gnueabihf-gcc \-print-sysroot)/usr/lib/pkgconfig$ arm-cortex_a8-linux-gnueabihf-gcc $(pkg-config sqlite3 --cflags --libs) \sqlite-test.c -o sqlite-test
许多配置脚本会读取由pkg-config生成的信息。这在交叉编译时可能会导致错误,我们接下来会看到。
交叉编译的问题
sqlite3是行为良好的软件包,可以很好地进行交叉编译,但并不是所有的软件包都是如此。典型的痛点包括以下几个方面:
- zlib等库的自制构建系统,其配置脚本的行为与上一节所述的Autotools configure不一样
- 配置脚本从主机上读取 pkg-config 信息、头文件和其他文件,而无视 --host 覆盖。
- 坚持尝试运行交叉编译代码的脚本
每种情况都需要仔细分析错误,给配置脚本增加参数以提供正确的信息,或者给代码打补丁以完全避免问题。请记住,软件包可能有许多依赖关系,特别是那些使用GTK或Qt的图形界面的程序,或者处理多媒体内容的程序。举个例子,mplayer,这是一个流行的播放多媒体内容的工具,与100多个库有依赖关系。要想把它们全部建立起来,需要花费数周的时间。
因此,我不建议以这种方式为目标手动交叉编译组件,除非没有其他选择,或者需要编译的软件包数量很少。更好的方法是使用Buildroot或Yocto项目这样的构建工具,或者通过为你的目标架构设置本地构建环境来完全避免这个问题。现在你可以明白为什么像Debian这样的发行版总是在本地编译了。
CMake
CMake更像是一个元构建系统,因为它依赖于底层平台的本地工具来构建软件。在Windows上,CMake可以为Microsoft Visual Studio生成项目文件,在macOS上,它可以为Xcode生成项目文件。与每个主要平台的主要IDE集成不是简单的任务,这解释了CMake作为领先的跨平台构建系统解决方案的成功。CMake也可以在Linux上运行,在那里它可以和你选择的交叉编译工具链一起使用。要配置、构建和安装本地Linux操作系统的软件包,请运行以下命令:
$ cmake .$ make$ sudo make install
在Linux上,本地构建工具是GNU make,因此CMake默认生成makefile文件供我们构建使用。很多时候,我们想进行源外构建,以便对象文件和其他构建工件与源文件保持分离。要在名为_build的子目录中配置源外构建,请运行以下命令:
$ mkdir _build$ cd _build$ cmake ..
这将在CMakeLists.txt所在的项目目录下的_build子目录中生成makefiles。CMakeLists.txt文件相当于基于Autotools的项目的CMake配置脚本。然后,我们可以从_build目录中构建项目的源代码,并像以前一样安装软件包:
$ make$ sudo make install
CMake使用绝对路径,所以一旦生成了makefiles,_build子目录就不能被复制或移动,否则任何后续的make步骤都可能失败。注意,CMake默认将软件包安装到系统目录中,如/usr/bin,即使是源外构建。要生成 makefiles 以使 make 将软件包安装在 _build 子目录中,请用下面的命令替换之前的 cmake 命令:
$ cmake .. -D CMAKE_INSTALL_PREFIX=../_build
我们不再需要在make install前加上sudo,因为我们不需要升高权限来复制包文件到_build目录中。同样地,我们可以使用另一个CMake命令行选项来生成用于交叉编译的makefiles:
$ cmake .. -D CMAKE_C_COMPILER="/usr/local/share/x-tools/arm-cortex_a8-linux-gnueabihf-gcc"
但是用CMake进行交叉编译的最佳做法是创建工具链文件,除了针对嵌入式Linux的其他相关变量外,还要设置CMAKE_C_COMPILER和CMAKE_CXX_COMPILER。当我们以模块化的方式设计我们的软件时,CMake的工作效果最好,它在库和库之间强制执行清楚地定义了库和组件之间的API边界。下面是一些在CMake中反复出现的关键术语:
- target: 一个软件组件,如库或可执行文件。
- properties: 包括构建目标所需的源文件、编译器选项和链接库。
- package:CMake文件,用于配置目标: CMake文件,用于配置外部目标的构建,就像它被定义在你的CMakeLists.txt本身一样。例如,如果我们有一个名为dummy的基于CMake的可执行文件,需要依赖SQLite,我们可以定义以下CMakeLists.txt:
cmake_minimum_required (VERSION 3.0)project (Dummy)add_executable(dummy dummy.c)find_package (SQLite3)target_include_directories(dummy PRIVATE ${SQLITE3_INCLUDE_DIRS})target_link_libraries (dummy PRIVATE ${SQLITE3_LIBRARIES})
CMake为流行的C和C++包配备了大量的搜索器,包括OpenSSL、Boost和protobuf,这使得本地开发比我们只使用纯粹的makefiles更有成效。PRIVATE修饰符可以防止诸如头文件和标志等细节泄露到假目标之外。当被构建的目标是库而不是可执行文件时,使用PRIVATE更有意义。在使用CMake定义你自己的目标时,把目标看作是模块,并试图最小化其暴露的表面区域。只有在绝对必要时才使用PUBLIC修饰符,并对仅有头文件的库使用INTERFACE修饰符。
将你的应用程序建模为一个具有目标之间的边的依赖图。这个图不仅应该包括你的应用程序直接链接到的库,还应该包括任何横向的依赖关系。为了获得最佳效果,请删除图中的任何循环或其他不必要的独立性。在你开始编码之前,通常最好进行这个练习。一个干净的、易于维护的CMakeLists.txt和一个无人问津的乱七八糟的东西之间,稍加计划就能产生不同的效果。
总结
工具链始终是你的起点;接下来的一切都取决于是否有有效的、可靠的工具链。
你可以从什么都不做开始,只用工具链--也许是用crosstool-NG构建的,或者是从Linaro下载的--用它来编译你在目标上需要的所有软件包。或者你可以获得工具链,作为使用Buildroot或Yocto项目等构建系统从源代码生成的发行版的一部分。小心那些作为硬件包的一部分免费提供给你的工具链或发行版;它们通常配置很差而且没有得到维护。
一旦你有了一个工具链,你就可以用它来构建你的嵌入式Linux系统的其他组件。在下一章中,你将学习引导程序,它将你的设备带入生活并开始启动过程。我们将使用本章中建立的工具链为BeagleBone Black建立一个工作的引导程序。进一步阅读这里有几段视频,记录了写作时交叉工具链和构建系统的技术状况:Bernhard "Bero" Rosenkränzer的《2020年工具链和交叉编译器的新视角》:https://www.youtube.com/watch?v=BHaXqXzAs0Y用于模块化设计的现代CMake,作者Mathieu Ropert:https://www.youtube.com/watch?v=eC9-iRN2b04
关键词:
掌握嵌入式Linux编程2工具链-世界聚焦
C语言实现顺序表的基本操作
外交部:中国
天天关注:海关回应日本进口蜜瓜138元一瓣:没有的事儿
丹麦特斯拉车主在家充电6小时 赚了17美元 全球即时
每日快讯!紫光公开嵌入式多层SeDRAM内存:带宽、能效遥遥领先
特斯拉完胜 比亚迪彻底狂飙 车企半年考交卷了
热消息:安兔兔6月安卓手机性能榜出炉:vivo X90s一骑绝尘
焦点快播:别了,华夏人寿 注册资本565亿的瑞众人寿来了
Jmeter学习之四_kingbaseV8R6数据库的简单验证 环球今亮点
日本企业信心有所改善 避险日债周一买盘下降
债市日报:7月3日
今日要闻!海贼王中明哥几集被路飞打败_海贼王明哥和路飞他们打从第几集到第几集
“小米汽车”非官方最新手绘图出炉:结合AIGC 造型更科幻|快消息
【全球独家】日本开卖500元方形西瓜:并不能吃
世界新消息丨保时捷卡宴的亲弟弟来了!新款大众途锐上市 售58.78万起
售价超过30万元/吨 国内单体最大碳酸锂项目投产:减少进口依赖-天天微资讯
全球快资讯:英国首推水葬:沸水煮遗体4小时 比火葬更环保对遗体更温和
北京一旅行社导游热射病去世
面向对象综合案例练习 天天热头条
世界热讯:记录--多行标签超出展开折叠功能
世界焦点!Linux下PAM认证详解(以centos7为例)
快递电子运单调查|半数快递面单未隐藏消费者个人信息
环球最资讯丨在地球上 我们也有了“空间站”!全球首创
“该有的一个也不能少”!理想ONE迎来OTA升级 新增任务大师等功能
1万4都拿不出来?威马汽车成失信被执行人 创始人沈晖被限高
全面超越特斯拉Model 3 广汽昊铂Hyper GT正式上市:21.99万起 报资讯
今亮点!《最终幻想16》偷盘哥后续:是个学生 证据确凿但还在嘴硬
泰尔股份:公司的激光产品及服务广泛应用于船舶制造、工程机械、钢结构等_当前消息
Linux设置字符编码 全球快播
视觉冲击力!水球图让数据一览无余_世界新消息
潼南民生村镇银行:金融助力 小葡萄“串”起致富路|今日热闻
世界今亮点!国产第三大晶圆代工厂晶合集成:持续提升55nm产能 40nm高压获重大成果
焦点!仅重500g搭载7840U!GPD新Win掌机真机现身
百事通!新卡来得很慢 老卡走得很快!AMD ROCm开发平台放弃Vega
宝马i3销量暴增 降价十万的“油改电”到底香不香 百事通
天天热推荐:墨西哥一市长连续2年与鳄鱼结婚:如此联姻当地已存在230年
渭南:纳凉晚会话交安
每日速讯:Mybatis-Plus的详细使用
天天百事通!华为扫地僧:揭秘IoT+鸿蒙帮助企业突围物联网安全问题
【新华500】新华500指数(989001)3日涨1.28% 全球热点
网红经济学家任泽平新动向!朋友圈宣称转型美妆博主,宣传话术暗藏玄机 环球热消息
三星独家手机支付技术 Galaxy手机更新后封杀MST:我国早已淘汰 天天热议
盲订超过2万台!比亚迪腾势N7即将上市 今晚21:30开启大定 独家
红魔魔灵电竞机械键盘官宣:自研主控芯片 1ms超低延迟
你做对了吗?自动扶梯上禁止行走 赶时间请走楼梯 最新
当前焦点!马斯克一年坐私人飞机超24万公里 华为李小龙:没我多
2023年椰子油行业统计数据分析:全球椰子油产量为3.51百万吨 环球今日讯
当前关注:援外日记丨在利比里亚 我们从朋友变成了兄弟姐妹
每日热点:RTX 4090烧毁无算!万恶的12VHPWR 16针接口终于要淘汰了
焦点播报:越南高考中文题曝光引热议 网友:难度不大
北大清华成才率中国最差 毁了一半人!俞敏洪称不能把人生寄托在考名牌大学上
热推荐:或为问界新LOGO:华为申请WENJIE图文商标
上海豪宅1.58亿成交 买家身份曝光:虎扑/得物创始人 身家百亿 全球最新
每日时讯!福州的成功之道
Jmeter之二_JSR223取样器,断言等添加失败的解决办法
每日速讯:科比5代多少钱_科比5代增高多少
你怎么看?21%的韩国人支持收单身税 有网友支持交 国家才能存续_环球观天下
环卫工儿子高考687分报考上海交大:关注科研 梦想攻克“卡脖子”技术|天天聚看点
全球快资讯:部分人大学生信息被非法获取,北京警方:一名毕业生被刑拘
避坑:.NET内存泄露的几种情况 每日热文
世界速讯:中国人民解放军海军博物馆预约规则
谁能想到!“白娘子”施法手势居然是赵雅芝自己设计的|世界微动态
性感掀背小翘臀!奔驰全新双门轿跑来了:CLE Coupe即将亮相 全球视讯
快讯:搭载骁龙8 Gne2稳了:三星Galaxy Z Fold5现身Geekbench数据库
俄罗斯2032年建成新一代空间站 届时国际空间站已坠毁|天天通讯
【环球新要闻】网页直播源码知识分享:“直播卫士”,查杀病毒功能在此!
环球百事通!芋泥香酥棒如何做 偷偷藏不住芋泥香酥鸭好吃不 基本情况讲解
多款新品汇聚2023MWC,AAC声光触XR全栈式解决方案成焦点 快报
韩国知名职业玩家怒批《星空》没韩语:太看不起人! 今日热议
网友自制进口豪华车国内外售价对比榜:只有特斯拉不割国人韭菜 头条
人大毕业生盗取学校学生信息建颜值打分网站 官方通报来了
天天头条:红魔首款电竞平板屏幕确定:144Hz高刷大屏 适配原生144帧游戏
谁家客户最满意?理想重回用户推荐度榜首、问界严重下滑
欧几里得空间望远镜升空 探索宇宙暗物质和暗能量
天天亮点!狂收 3K Star!一个高性能、无侵入的 Java 性能监控和统计工具,十分强悍!
【环球报资讯】Linux下轻松修改MySQL/MariaDB的Root密码
世界新动态:自学Python之路-django模板--模板继承
Linux磁盘操作:分区、格式化、挂载
2.1 Prometheus组件_观天下
世界新消息丨原来店铺照片可以造假?你点的外卖,可能来自“幽灵餐厅”
四大家电合一!石头智能洗地机A10 UltraE图赏
当前报道:微型车市场改写!比亚迪海鸥热销2.3万台:本田飞度彻底没活路
苹果MR头显未规划遥控器:主打手控和眼控
16GB再见 128GB内存满上的感觉有多爽?实测来了|全球新视野
41年大牌显卡厂商Sparkle投身Intel:三款Arc显卡开卖 千元起步
沪深股通|山推股份6月30日获外资买入0.12%股份-世界时讯
焦点速看:前端Vue自定义可自由滚动精美tabs选项卡标签栏标题栏 可设置背景颜色,
【工程报告】编程技术项目报告【编号:网页01号】
创世纪(300083.SZ):钻铣加工中心已累计出货超过90000台,成功入选2022年国家制造业单项冠军产品|环球动态
Twitter (TWTR.US) 阅读限制引发混乱,马斯克辩称是为了保护用户数据安全_每日快看
国产S80太阳能滑板车亮相:最短晒7小时充满电 可跑35公里
比亚迪高端MPV腾势D9 6月大卖超1.1万辆:连拿半年销量第一_世界今日报
0糖0卡0脂 5年福鼎老白茶:福鼎原煮白茶2.3元/瓶大促
全球聚焦:卷上天!上海交大博士应聘中学保健员 复旦附中回应
新疆哈密遭沙尘袭击 “百米沙墙”进城!附个人防沙方法
木薯资源7月3日上午起停牌 原因未知-即时
Shopee面经总结
最高检发布湿地保护公益诉讼典型案例
降价太香!特斯拉二季度交付超46.6万辆:Model 3/Y卖爆-天天快播