交叉编译说明

1. 交叉编译目的

一般来说 x86 的性能比较强劲,且工具成熟,生态强大。而 arm 嵌入式平台的性能比较弱,但功耗低。很多嵌入式产品都是运行在 arm 平台上的。而编译是一个比较费性能的工作,直接在 arm 平台上编译代码不太现实,因此有了交叉编译。交叉编译即在 A 平台(一般是 x86)上编译出能在 B 平台(一般是 arm 平台)上运行的代码。

2. 交叉编译步骤

一般需要交叉编译的工具的源码都是 C 代码,且一般都是用 makefile 构建的。一般的步骤为:(1) 下载源码;(2) 运行源码中的 configure 脚本文件进行配置;(3) make 编译。

2.1. 下载源码

此步骤不再详细说明,直接从网上找源码下载即可。

2.2. configure 配置

一般这些源码的目录下都有一个 configure 脚本文件,目的是用来配置编译参数,编译的工具链等。

查看所有支持的参数以及说明:

./configure -h

这里以 bash 为例,查看所有支持的参数:

[01:05:48  bash-5.2.9]$ ./configure -h
`configure' configures bash 5.2-release to adapt to many kinds of systems.

Usage: ./configure [OPTION]... [VAR=VALUE]...

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.

Configuration:
  -h, --help              display this help and exit
      --help=short        display options specific to this package
      --help=recursive    display the short help of all the included packages
  -V, --version           display version information and exit
  -q, --quiet, --silent   do not print `checking ...' messages
      --cache-file=FILE   cache test results in FILE [disabled]
  -C, --config-cache      alias for `--cache-file=config.cache'
  -n, --no-create         do not create output files
      --srcdir=DIR        find the sources in DIR [configure dir or `..']

Installation directories:
  --prefix=PREFIX         install architecture-independent files in PREFIX
                          [/usr/local]
  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
                          [PREFIX]

