本文档将指导您选择正确的 Clang 选项,以便将代码交叉编译到不一样的体系结构。它假定您已经知道如何为主机体系结构编译相关代码,而且知道如何选择附加的include和库路径。
然而,这个文档不是一个“如何作”的文档,也不会帮助您设置build
系统或Makefiles
,也不会帮助您选择正确的 CMake 选项,等等。此外,它没有涵盖全部可能的选项,也没有包含特定架构的特定示例。对于一个具体的例子,交叉编译LLVM自己的说明可能会颇有趣。
阅读本文以后,您应该熟悉与交叉编译相关的主要问题,以及 Clang 为执行交叉编译提供了哪些主要编译器选项。html
在 GCC 世界中,每一个主机/目标(host/target)组合都有本身的一组二进制文件、头文件、库等。所以,一般很容易下载一个包含全部文件的包,解压缩到一个目录,而后将build
系统指向该编译器,该编译器将知道其位置,并在编译代码时找到所需的全部内容。linux
另外一方面,Clang/LLVM 本质上是一个交叉编译器,这意味着一组程序能够经过设置-target
选项编译到全部目标。对于但愿编译到不一样平台和体系结构的程序员,对于只须要维护一个build
系统的编译器开发人员,对于OS发布,只须要一组main包,这使得编译变得容易得多。android
可是,与任何交叉编译器同样,考虑到不一样体系结构、操做系统和选项的复杂性,要找到头文件、库或binutils来生成目标特定的代码并不老是那么容易。所以,您须要指定选项(options)来帮助 Clang 了解您要编译的目标、工具的位置等等。程序员
另外一个问题是编译器只附带标准库(如compiler-rt
、libcxx
、libgcc
、libm
等),所以您必须找到并提供给构建系统,以及构建软件所需的每一个特定于您的目标(target)的其余库。仅仅安装主机(host)的库是不够的。web
最后,并非全部的工具链(toolchains)都是相同的,所以,并非每一个 Clang 选项都能神奇地工做。有些选项,好比--sysroot
(它能够有效地更改头文件和库的逻辑根),假设全部二进制文件和库都在同一个目录中,当发行版的包管理安装了交叉编译器时,这可能不是真的。所以,对于每一个特定的状况,您可使用多个选项,而且在大多数状况下,您最终将手动设置include paths (-I)
和library paths (-L)
swift
综上所述,不一样的工具链可能:安全
基本选项是定义目标体系结构。为此,使用-target <triple>
。若是不指定目标,CPU 名称将不匹配(由于 Clang 假设host triple),编译将继续进行,为主机平台建立代码,稍后在汇编或连接时代码将中断。架构
triple 的通常格式为<arch><sub>-<vendor>-<sys>-<abi>
,其中:app
arch
= x86_64
、i386
、arm
、thumb
、mips
等。固然,sub
体系结构选项对于它们本身的体系结构是可用的,因此“x86v7a”没有意义。只有在发生相关更改时才须要指定vendor
,例如在PC和Apple之间。大多数状况下,能够忽略它(而且未知),这将为指定的体系结构设置默认值。system
名称一般是OS (linux, darwin),但也能够像裸金属“none”同样特殊。
当一个参数不重要时,能够省略它,或者您能够选择unknown
并使用缺省值。若是您选择一个 Clang 不知道的参数,好比blerg
,它将忽略并假设未知,这并不老是须要的,因此要当心。
最后,ABI
选项将选择默认的CPU/FPU
,定义代码的特定行为(pc、扩展),并选择正确的库调用,等等。ide
一旦指定了目标,就到了选择要编译到的硬件的时候了。对于每种体系结构,都会选择一组默认的CPU/FPU/ABI,所以您几乎老是必须经过flag
更改它。
典型的flag
包括:
-mcpu=<cpu-name>
,如x86-64, swift, cortex-a15-mfpu=<fpu-name>
,如SSE3, NEON,控制FP单元可用-mfloat-abi=<fabi>
,如控制浮点寄存器的软、硬件例如,若是您的目标是arm-none-eabi,那么默认的CPU将是使用软浮点数的arm7tdmi,这在现代内核上是很是慢的,而若是您的三元组是armv7a-none-eabi,那么它将是带NEON的Cortex-A8,可是仍然使用软浮点数,这要好得多,可是仍然不是很好。
控制对交叉编译器的访问有三个主要选项:-sysroot
、-I
和-L
。最后两个是众所周知的,可是对于特定于目标的附加库和头文件来讲,它们尤为重要。
有两种主要的方式得到一个交叉编译器:
--sysroot=<path>
。path
是您解压缩文件的根目录,Clang 将查找其中包含的目录bin、lib。include/library
目录(经过-I
和-L
),将会安全得多。做为构建的一部分编译的全部库都将被交叉编译到目标,构建系统可能会在正确的位置找到它们。可是,一般检查的全部依赖项(如libxml
或libz
等)都将与主机平台匹配,而不是与目标平台匹配。
所以,若是构建系统没有意识到您想交叉编译代码,那么它将错误地得到每一个依赖项,而且编译将在构建期间失败,而不是在配置期间。
此外,查找目标的库并不像查找主机那么容易。大多数OS的包中没有多少跨库可用,因此您必须从源代码交叉编译它们,或者下载目标平台的包,提取库和头文件,将它们放在特定的目录中,并添加-I
和-L
指向它们。
此外,一些库对不一样的目标具备不一样的依赖关系,所以,在主机中查找依赖关系的配置工具可能会将目标平台的列表弄错。这意味着在设置本身的库路径时,您的构建的配置可能会出错,您必须经过附加标志(configure、Make、CMake等)来扩充它。
当您但愿交叉编译到多个配置时,例如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
)编译,甚至可能连接并运行,但会产生运行时错误,这将更加难以跟踪和修复,那么状况也是同样的。