郑州网站建设兼职,1534939978姐的微信德惠市,牛网站,个人网站免费域名获取1. 交叉编译是什么#xff1f;
交叉编译简单说来#xff0c;就是编译成果物的地儿不是你运行这个成果物的地儿。最常见的场景#xff0c;就是我们要编译一个 ARM版本 的可执行程序#xff0c;但我们编译这个 ARM版本 可执行程序的地方#xff0c;是在一个 x86_x64 的平台…1. 交叉编译是什么
交叉编译简单说来就是编译成果物的地儿不是你运行这个成果物的地儿。最常见的场景就是我们要编译一个 ARM版本 的可执行程序但我们编译这个 ARM版本 可执行程序的地方是在一个 x86_x64 的平台上。
2. 为什么需要交叉编译
绝大部分的原因是目标平台不具备编译成果物的算力。具体说来就是ARM平台早期是 并没有 编译代码所需的 算力 和 相关空间 的。所以不得不借助性能更高的平台来辅助进行编译成果物然后ARM平台仅负责运行成果物即可。
3. 交叉编译只能在目标平台同一系统上吗
虽然绝大多数的 ARM Linux系统中编译的成果物是在对应的 x86_x64平台的Linux系统中进行的所以大多数时候使用Windows平台电脑需要安装一个虚拟机或者连接到某个x86_x64平台的Linux编译服务器中但实际上这种搭配大多数情况是为了方便也为了让编译者熟悉Linux环境但这种搭配并不是唯一的解决方案。
ARM官网 上就能直接下载各种平台( Linux、Windows )的编译工具链另外一个很常见的第三方工具链制作商linaro也是对外直接提供可用版本的工具链但不支持全平台 linaro gnu。
如果上述两个网站你都觉得不是很符合自己的开发板那么你也可以自己动手做一个链子符合目标平台的交叉编译链制作及简单分析。 这里主要使用crosstool-ng这个工具进行的制作自己做链子的好处是你可以自己按需选择对应的gcc版本以及glibc版本和一些其他重要基础库的版本。而上面现成的链子可能并不刚好是你需要的版本搭配。
4. 如何编译一个第三方开源库
以下编译前提均假设编译人员已经获得了一个可以使用的交叉编译工具链并且已经将编译工具链的可执行程序设置进环境变量。
4.1 最常见也是最简单的编译方式
大多数的简单第三方库均可以尝试以下的方式。这里的简单值得是不额外依赖一些其他第三方库的库。
configure --hostarm-linux-gnueabi --prefix${PWD}/build
make
make install一般验证自己是不是编译的正确的主要检查步骤就是在make的时候查看对应输出打印如果开头的的编译的确是对应编译工具链名称的开头那么至少配置是对的这里给个例子
/bin/sh ../libtool --tagCXX --modecompile arm-at91-linux-gnueabi-g -stdgnu11 -DHAVE_CONFIG_H -I. -I.. -I/data1/xiaoyanyi/work/snmp/snmp-3.5.0/include -pthread -I/data1/xiaoyanyi/work/openssllll/include -D_GNU_SOURCE -g -O2 -pthread -MT asn1.lo -MD -MP -MF .deps/asn1.Tpo -c -o asn1.lo asn1.cpp
libtool: compile: arm-at91-linux-gnueabi-g -stdgnu11 -DHAVE_CONFIG_H -I. -I.. -I/data1/xiaoyanyi/work/snmp/snmp-3.5.0/include -pthread -I/data1/xiaoyanyi/work/openssllll/include -D_GNU_SOURCE -g -O2 -pthread -MT asn1.lo -MD -MP -MF .deps/asn1.Tpo -c asn1.cpp -fPIC -DPIC -o .libs/asn1.o
libtool: compile: arm-at91-linux-gnueabi-g -stdgnu11 -DHAVE_CONFIG_H -I. -I.. -I/data1/xiaoyanyi/work/snmp/snmp-3.5.0/include -pthread -I/data1/xiaoyanyi/work/openssllll/include -D_GNU_SOURCE -g -O2 -pthread -MT asn1.lo -MD -MP -MF .deps/asn1.Tpo -c asn1.cpp -o asn1.o /dev/null 21make的过程中输出的打印里的确是以我们指定的host开头的那么这步骤至少是没问题的了。
4.2.1 简单解释一下configure里面常见通用参数的含义
先简单的介绍一下 configure make make install三部曲每部分是做了什么然后在展开介绍configure的通用参数。 configure : 通过配置生成makefile文件 make使用configure生成的Makefile文件进行编译工作 make install:将make生成的成果物文件按照prefix的路径进行复制有时候也会动态生成一些说明文档 所以大多数时候configure配置对了后面两步就一定能走通99%的编译不过问题基本都是configure的时候配置不对。以下重点讲解configure相关的参数。
4.2.1.1 prefix
这里prefix中文解释就是安装路径也就是make install的时候最终这些库会放到哪里去。一般对于交叉编译而言是需要指定的因为默认的路径是 /usr/local/但这路径实际上对于交叉编译而言一定是不行的。因为这个路径通常放的是本身系统的库如果交叉编译的库放进去后本身系统也会去检索这个路径下的库名字虽然匹配上了但是用不起来后续会造成极大的麻烦。
这里还有一点需要额外注意的后文会重新展开这个问题。这里先说结论 如果你需要编译的库ABC依赖DEF那么你先编译DEF的时候最好把prefix设置成自己交叉编译链的 sysroot/usr 中。 4.2.1.2 host
简单解释一下交叉编译和普通的编译第三方库差异主要在于需要指定host这个变量。这里贴一段标准解释
System types:--buildBUILD configure for building on BUILD [guessed]--hostHOST cross-compile to build programs to run on HOST [BUILD]这里仅需要额外强调一点host这个参数的指定逻辑和使用的目标编译工具链名称有关假设你的编译工具链的gcc名字叫 arm-at91-linux-gnueabi-gcc那么这里的host名字就是 arm-at91-linux-gnueabi。具体的逻辑就是把对应链子的 -gcc部分拆掉就是host的名字。其实configure脚本的逻辑也就是对应的反向在host后拼接一个 -gcc而已。
4.2.1.3 enable-xxxxx disable-xxxxx
这两个参数一般是在编译的时候可配置的打开某些功能或者关闭某些功能的时候会使用到。每个第三方库的特色不一样这里推荐遇到编译不过的问题首先就去看看: ./configure --help有时候发现你编译的第三方库依赖了过多的其他库而且这些功能对你并不需要的时候可以尽可能的--disable-xxxxx和 --without-xxxx。这样在后续make的时候就不会出现某些依赖库找不到的报错了。
4.2.1.4 CXX CC CFLAGS
这些shell环境变量也会产生一定的效果有时候你百度一些博客教程的时候会搜到交叉编译的某些指导文档会这么写 ./configure CCarm-linux-gnueabi-gcc --prefix${PWD}/build
make
make install或者写成这样
export CCarm-linux-gnueabi-gcc
./configure --prefix${PWD}/build
make
make instal这两种手法的思路一致的都是利用./configure 脚本是可以阅读当前shell环境变量中的CC并且将这个环境变量替换到脚本里从而实现CC替换成对应链子的目的。相关解释同样可以在--help中看到
Some influential environment variables:CC C compiler commandCFLAGS C compiler flagsLDFLAGS linker flags, e.g. -Llib dir if you have libraries in anonstandard directory lib dir但需要注意的一点是这种方法有一些隐患主要在于在很多时候交叉编译并不是把gcc变成arm-linux-gnueabi-gcc一切就完事了。我们可以在Makefile中和交叉编译链的bin路径中看到如下打印
(base) xiaoyanyisnmp-3.5.0$ls ~/cross-tool/arm-at91-linux-gnueabi/bin
arm-at91-linux-gnueabi-addr2line arm-at91-linux-gnueabi-gcc-4.9.2 arm-at91-linux-gnueabi-nm
arm-at91-linux-gnueabi-ar arm-at91-linux-gnueabi-gcc-ar arm-at91-linux-gnueabi-objcopy
arm-at91-linux-gnueabi-as arm-at91-linux-gnueabi-gcc-nm arm-at91-linux-gnueabi-objdump
arm-at91-linux-gnueabi-c arm-at91-linux-gnueabi-gcc-ranlib arm-at91-linux-gnueabi-populate
arm-at91-linux-gnueabi-cc arm-at91-linux-gnueabi-gcov arm-at91-linux-gnueabi-ranlib
arm-at91-linux-gnueabi-cfilt arm-at91-linux-gnueabi-gdb arm-at91-linux-gnueabi-readelf
arm-at91-linux-gnueabi-cpp arm-at91-linux-gnueabi-gprof arm-at91-linux-gnueabi-size
arm-at91-linux-gnueabi-ct-ng.config arm-at91-linux-gnueabi-ld arm-at91-linux-gnueabi-strings
arm-at91-linux-gnueabi-elfedit arm-at91-linux-gnueabi-ld.bfd arm-at91-linux-gnueabi-strip
arm-at91-linux-gnueabi-g arm-at91-linux-gnueabi-ldd
arm-at91-linux-gnueabi-gcc arm-at91-linux-gnueabi-ld.gold有些Makefile生成的环境中还需要使用ARNMRANLIB等等这些东西也需要使用对应的交叉编译工具链版本的程序。而上述的CC手法仅仅替换了一个。所以为什么是存在风险和后续还会遇到问题的。 这里推荐如果能用host就优先使用host指定这种方式相当于自动帮你设置了上述每一个需要替换的变量。 在极少数的时候host不被confiugre支持那么能用的方法只有这种了但同时也是最不同推荐的。
4.2.2 对于编译自依赖的第三方库族的推荐方法
有时候我们使用的第三方库有两层甚至多层底层他们自己开发了一个基础库然后在自己的基础库上封了一层应用。例如protobuf和grpcsnmp和agent。这个时候我们需要先编译基础库然后在编译应用库。这一点很容易理解从下到上但对于交叉编译来说又有一点需要注意的。主要和 prefix 有关。
当我们编译完基础库之后一般make install之后成果物大多情况是这样的[假设我们安装到了一个build目录]
(base) xiaoyanyibuild$ls
bin include libbin 目录一般是这个库的一些demo样例或者一些可执行程序。 include目录一般就是这个第三方库的头文件 lib目录一般就是这个第三方库的静态、动态库文件
那么在编译上层应用库的时候有些教程会推荐按照configure中的指定底层库路径宏变量的方式去显式指定对应路径。
例如agent这个库依赖snmp其中agent的confiugre文件里有这样一个变量
Some influential environment variables:PKG_CONFIG path to pkg-config utilityPKG_CONFIG_PATHdirectories to add to pkg-configs search pathPKG_CONFIG_LIBDIRpath overriding pkg-configs built-in search pathCXXCPP C preprocessorsnmp_CFLAGS C compiler flags for snmp, overriding pkg-configsnmp_LIBS linker flags for snmp, overriding pkg-configLT_SYS_LIBRARY_PATHUser-defined run-time library search path.
这里可以通过snmp_LIB这个变量显式指定去libsnmp.so的位置。这种方式在表面上是能够通过编译并且不会出现什么太大问题的。但会买下一个隐患在于后续使用库或者维护的时候会出现问题。这里需要讲解造成这个隐患问题的另外两个文件。
4.2.2.1 *.la
一般编译完成的第三方库的lib文件夹下面会有一个对应扩展名为la的文件。
例如
(base) xiaoyanyilib$ls
libsnmp.a libsnmp.la libsnmp.so libsnmp.so.35 libsnmp.so.35.0.0 pkgconfig这个la文件实际上并不是一个静/动态库程序而是一个配置文件我们可以直接vim打开
# libsnmp.la - a libtool library file
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-14
#
# Please DO NOT delete this file!
# It is necessary for linking the library.# The name that we can dlopen(3).
dlnamelibsnmp.so.35# Names of this library.
library_nameslibsnmp.so.35.0.0 libsnmp.so.35 libsnmp.so# The name of the static archive.
old_librarylibsnmp.a# Linker flags that cannot go in dependency_libs.
inherited_linker_flags -pthread# Libraries that this one depends upon.
dependency_libs -lssl -lcrypto
/data1/xiaoyanyi/cross-tool/arm-at91-linux-gnueabi/arm-at91-linux-gnueabi/lib/libstdc.la# Names of additional weak libraries provided by this library
weak_library_names# Version information for libsnmp.
current35
age0
revision0# Is this an already installed library?
installedyes# Should we warn about portability when linking against -modules?
shouldnotlinkno# Files to dlopen/dlpreopen
dlopen
dlpreopen# Directory that this library needs to be installed in:
libdir/data1/xiaoyanyi/work/snmp/snmp-3.5.0/build/lib 关键的问题就在最后的libdir里这个路径指明了这个动态库的安装路径在连接的过程中如果有动态库依赖动态库的情况gcc的连接应用会顺着路径去查找随影目标文件。而如果我们安装的时候就随意选一个build目录。最后即便是把这个路径放到了sysroot里去之后这个la文件里面的内容依旧不会变导致最后就找不到了。 4.2.2.2 pkgconfig *.pc
大多数库编完之后除了上述的la其实大家也可以在lib里面找到有一个pkgconfig文件夹里面会有一个对应的pc文件。
这个文件和la文件的作用极为相似也是一个配置文件我们可以打开看看。 prefix/data1/xiaoyanyi/work/snmp/snmp-3.5.0/build
exec_prefix${prefix}
libdir${exec_prefix}/lib
includedir${prefix}/include
modulesName: snmp
Version: 3.5.0
Description: SNMP C framework version 3
Requires:
Libs: -L${libdir} -lsnmp
Libs.private: -lssl -lcrypto
Cflags: -I${includedir}这里造成错误的原因是因为prefix这个变量也是会因为随意指定从而即便pc文件的位置移动也无法正确索引。pc文件主要是pkg-config这个应用为了编译的时候自动指定FLAGS和自动找库用的。
4.2.2.3 那么应该安装在哪里比较合适呢 这里推荐的路径是安装在编译工具链的sysroot中。 一般用crosstool-ng做的交叉工具链对应的sysroot是在 ${host}/${host}/sysroot/ 对于其他的链子基本也都有一个sysroot可以自行查找。而我们安装一般的第三方库一般推荐放在sysroot中的usr中因为不是系统库而是用户自己制作的。
这个路径下的例如我做了一个链子host是 arm-at91-linux-gnueabi那么对应的sysroot/usr路径是 arm-at91-linux-gnueabi/arm-at91-linux-gnueabi/sysroot/usr/ 所以绝大部分的时候将第三方库的prefix指定为上述路径可以直接将第三方库安装到编译工具链中从而达到后续编译其他库的时候链子会自动索引已经安装过的库不需要显示指定对应路径的目的。
但这里同样有一个风险点如果对应第三方库的更新较为频繁那么就可能存在要编译的新库但sysroot里有个老库的场景。这里推荐是更新较为频繁的库不要放到sysroot里去。
4.2 一些不常见但是常用的第三方库可能的编译方式
4.1里介绍的手法基本上能处理绝大多数的第三方库但仍旧有某些库的configure写的比较奇特按照常规三部曲无法解决的。一般通用的处理思路还是好好阅读configure --help同时这里重点解释两个场景。
4.2.1 常见开源加密算法库OpenSSL
这个动态库的交叉编译就不合适上述的情况configure中并没有指定host的能力这里推荐的建议是这样先说结果 ./Configure linux-armv4 no-asm shared这个库为什么会这么输入我们又是如何知道的呢其实还是看configure --help就能发现端倪。
(base) xiaoyanyiopenssl-1.0.2t$./Configure --help
Configuring for
Usage: Configure [no-cipher ...] [enable-cipher ...] [experimental-cipher ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-dso] [no-krb5] [sctp] [386] [--prefixDIR] [--openssldirOPENSSLDIR] [--with-xxx[vvv]] [--test-sanity] os/compiler[:flags]pick os/compiler from:
BC-32 BS2000-OSD BSD-generic32 BSD-generic64 BSD-ia64 BSD-sparc64 BSD-sparcv8
BSD-x86 BSD-x86-elf BSD-x86_64 Cygwin Cygwin-x86_64 DJGPP MPE/iX-gcc OS2-EMX
OS390-Unix QNX6 QNX6-i386 ReliantUNIX SINIX SINIX-N UWIN VC-CE VC-WIN32
VC-WIN64A VC-WIN64I aix-cc aix-gcc aix3-cc aix64-cc aix64-gcc android
android-armv7 android-mips android-x86 android64-aarch64 aux3-gcc
beos-x86-bone beos-x86-r5 bsdi-elf-gcc cc cray-j90 cray-t3e darwin-i386-cc
darwin-ppc-cc darwin64-ppc-cc darwin64-x86_64-cc dgux-R3-gcc dgux-R4-gcc
dgux-R4-x86-gcc dist gcc hpux-cc hpux-gcc hpux-ia64-cc hpux-ia64-gcc
hpux-parisc-cc hpux-parisc-cc-o4 hpux-parisc-gcc hpux-parisc1_1-cc
hpux-parisc1_1-gcc hpux-parisc2-cc hpux-parisc2-gcc hpux64-ia64-cc
hpux64-ia64-gcc hpux64-parisc2-cc hpux64-parisc2-gcc hurd-x86 iphoneos-cross
irix-cc irix-gcc irix-mips3-cc irix-mips3-gcc irix64-mips4-cc irix64-mips4-gcc
linux-aarch64 linux-alphabwx-ccc linux-alphabwx-gcc linux-alpha-ccc
linux-alpha-gcc linux-aout linux-armv4 linux-elf linux-generic32
linux-generic64 linux-ia32-icc linux-ia64 linux-ia64-icc linux-mips32
linux-mips64 linux-ppc linux-ppc64 linux-ppc64le linux-sparcv8 linux-sparcv9
linux-x32 linux-x86_64 linux-x86_64-clang linux-x86_64-icc linux32-s390x
linux64-mips64 linux64-s390x linux64-sparcv9 mingw mingw64 ncr-scde
netware-clib netware-clib-bsdsock netware-clib-bsdsock-gcc netware-clib-gcc
netware-libc netware-libc-bsdsock netware-libc-bsdsock-gcc netware-libc-gcc
newsos4-gcc nextstep nextstep3.3 osf1-alpha-cc osf1-alpha-gcc purify qnx4
rhapsody-ppc-cc sco5-cc sco5-gcc solaris-sparcv7-cc solaris-sparcv7-gcc
solaris-sparcv8-cc solaris-sparcv8-gcc solaris-sparcv9-cc solaris-sparcv9-gcc
solaris-x86-cc solaris-x86-gcc solaris64-sparcv9-cc solaris64-sparcv9-gcc
solaris64-x86_64-cc solaris64-x86_64-gcc sunos-gcc tandem-c89 tru64-alpha-cc
uClinux-dist uClinux-dist64 ultrix-cc ultrix-gcc unixware-2.0 unixware-2.1
unixware-7 unixware-7-gcc vos-gcc vxworks-mips vxworks-ppc405 vxworks-ppc60x
vxworks-ppc750 vxworks-ppc750-debug vxworks-ppc860 vxworks-ppcgen
vxworks-simlinux debug debug-BSD-x86-elf debug-VC-WIN32 debug-VC-WIN64A
debug-VC-WIN64I debug-ben debug-ben-darwin64 debug-ben-debug
debug-ben-debug-64 debug-ben-debug-64-clang debug-ben-macos
debug-ben-macos-gcc46 debug-ben-no-opt debug-ben-openbsd
debug-ben-openbsd-debug debug-ben-strict debug-bodo debug-darwin-i386-cc
debug-darwin-ppc-cc debug-darwin64-x86_64-cc debug-geoff32 debug-geoff64
debug-levitte-linux-elf debug-levitte-linux-elf-extreme
debug-levitte-linux-noasm debug-levitte-linux-noasm-extreme debug-linux-elf
debug-linux-elf-noefence debug-linux-generic32 debug-linux-generic64
debug-linux-ia32-aes debug-linux-pentium debug-linux-ppro debug-linux-x86_64
debug-linux-x86_64-clang debug-rse debug-solaris-sparcv8-cc
debug-solaris-sparcv8-gcc debug-solaris-sparcv9-cc debug-solaris-sparcv9-gcc
debug-steve-opt debug-steve32 debug-steve64 debug-vos-gccNOTE: If in doubt, on Unix-ish systems use ./config.这个库显式指定了所有它能支持的平台种类所以我们只能按照这个指定了。这样指定完之后需要把生成的Makefile中对应宏定义进行显式修改包括但不限于CC、AR、NM、RANLIB、CXX等等。
4.2.2 有些库压根就没有configure需要用cmake
有些开源库就没给configure但是能看到cmakelist的文件那么这个时候把cmakelist当做configure就好但是它的指定写法会稍显不同。一般需要提供一个对应的cmake配置对于我们交叉编译来说需要指定对应的host系统名称CMAKE_SYSTEM_NAME和处理器名称CMAKE_SYSTEM_PROCESSOR然后需要指定对应的编译链路径CMAKE_C_COMPILER和CMAKE_CXX_COMPILER。
这里给出一个样例 #arm.cmakeset(CMAKE_SYSTEM_NAME Linux)set(CMAKE_SYSTEM_PROCESSOR arm)set(tools /data1/xiaoyanyi/cross-tool/arm-imx6ul-linux-gnueabihf/bin/arm-imx6ul-linux-gnueabihf-)set(CMAKE_C_COMPILER ${tools}gcc)set(CMAKE_CXX_COMPILER ${tools}g)cmake文件准备好后就可以直接cmake了机理是和configure一样的
cmake CMakeLists.txt -DCMAKE_TOOLCHAIN_FILE./arm.cmake上述命令执行完后最终会生成一个Makefile文件接着makemake install就好了。
5. 总结 绝大多数场景交叉编译的时候configuremakemake install三部曲就好与普通编译不一样的是需要指定host 自依赖第三方库在不频繁迭代更新的条件下建议安装到交叉编译链的sysroot目录中 如果怎么编译都有点问题建议仔细阅读configure --help或者README分析查找端倪