By default, `make install' will install all the files in
`/usr/local/bin', `/usr/local/lib' etc.  You can specify
an installation prefix other than `/usr/local' using `--prefix',
for instance `--prefix=$HOME'.

For better control, use the options below.

Fine tuning of the installation directories:
  --bindir=DIR            user executables [EPREFIX/bin]
  --sbindir=DIR           system admin executables [EPREFIX/sbin]
  --libexecdir=DIR        program executables [EPREFIX/libexec]
  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
  --libdir=DIR            object code libraries [EPREFIX/lib]
  --includedir=DIR        C header files [PREFIX/include]
  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
  --infodir=DIR           info documentation [DATAROOTDIR/info]
  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
  --mandir=DIR            man documentation [DATAROOTDIR/man]
  --docdir=DIR            documentation root [DATAROOTDIR/doc/bash]
  --htmldir=DIR           html documentation [DOCDIR]
  --dvidir=DIR            dvi documentation [DOCDIR]
  --pdfdir=DIR            pdf documentation [DOCDIR]
  --psdir=DIR             ps documentation [DOCDIR]

System types:
  --build=BUILD     configure for building on BUILD [guessed]
  --host=HOST       cross-compile to build programs to run on HOST [BUILD]

Optional Features:
  --disable-option-checking  ignore unrecognized --enable/--with options
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-minimal-config a minimal sh-like configuration
  --enable-alias          enable shell aliases
  --enable-alt-array-implementation
                          enable an alternate array implementation that
                          optimizes speed at the cost of space
  --enable-arith-for-command
                          enable arithmetic for command
  --enable-array-variables
                          include shell array variables
  --enable-bang-history   turn on csh-style history substitution
  --enable-brace-expansion
                          include brace expansion
  --enable-casemod-attributes
                          include case-modifying variable attributes
  --enable-casemod-expansions
                          include case-modifying word expansions
  --enable-command-timing enable the time reserved word and command timing
  --enable-cond-command   enable the conditional command
  --enable-cond-regexp    enable extended regular expression matching in
                          conditional commands
  --enable-coprocesses    enable coprocess support and the coproc reserved
                          word
  --enable-debugger       enable support for bash debugger
  --enable-dev-fd-stat-broken
                          enable this option if stat on /dev/fd/N and fstat on
                          file descriptor N don't return the same results
  --enable-direxpand-default
                          enable the direxpand shell option by default
  --enable-directory-stack
                          enable builtins pushd/popd/dirs
  --enable-disabled-builtins
                          allow disabled builtins to still be invoked
  --enable-dparen-arithmetic
                          include ((...)) command
  --enable-extended-glob  include ksh-style extended pattern matching
  --enable-extended-glob-default
                          force extended pattern matching to be enabled by
                          default
  --enable-function-import
                          allow bash to import exported function definitions
                          by default
  --enable-glob-asciiranges-default
                          force bracket range expressions in pattern matching
                          to use the C locale by default
  --enable-help-builtin   include the help builtin
  --enable-history        turn on command history
  --enable-job-control    enable job control features
  --enable-multibyte      enable multibyte characters if OS supports them
  --enable-net-redirections
                          enable /dev/tcp/host/port redirection
  --enable-process-substitution
                          enable process substitution
  --enable-progcomp       enable programmable completion and the complete
                          builtin
  --enable-prompt-string-decoding
                          turn on escape character decoding in prompts
  --enable-readline       turn on command line editing
  --enable-restricted     enable a restricted shell
  --enable-select         include select command
  --enable-separate-helpfiles
                          use external files for help builtin documentation
  --enable-single-help-strings
                          store help documentation as a single string to ease
                          translation
  --enable-strict-posix-default
                          configure bash to be posix-conformant by default
  --enable-translatable-strings
                          include support for $"..." translatable strings
  --enable-usg-echo-default
                          a synonym for --enable-xpg-echo-default
  --enable-xpg-echo-default
                          make the echo builtin expand escape sequences by
                          default
  --enable-mem-scramble   scramble memory on calls to malloc and free
  --enable-profiling      allow profiling with gprof
  --enable-static-link    link bash statically, for use as a root shell
  --disable-largefile     omit support for large files
  --disable-nls           do not use Native Language Support
  --enable-threads={posix|solaris|pth|windows}
                          specify multithreading API
  --disable-threads       build without multithread safety
  --disable-rpath         do not hardcode runtime library paths

Optional Packages:
  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
  --with-afs              if you are running AFS
  --with-bash-malloc      use the Bash version of malloc
  --with-curses           use the curses library instead of the termcap
                          library
  --with-gnu-malloc       synonym for --with-bash-malloc
  --with-installed-readline
                          use a version of the readline library that is
                          already installed
  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
  --with-libpth-prefix[=DIR]  search for libpth in DIR/include and DIR/lib
  --without-libpth-prefix     don't search for libpth in includedir and libdir
  --with-libiconv-prefix[=DIR]  search for libiconv in DIR/include and DIR/lib
  --without-libiconv-prefix     don't search for libiconv in includedir and libdir
  --with-included-gettext use the GNU gettext library included here
  --with-libintl-prefix[=DIR]  search for libintl in DIR/include and DIR/lib
  --without-libintl-prefix     don't search for libintl in includedir and libdir

Some influential environment variables:
  DEBUGGER_START_FILE
              location of bash debugger initialization file
  CC_FOR_BUILD
              C compiler used when compiling binaries used only at build time
  CFLAGS_FOR_BUILD
              Compilation options (CFLAGS) used when compiling binaries used
              only at build time
  LDFLAGS_FOR_BUILD
              Linker options (LDFLAGS) used when compiling binaries used only
              at build time
  CPPFLAGS_FOR_BUILD
              C preprocessor options (CPPFLAGS) used when compiling binaries
              used only at build time
  CC          C compiler command
  CFLAGS      C compiler flags
  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
              nonstandard directory <lib dir>
  LIBS        libraries to pass to the linker, e.g. -l<library>
  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
              you have headers in a nonstandard directory <include dir>
  CPP         C preprocessor
  YACC        The `Yet Another Compiler Compiler' implementation to use.
              Defaults to the first program found out of: `bison -y', `byacc',
              `yacc'.
  YFLAGS      The list of arguments that will be passed by default to $YACC.
              This script will default YFLAGS to the empty string to avoid a
              default value of `-d' given by some make applications.

Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.

Report bugs to <bug-bash@gnu.org>.

而一般的源码,大家约定俗称,一般都支持下列参数。

2.2.1. 安装选项

  • --prefix:编译好的二进制安装的路径

