交叉编译
date: 2011-0908
author:
copyright: all rights reserved.
1. 引言
其实交叉编译做过单片机开发的就已经体验过了,只是通常我们在较大的嵌入式系统开发的时候才会提及这个概念,所谓交叉编译无非就是在一种机器结构上给另外一种机器结构编译代码。
通常嵌入式系统由于要求身材苗条,其上只跑一些应用,不会将编译器放到上面运行,其实也是运行不出来的,如果用avr给自己编译代码,一个io口操作的代码估计要运行1天左右。
所以,在x86等性能高的机器上给小系统编译代码也是没有办法的事情。交叉编译真就说起来简单,那么为什么还要拿出来大讲一番呢 ? 原因是很多时候我们并没有意识到要交叉编译,交叉编译的错误也就无非是找不到库,头文件,或者说symbol 错误之类的。很多时候我们很容易将x86上面的库试图给arm链接器用,所以就会出错了。
所以,交叉编译要对一定要分清楚什么是主机的,什么是目标机器的。
2. 针对我们的情况 --- 先说说ARM GCC
2.1 什么是GCC ? 都包括那些组件? 之间什么关系?
GCC: GNU compiler collection. GNU编译器集合,通常可以支持c/c++, objective-c, ada, fortran, java , D language等。我们最常用的是c/c++,对应命令gcc, g++
实际上GCC是一组工具集,并且我们通常也会更大范围的说工具链(GCC环境)。
完整的工具链包括: binutils, GCC, libc, kernel headers.
- binutils:
二进制工具集: 通常就是汇编相关的东西,包括as,ld, objcopy, objdump等。任何编译型高级语言都有GCC转化为汇编文件,这些汇编文件就得交给binutils工具集来处理了。
- GCC
c/c++ 语言编译器,负责处理c/c++为汇编文件,当然GCC也可以自行调用二进制工具到最终的目标文件。
- libc
C/c++本身仅仅是个语言,我们通常写代码中用到的所谓系统函数,就是由libc提供的,如果没有libc,那么使用系统函数也就无从谈起了,很多时候单片机的开发就不需要c库。
ARM linux 里面 C库通常可以使用glibc( GNU libc), uClibc (micro controller libc), dietlibc, eglibc (embedded glibc)。
uClibc是跟busybox相关的一个项目,旨在提供嵌入式系统使用的小型c库,因为是micro的,所以一些posix调用没有也是很正常的事情了。
eglibc是glibc面向嵌入式的移植和修改,提供全部的库函数。
- kernel headers
两个作用: 编译内核驱动的时候使用; 编译用户空间程序的时候使用,libc通常也需要kernel headers, 因为kernel headers提供了linux系统调用函数定义。
2.2 gcc的常用参数说明
- -c只编译不链接,会将c文件转换给.o文件,并且是一个.c/cpp一个.o
- -I指定和添加头文件路径, 可以多重使用, 也就是-I是可以- N次的。
- -L
指定库文件所在路径,通-I一样可以多次使用,通常我们一个易犯的错误是认为我都-I过了么,为什么还说找不到函数定义? 因为.h里面仅仅是个函数的声明而已。
库文件.so, .lib, 或者.o里面才有函数真正的执行代码。所以库是二进制的,头文件仅仅是告诉我们如何使用函数,和编译器预处理的时候知道函数是什么样子。
- -l刚才是路径,现在是指明链接什么库 - lm 就是链接libm.so, 奇怪 ? 为什么我一起编译c程序什么不指定就可以了? --- 那是因为编译器后台处理了,它自己做了,没让你动。
3. 还是说正题吧: 通常我们要遇到的交叉编译
3.1 大型C/cpp项目的管理
- make
大型的c/cpp项目都是make管理的,这个就不多说了,大家最喜欢也都知道编译就敲make,只是不喜欢看到make 说error1, error2。。。 而已。
- autotools
autotools是什么 ? 写makefile很痛苦的,所以有牛人(redhat公司的)弄了个autotools,makefile自动生成,哈哈,很期待这样的东西吧,不过顺便说一句,这个autotools听着很牛,不过它的配置比makefile还难理解,不过我们通常不需要做这个,我们只需要知道这个东西最终生成了传说中的configure就行了。
- *nix 代码编译三部曲
./configure , make, makeinstall. autotools原本是打算达到这个目的的,不过往往事情没有这么简单,./configure这个shell脚本通常有很多的--,不过最需要知道的是--help, 这个是帮助,可以让你看到详细的参数说明. --prefix这个设置安装的路径,不过交叉编译的时候我们往往都是设置为不在主机上存在的--prefix=/usr,这是因为要让编译管理工具知道,程序是想在/usr下面安装的,目标板的系统是有这个路径的。
- ./configure --host= , --target= --build=
因为gcc的神奇特技,configure有这么三个参数,gcc,配合configure可以完成 在x86上编译出一个sparc处理器上运行的gcc然后给arm处理器编译代码,不过这种情况很少用。我们通常会用--host= --target=,这就是通常说的交叉编译,比如x86上给arm编译代码。
3.2 大部分时候你需要敲--target=arm-xxx
- --target=后面到底跟什么东西 ?
答案是机器名。也就是目标板上系统的名称。我们的情况就是 --target=armv5l-jztech-linux-uclibceabi,名字这么长 ? 其实这里的名字是有学问的,gcc因为支持很多种平台和处理器,而且gcc不只在linux上面运行,所以这里的名称会说明你用的什么处理器,什么系统,什么C库,什么ABI(application binary interface,二进制应用程序接口,跟api类似,不过编译器用的,我们知道这个名字就行了). 这里这个讨厌的长名字意思就是: 我们用的arm处理器,并且核心是v5的,然后是littel india, 然后厂商是jztech, 系统是linux(不是uclinux,也不是没有系统),uclibc库,eabi的ABI,所以除了jztech感觉是多的外,其他的都是必不可少的,传统的arm-linux-是gcc3.x的事情了,4.x至少也是arm-none-linux-.
- --target=xxx的作用
当你指定了这个参数的时候,configure脚本就会试图从你的系统中运行xxx-gcc, xxx-ld,等等工具来为你的目标板编译程序,而不是gcc,ld,等在你的x86上的东西。当你制定了--target的时候, 一般情况下,configure还会检测它运行所在的机器的gcc前缀,通常会检测出来i686-pc-linux这样的东西,如果编译的时候有些东西需要在主机上进行,那么就会用这样的gcc。所以你单独指定--target的时候,configure是知道什么是你的主机的,什么是你的arm的。
3.4 既然如此简单,为什么还会出错 ?
因为你在安装一个开始设计的时候根本没打算往其他平台上跑的代码。
那怎么办?
----- 强制!
CC=armv5l-jztech-linux-uclibceabi-gcc, LD=xxx-ld, ....
这样设置你的环境变量,make会知道替换gcc为你设定的这些值。
既然这个包开始没打算往arm上跑,那么编译出现各种找不到头文件,库就很正常了。又怎么办 ?
前面说的gcc参数啊,该用arm的东西的地方就
-I -L -l
4. 结语
上面的交叉编译说了一些基础的东西,都是心得,没有从网上复制粘贴,而且交叉编译这个东西有时候很简单,有时候就会非常难,问题一个就是编译不过,要么过了也段错误,那怎么办? Google, baidu, 到处搜罗呗。
然后如果允许可以弄弄LFS, CLFS, 编译过很多包之后,经验也就多了,也就顺手了。
阅读(2727) | 评论(0) | 转发(1) |