使⽤Clang作为编译器——使⽤Clang交叉编译
使⽤ Clang 交叉编译
本⽂为译⽂,点击 查看原⽂。
1. 介绍
本⽂档将指导您选择正确的 Clang 选项,以便将代码交叉编译到不同的体系结构。它假定您已经知道如何为主机体系结构编译相关代码,并且知道如何选择附加的include和库路径。
然⽽,这个⽂档不是⼀个“如何做”的⽂档,也不会帮助您设置build系统或Makefiles,也不会帮助您选择正确的 CMake 选项,等等。此外,它没有涵盖所有可能的选项,也没有包含特定架构的特定⽰例。对于⼀个具体的例⼦,可能会很有趣。
阅读本⽂之后,您应该熟悉与交叉编译相关的主要问题,以及 Clang 为执⾏交叉编译提供了哪些主要编译器选项。
2. 交叉编译问题(Cross compilation issues)
在 GCC 世界中,每个主机/⽬标(host/target)组合都有⾃⼰的⼀组⼆进制⽂件、头⽂件、库等。因此,通常很容易下载⼀个包含所有⽂件的包,解压缩到⼀个⽬录,然后将build系统指向该编译器,该编译器将知道其位置,并在编译代码时到所需的所有内容。
另⼀⽅⾯,Clang/LLVM 本质上是⼀个交叉编译器,这意味着⼀组程序可以通过设置-target选项编译到所有⽬标。对于希望编译到不同平台和体系结构的程序员,对于只需要维护⼀个build系统的编译器开发⼈员,对于OS发布,只需要⼀组main包,这使得编译变得容易得多。
但是,与任何交叉编译器⼀样,考虑到不同体系结构、操作系统和选项的复杂性,要到头⽂件、库或binutils来⽣成⽬标特定的代码并不总是那么容易。因此,您需要指定选项(options)来帮助 Clang 了解您要编译的⽬标、⼯具的位置等等。
另⼀个问题是编译器只附带标准库(如compiler-rt、libcxx、libgcc、libm等),因此您必须到并提供给构建系统,以及构建软件所需的每个特定于您的⽬标(target)的其他库。仅仅安装主机(host)的库是不够的。
最后,并不是所有的⼯具链(toolchains)都是相同的,因此,并不是每个 Clang 选项都能神奇地⼯作。有些选项,⽐如--sysroot(它可以有效地更改头⽂件和库的逻辑根),假设所有⼆进制⽂件和库都在同⼀个⽬录中,当发⾏版的包管理安装了交叉编译器时,这可能不是真的。因此,对于每个特定的情况,
您可以使⽤多个选项,并且在⼤多数情况下,您最终将⼿动设置include paths (-I)和library paths (-L)
综上所述,不同的⼯具链可能:
特定于 host/target 或更灵活
放在⼀个⽬录中,或者分散在整个系统中
默认情况下有不同的库集和头⽂件集
需要特殊的选项,这是您的构建系统⽆法⾃⼰解决的
3. Clang中的⼀般交叉编译选项(General Cross-Compilation Options in Clang)
3.1 Target Triple
基本选项是定义⽬标体系结构。为此,使⽤-target <triple>。如果不指定⽬标,CPU 名称将不匹配(因为 Clang 假设host triple),编译将继续进⾏,为主机平台创建代码,稍后在汇编或链接时代码将中断。
triple 的⼀般格式为<arch><sub>-<vendor>-<sys>-<abi>,其中:
arch = x86_64、i386、arm、thumb、mips等。
sub = v5, v6m, v7a, v7m等。
vendor = pc, apple, nvidia, ibm,等。
sys = none, linux, win32, darwin, cuda等。
abi = eabi, gnu, android, macho, elf等。
当然,sub体系结构选项对于它们⾃⼰的体系结构是可⽤的,所以“x86v7a”没有意义。只有在发⽣相关更改时才需要指定vendor,例如在PC和Apple之间。⼤多数情况下,可以忽略它(并且未知),这将为指定的体系结构设置默认值。system名称通常是OS (linux, darwin),但也可以像裸⾦属“none”⼀样特殊。
当⼀个参数不重要时,可以省略它,或者您可以选择unknown并使⽤缺省值。如果您选择⼀个 Clang 不知道的参数,⽐如blerg,它将忽略并假设未知,这并不总是需要的,所以要⼩⼼。
最后,ABI选项将选择默认的CPU/FPU,定义代码的特定⾏为(pc、扩展),并选择正确的库调⽤,等等。
3.2 CPU、FPU、ABI
⼀旦指定了⽬标,就到了选择要编译到的硬件的时候了。对于每种体系结构,都会选择⼀组默认的CPU/FPU/ABI,因此您⼏乎总是必须通过flag更改它。
典型的flag包括:
-mcpu=<cpu-name>,如x86-64, swift, cortex-a15
-mfpu=<fpu-name>,如SSE3, NEON,控制FP单元可⽤
-mfloat-abi=<fabi>,如控制浮点寄存器的软、硬件
默认值通常是公分母,因此Clang不会⽣成中断的代码。但这也意味着您⽆法为特定的硬件获得最佳代码,这可能意味着速度⽐您预期的要慢⼏个数量级。
例如,如果您的⽬标是arm-none-eabi,那么默认的CPU将是使⽤软浮点数的arm7tdmi,这在现代内核上是⾮常慢的,⽽如果您的三元组是armv7a-none-eabi,那么它将是带NEON的Cortex-A8,但是仍然使⽤软浮点数,这要好得多,但是仍然不是很好。
3.3 ⼯具链选项
控制对交叉编译器的访问有三个主要选项:-sysroot、-I和-L。最后两个是众所周知的,但是对于特定于⽬标的附加库和头⽂件来说,它们尤其重要。
有两种主要的⽅式获得⼀个交叉编译器:
1. 当您将交叉编译器从zip⽂件解压缩到⽬录中时,必须使⽤--sysroot=<path>。path是您解压缩⽂件的根⽬录,Clang 将查其中包含
的⽬录bin、lib。
在这种情况下,您的设置应该已经基本完成(如果不需要额外的头⽂件或库),因为Clang将在其中到它需要的所有⼆进制⽂件(汇编器、链接器等)。
gnu编译器2. 当您通过包管理器安装(现代Linux发⾏版有交叉编译器包可⽤)时,请确保您设置的⽬标triple也是交叉编译器⼯具链的前缀。
在本例中,Clang 将到其他⼆进制⽂件(汇编器、链接器),但并不总是在⽬标头⽂件和库所在的位置。⼈们经常在Clang 中添加特定于系统的线索,但是随着事情的变化,它很可能不会发现,⽽不是相反。
因此,在这⾥,如果⼿动指定include/library⽬录(通过-I和-L),将会安全得多。
4. 特定⽬标的库(Target-Specific Libraries)
作为构建的⼀部分编译的所有库都将被交叉编译到⽬标,构建系统可能会在正确的位置到它们。但是,通常检查的所有依赖项
(如libxml或libz等)都将与主机平台匹配,⽽不是与⽬标平台匹配。
因此,如果构建系统没有意识到您想交叉编译代码,那么它将错误地获得每个依赖项,并且编译将在构建期间失败,⽽不是在配置期间。
此外,查⽬标的库并不像查主机那么容易。⼤多数OS的包中没有多少跨库可⽤,所以您必须从源代码交叉编译它们,或者下载⽬标平台的包,提取库和头⽂件,将它们放在特定的⽬录中,并添加-I和-L指向它们。
此外,⼀些库对不同的⽬标具有不同的依赖关系,因此,在主机中查依赖关系的配置⼯具可能会将⽬标平台的列表弄错。这意味着在设置⾃⼰的库路径时,您的构建的配置可能会出错,您必须通过附加标志(configure、Make、CMake等)来扩充它。
5. Multilibs
当您希望交叉编译到多个配置时,例如hard-float-ARM和soft-float-ARM,您必须拥有库的多个副本和(可能的)头⽂件。
有些Linux发⾏版⽀持Multilib,⽤⼀种更简单的⽅式为你处理,但是如果你不⼩⼼,例如,忘记指定-ccc-gcc-name armv7l-linux-gnueabihf-gcc(使⽤hard-float),Clang将选择armv7l-linux-gnueabi-ld(使⽤soft-float),并且将会发⽣链接器错误。
如果您正在为不同的ABIs(如gnueabi和androideabi)编译,甚⾄可能链接并运⾏,但会产⽣运⾏时错误,这将更加难以跟踪和修复,那么情况也是⼀样的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论