2.2.2. 平台选项

  • --build:The platform on which the compilation tools are executed。(编译此代码的平台,或者是说交叉编译器运行的平台,一般都是 x86)
  • --host:The platform on which the code will run。(编译好的代码要运行在哪种平台上,一般是 arm。注:--build 和 --host 不同的时候就被配置文件认定为交叉编译方式)
  • --target:Only when building a compiler, this is the platform for which the compiler will generate code。(这个参数一般不用,只有我们编译的软件是编译器时才可能用到)

例如:

假设在 x86 下编译一个运行于 arm 上的编译器 gcc,这个 gcc 产生 mips 平台的目标代码。那么我们的配置方式是:

--build=x86 --host=aarch64 --target=mips

一般只有在这种情况下才需要配置 --target 参数。我们编译的时候,一般仅仅需要指定 --host 参数即可。--build 参数一般都能推导出来,因为代码知道我们是在什么平台上进行的编译活动。

关于这几个参数的取值,其实就是个字符串,没有统一规定。一般来说 --build 参数一般指定 x86x86-linuxx86-unknown-none 即可。--host 参数一般指定 aarch64aarch64-linuxaarch64-unknown-nonearm64 即可。但根据经验,有些 configure 只识别特定的名字,例如有的传递 aarch64 不识别,只识别 arm64;而有的更奇怪,传递 aarch64 不编译动态库,传递 aarch64-linux 才编译动态库,这个需要根据实际情况识别。

2.2.3. 环境变量

一个完整的编译需要经过 "预处理 > 编译 > 汇编 > 链接" 步骤才能完成。我们通常运行 gcc main.cpp -o main 一条指令就把上述步骤涵盖完毕了。但其实每个步骤有特定的编译工具负责。

一般一个完整的交叉编译工具都包含如下类似工具:

程序命令程序功能
addr2line给出一个可执行文件的内部地址,addr2line 使用文件中的调试信息将地址翻译成源代码文件名和行号。
ar这是一个程序,可通过从文档中增加、删除和析取文件来维护库文件。通常使用该工具是为了创建和管理连接程序使用的目标库文档。
asGNU 汇编器。实际上它是一族汇编器,因为它可以被编译或能够在各种不同平台上工作。
c++filt程序接受被 C++ 编译程序转换过的名字(不是被重载的),而且将该名字翻译成初始形式。
elfedit更新 ELF 文件的 ELF 头。
gprof该程序会监督编译程序的执行过程,并报告程序中各个函数的运行时间,可以根据所提供的配置文件来优化程序。
ldGNU 连接程序。该程序将目标文件的集合组合成可执行程序。
ld.bfd到 ld 的硬链接。
libbfd二进制文件描述器库。该程序是 binutils 包的一部分
libiberty包含多个 GNU 程序会使用的途径,包括 getopt、obstack、strerror、strtol 和 strtoul。
libopcodes一个库,用于处理 opcodes——处理器指令的 "可读文本" 版本;用于编制 objdump 这样的工具。
nlmconv将可重定位的目标文件转换成 NetWare 可加载模块(NetWare Loadable Module, NLM)。
nm列出目标文件中定义的符号。
objcopy将目标文件从一种二进制格式复制和翻译到另外一种。
objdump显示一个或多个目标文件中保存的多种不同信息。
ranlib创建和添加到 ar 文档的索引。该索引被 ld 使用来定位库中的模块。
readelf从 ELF 格式的目标文件显示信息
size列出目标文件中每个部分的名字和尺寸。
strings浏览所有类型的文件,析取出用于显示的字符串。
strip从目标文件或文档库中去掉符号表,以及其他调试所需的信息。
windresWindow 资源文件编译程序。

一般需要指定以下环境变量:

  • CC:C compiler command。
  • AR:打包库使用。
  • CFLAGS:C compiler flags。一般交叉编译为了移植方便,都要指定静态编译,这样运行时不依赖 so 库。大部分工具都不会提供静态编译参数,因此为了静态编译一般都要指定 CFLAGS=-static。bash 的源码中提供了静态编译选项 --enable-static-link,这为用户提供了方便,因此在交叉编译 bash 时直接指定这个参数就能静态编译,而不需要指定 CFLAGS 参数。因此,我们拿到一个源码时,运行 ./config -h | grep static,通过关键字查找一下是否提供了静态编译选项。

2.2.4. 完整配置

因此一般在运行 configure 配置时,需要传递以下参数:

./configure \
    --prefix=YOU_INSTALL_PATH \
    --host=aarch64 \
    CC=/xxx/aarch64-linux-gnu-cc \
    CFLAGS=-static

2.2.5. make 编译

一般这些工具的源码都是用 makefile 构建的,对应的工具就是 mark。

一般运行如下指令编译:

make -j16
  • -j:指定用多少个线程同时编译,越多越快,根据电脑实际情况配置。

但有的时候,需要给 makefile 传递变量,即有的时候 makefile 中定义了诸如 ARCH 和 CROSS_COMPILE 等变量,但不是每个 makefile 都定义了这些变量,这是通用做法,而不是必须做法。具体定义了哪些变量,需要打开 makefile 具体看一看。一般工程都自带 一个 Makefile.in 文件,然后运行 ./configure 配置时根据参数与 Makefile.in 生成真正的 Makefile 文件供 make 指令使用。因此这里查看的是运行 ./configure 生成的 Makefile 文件。

  • ARCH:即 architecture,就是编译出的代码运行的平台架构,一般指定 ARCH=aarch64 或 ARCH=arm64 即可。

  • CROSS_COMPILE:即交叉编译器的前缀(prefix),如指定 CROSS_COMPILE=/xxx/aarch64-linux-gnu- 就是使用 /xxx/aarch64-linux-gnu-cc/xxx/aarch64-linux-gnu-ar/xxx/aarch64-linux-gnu-ld 等工具将代码编译成 arm 的可执行指令。

此时编译指令一般如下:

make ARCH=aarch64 CROSS_COMPILE=/xxx/aarch64-linux-gnu- -j16

编译完整后,运行如下指令把编译好的二进制安装到 --prefix=YOU_INSTALL_PATH 指定的目录下:

make install

3. 交叉编译器说明

3.1. 命名规则

交叉编译工具链的命名规则为:arch [-vendor] [-os] [-(gnu)eabi]

  • arch -体系架构,如 ARM,MIPS
  • vendor -工具链提供商
  • os -目标操作系统
  • eabi -嵌入式应用二进制接口(Embedded Application Binary Interface)

根据对操作系统的支持与否,ARM GCC 可分为支持和不支持操作系统,如:

  • arm-none-eabi:这个是没有操作系统的,自然不可能支持那些跟操作系统关系密切的函数,比如 fork(2)。它使用的是 newlib 这个专用于嵌入式系统的 C 库。
  • arm-none-linux-eabi:用于 Linux 的,使用 Glibc

3.2. 实例

1、arm-none-eabi-gcc

(ARM architecture,no vendor,not target an operating system,complies with the ARM EABI)

用于编译 ARM 架构的裸机系统(包括 ARM Linux 的 boot、kernel,不适用编译 Linux 应用 Application),一般适合 ARM7、Cortex-M 和 Cortex-R 内核的芯片使用,所以不支持那些跟操作系统关系密切的函数,比如 fork(2),它使用的是 newlib 这个专用于嵌入式系统的 C 库。

2、arm-none-linux-gnueabi-gcc

(ARM architecture, no vendor, creates binaries that run on the Linux operating system, and uses the GNU EABI)

主要用于基于 ARM 架构的 Linux 系统,可用于编译 ARM 架构的 u-boot、Linux 内核、Linux 应用等。arm-none-linux-gnueabi 基于 GCC,使用 Glibc 库,经过 Codesourcery 公司优化过推出的编译器。arm-none-linux-gnueabi-xxx 交叉编译工具的浮点运算非常优秀。一般 ARM9、ARM11、Cortex-A 内核,带有 Linux 操作系统的会用到。

3、arm-eabi-gcc

Android ARM 编译器。

4、armcc

ARM 公司推出的编译工具,功能和 arm-none-eabi 类似,可以编译裸机程序(u-boot、kernel),但是不能编译 Linux 应用程序。armcc 一般和 ARM 开发工具一起,Keil MDK、ADS、RVDS 和 DS-5 中的编译器都是 armcc,所以 armcc 编译器都是收费的。

5、arm-none-uclinuxeabi-gcc 和 arm-none-symbianelf-gcc

arm-none-uclinuxeabi 用于 uCLinux,使用 Glibc。

arm-none-symbianelf 用于 symbian,不知道 C 库是什么。

3.3. Codesourcery

Codesourcery 推出的产品叫 Sourcery G++ Lite Edition,其中基于 command-line 的编译器是免费的,在官网上可以下载,而其中包含的 IDE 和 debug 工具是收费的,当然也有 30 天试用版本的。

目前 Codesourcery 已经由明导国际(Mentor Graphics)收购,所以原本的网站风格已经全部变为 Mentor 样式,但是 Sourcery G++ Lite Edition 同样可以注册后免费下载。

Codesourcery 一直是在做 ARM 目标 GCC 的开发和优化,它的 ARM GCC 在目前在市场上非常优秀,很多 patch 可能还没被 gcc 接受,所以还是应该直接用它的(而且它提供 Windows 下[mingw 交叉编译的]和 Linux 下的二进制版本,比较方便;如果不是很有时间和兴趣,不建议下载 src 源码包自己编译,很麻烦,Codesourcery 给的 shell 脚本很多时候根本没办法直接用,得自行提取关键的部分手工执行,又费精力又费时间,如果想知道细节,其实不用自己编译一遍,看看它是用什么步骤构建的即可,如果你对交叉编译器感兴趣的话。

3.4. ABI 和 EABI

ABI:二进制应用程序接口(Application Binary Interface (ABI)for the ARM Architecture)。在计算机中,应用二进制接口描述了应用程序(或者其它类型)和操作系统之间或其它应用程序的低级接口。

EABI:嵌入式 ABI。嵌入式应用二进制接口指定了文件格式、数据类型、寄存器使用、堆积组织优化和在一个嵌入式软件中的参数的标准约定。开发者使用自己的汇编语言也可以使用 EABI 作为与兼容的编译器生成的汇编语言的接口。

两者主要区别是,ABI 是计算机上的,EABI 是嵌入式平台上(如 ARM,MIPS 等)。

3.5. arm-linux-gnueabi-gcc 和 arm-linux-gnueabihf-gcc

两个交叉编译器分别适用于 armel 和 armhf 两个不同的架构,armel 和 armhf 这两种架构在对待浮点运算采取了不同的策略(有 fpu 的 ARM 才能支持这两种浮点运算策略)。

其实这两个交叉编译器只不过是 gcc 的选项-mfloat-abi 的默认值不同。gcc 的选项-mfloat-abi 有三种值 soft、softfp、hard(其中后两者都要求 ARM 里有 fpu 浮点运算单元,soft 与后两者是兼容的,但 softfp 和 hard 两种模式互不兼容):

  • soft:不用 fpu 进行浮点计算,即使有 fpu 浮点运算单元也不用,而是使用软件模式。
  • softfp:armel 架构(对应的编译器为 arm-linux-gnueabi-gcc)采用的默认值,用 fpu 计算,但是传参数用普通寄存器传,这样中断的时候,只需要保存普通寄存器,中断负荷小,但是参数需要转换成浮点的再计算。
  • hard:armhf 架构(对应的编译器 arm-linux-gnueabihf-gcc)采用的默认值,用 fpu 计算,传参数也用 fpu 中的浮点寄存器传,省去了转换,性能最好,但是中断负荷高。

把以下测试使用的 C 文件内容保存成 mfloat.c:

#include <stdio.h>
int main(void)
{
    double a,b,c;
    a = 23.543;
    b = 323.234;
    c = b/a;
    printf("the 13/2 = %f\n", c);
    printf("hello world !\n");
    return 0;
}

1、使用 arm-linux-gnueabihf-gcc 编译,使用 -v 选项以获取更详细的信息:

# arm-linux-gnueabihf-gcc -v mfloat.c
COLLECT_GCC_OPTIONS='-v' '-march=armv7-a' '-mfloat-abi=hard' '-mfpu=vfpv3-d16' '-mthumb'
-mfloat-abi=hard

可看出使用 hard 硬件浮点模式。

2、使用 arm-linux-gnueabi-gcc 编译:

# arm-linux-gnueabi-gcc -v mfloat.c
COLLECT_GCC_OPTIONS='-v' '-march=armv7-a' '-mfloat-abi=softfp' '-mfpu=vfpv3-d16' '-mthumb'
-mfloat-abi=softfp

可看出使用 softfp 模式。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值