Linux 交叉编译工具链

本文连接:http://blog.chinaunix.net/uid-2630593-id-2138544.html

搞了一个“中基学生电脑”(详细的见这里http://www.ouravr.com/bbs /bbs_content.jsp?bbs_sn=1420850&bbs_page_no=1&search_mode=4& search_text=dack&bbs_id=9999),当作arm开发板用。但本人对linux不熟,arm更是没有搞过,所以从头开始 学,先编译一套工具链才能进行下一步。
了解到有crosstools可以方便的编译工具链,甚至还有编译好的下载,但是鉴于本人初学,什么都不懂,决定还是自己一步一步编译,权当熟 悉linux,熟悉一下编译流程了。花了一个多星期终于搞好了,其实第一次编译成功用了不到一个星期,为了写这篇文章和搞清楚其中的一些不明所以的地方, 又重新编译了两次,现在基本搞清楚了,发上来与大家共享。其实还有一些不明所以的地方,以后再说吧,实在不想再编译了。
中基是基于sa1110芯片的,主要部分和assabet开发板一样。以下linux头文件是基于这样的系统产生的,不过我想也能用于其他型号的芯片。
先说说我的编译环境,一台384M内存的p4电脑,win2000系统,开vmware虚拟机,装了一个puppy linux 3.01,这个linux以小巧见称。可想而知有多慢了。
个人感觉,编译这个很考人品,人品不好总会碰到一些稀奇古怪的问题。呵呵,我估计是那个人品最差的。
虽然我已经很小心,最后一遍命令都是粘贴上去执行的,后面又进行了一些修改,主要是安装目录,但文中难免有错漏,发现了请指出。
初学者可以尝试自己编译工具链,能够学到一下东西。
所有用到的软件都是最新版的。
好了,闲话少说,开干。

1.下载
ftp://mirrors.kernel.org/gnu/binutils/binutils-2.18.tar.gz
ftp://mirrors.kernel.org/gnu/gcc/gcc-4.3.2/gcc-4.3.2.tar.gz
ftp://mirrors.kernel.org/gnu/gdb/gdb-6.8.tar.gz
ftp://mirrors.kernel.org/gnu/glibc/glibc-2.7.tar.gz
ftp://mirrors.kernel.org/gnu/glibc/glibc-ports-2.7.tar.gz
http://ftp.osuosl.org/pub/clfs/conglomeration/glibc/glibc-2.7-libgcc_eh-1.patch
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.27.tar.gz
http://www.mpfr.org/mpfr-current/mpfr-2.3.2.tar.gz
ftp://ftp.gmplib.org/pub/gmp-4.2.4.tar.gz

2.建立工作目录
我的工作目录放在一个单独的盘上,/mnt/hdc1。你可以任选一个目录做工作目录,例如~/work。将下载的文件全部放到工作目录下。
工作目录中建立目录build,用来放解压后的源码和build时产生的文件,最终产生的工具放到/usr/armtools下。
3.设置环境变量
在etc/ld.so.conf中加上一行/usr/local/lib,用来设置动态库的搜索目录,已经有的话就不用加了,因为gmp和mpfr会装到这里,不加以后的编译通不过。
开一个终端窗口
export TARGET=arm-linux
export PREFIX=/usr/armtools
export PATH=$PATH:/usr/armtools/bin
export BUILD_DIR=/mnt/hdc1/build
4.安装gmp和mpfr库,这两个库是gcc需要的,老版本如果不用fortan77的话不需要,但新版本只编译c也需要。有的话就不用装了。
cd /mnt/hdc1 进入工作目录
cd build
tar -zxvf ../gmp-4.2.4.tar.gz 这里有个技巧,打完gm后直接按tab键文件名会自动补全。后面需要输入目录的地方都可以这么用。
cd gmp-4.2.4
./configure
make
make check 文档里说这个非常重要。
make install
cd ..
rm -rf gmp-4.2.4 装完源目录没用了,删掉,节省空间。

ldconfig /usr/local/bin
tar -zxvf ../mpfr-2.3.2.tar.gz 这里有个技巧,打完gm后直接按tab键文件名会自动补全。后面需要输入目录的地方都可以这么用。
cd mpfr-2.3.2
./configure
make
make check 文档里说这个非常重要。
make install
cd ..
rm -rf mpfr-2.3.2 装完源目录没用了,删掉,节省空间。

ldconfig /usr/local/bin 更新一下缓存
完成,这一步一般不会出问题。
5.编译binutils
tar -zxvf ../binutils-2.18.tar.gz
mkdir binutils-2.18-build
cd binutils-2.18-build
../binutils-2.18/configure --target=$TARGET --prefix=$PREFIX
make
make install
cd ..
rm -rf binutils-2.18
rm -rf binutils-2.18-build
ok,这一步也很简单,完成后armtools里会有很多文件。
6.编译bootstrap gcc
tar -zxvf ../gcc-4.3.2.tar.gz
mkdir gcc-4.3.2-build
cd gcc-4.3.2-build
../gcc-4.3.2/configure --target=$TARGET --prefix=$PREFIX --enable-languages=c --with-local-prefix=$PREFIX/arm-linux --without-headers --with-newlib --disable-shared --disable-threads --disable-libmudflap --disable-libssp
这一步参数比较多,大概说一下
--target=$TARGET 目标,arm-linux,就是要产生linux下编译arm程序的编译器
--prefix=$PREFIX 安装目录
--enable-languages=c 只产生c编译器
--with-local-prefix=$PREFIX/arm-linux 不知道有啥用,实在不想再编译一遍试验了,谁有空可以试试不加行不行。
--without-headers 不使用头文件,因为还没有glibc
--with-newlib 这个不太明白,似乎是编译一个自带的库
--disable-shared 不编译共享库
--disable-threads 不编译线程支持
--disable-libmudflap 禁止mudflap库,这个库需要glibc支持
--disable-libssp 禁止ssp库,这个库需要glibc支持

make all-gcc 编译安装gcc
make install-gcc
make all-target-libgcc 编译安装库,不做的话编译glibc时找不到库
make install-target-libgcc
cd ..
rm -rf gcc-4.3.2-build
7.建立linux头文件
tar -zxvf ../linux-2.6.27.tar.gz
cd linux-2.6.27
make ARCH=arm menuconfig
菜单最下面选择load一个arm板子的config文件,我load的arch/arm/configs/assabet_defconfig,然后保存为.config。退出
make ARCH=arm CROSS_COMPILE=$PREFIX/bin/arm-linux- 出现CC什么的时候就可以停止了。
cd ..
2.6.27版和2.6.26.5版比目录结构发生了变化,所以编译glibc时要改路径,否则找不到头文件。编译glibc时候用到了特定arm芯片(例如我用的sa1100)的头文件,难道编译出来的glibc只能用在特定的芯片上吗,有知道的说一下。
8.编译glibc
tar -zxvf ../glibc-2.7.tar.gz
cd glibc-2.7
tar -zxvf ../../glibc-ports-2.7.tar.gz glibc本身是不支持arm等的,需要加这个port。
mv glibc-ports-2.7 ports
打补丁,sed -i ‘s/aaa/bbb/‘ file 就是把file里的aaa替换成bbb,bbb里面的&代表 aaa,\t代表tab,\n代表换行。你也可以用文本编辑器来做,用sed做要注意不要打错任何一个字符,空格啦,大小写啦都要注意。
sed -i ‘s/-nostdinc -isystem $ccheaders /-nostdinc -isystem $ccheaders -isystem $ccheaders-fixed /‘ configure.in
sed -i ‘s/-isystem $cxxheaders /-isystem $cxxheaders -isystem $cxxheaders-fixed /‘ configure.in
sed -i ‘s/# define UNDOCARGS_5\tUNDOCARGS_4/&\n\n# define DOCARGS_6\tUNDOCARGS_5\n# define UNDOCARGS_6\tUNDOCARGS_5/‘ ports/sysdeps/unix/sysv/linux/arm/nptl/sysdep-cancel.h
sed -i ‘s/#include /&\n#include /‘ ports/sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.h
sed -i ‘s/__deprecated/__attribute__((deprecated))/‘ ../linux-2.6.27/arch/arm/include/asm/memory.h
patch -p1 < ../../glibc-2.7-libgcc_eh-1.patch cd .. mkdir glibc-2.7-build cd glibc-2.7-build CC=arm-linux-gcc AR=arm-linux-ar RANLIB=arm-linux-ranlib ../glibc-2.7/configure --host=$TARGET --prefix=$PREFIX/$TARGET --enable-add-ons --with-headers=$BUILD_DIR/linux-2.6.27/include:$BUILD_DIR/linux-2.6.27/arch/arm/include:$BUILD_DIR/linux-2.6.27/arch/arm/mach-sa1100/include libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes cross_compiling=yes build_alias=i386-linux-gnu 上面这句里的cross_compiling=yes build_alias=i386-linux-gnu不是必需的,我第一遍编 译时没加也通过了,但后来不知道为什么就不行了,如果出错或者make出错可以加上试试,主要是因为configure自动判定交叉编译判断错误,把交叉 编译出来的文件在本地执行,当然执行不了啦。 make make install cd .. rm -rf glibc-2.7-build rm -rf glibc-2.7 9.拷贝linux头文件 cp -r linux-2.6.27/include/linux $PREFIX/$TARGET/include cp -r linux-2.6.27/arch/arm/include/asm $PREFIX/$TARGET/include mv $PREFIX/$TARGET/include/asm $PREFIX/$TARGET/include/asm-arm cp -r -f linux-2.6.27/include/asm-arm $PREFIX/$TARGET/include cp -r linux-2.6.27/include/asm $PREFIX/$TARGET/include cp -r linux-2.6.27/include/asm-generic $PREFIX/$TARGET/include cp -r linux-2.6.27/arch/arm/mach-sa1100 $PREFIX/$TARGET/include mv $PREFIX/$TARGET/include/mach-sa1100 $PREFIX/$TARGET/include/mach 这些文件不但编译glibc gcc时有用,而且似乎在以后编译应用程序的时候也可能有用,因为glibc库的头文件里有包含他们,所以我把他们拷贝到安装目录里。 10.编译gcc mkdir gcc-4.3.2-build cd gcc-4.3.2-build ../gcc-4.3.2/configure --target=$TARGET --prefix=$PREFIX --enable-languages=c,c++ --with-local-prefix=$PREFIX/$TARGET make make install cd ../.. rm -rf build 到此,工具链就算装好了,可以写个hello world程序来试验一下了。 然后把我前面的艰辛历程贴出来,如果大家编译的时候发生什么问题,可以在里面找一下有没有我碰到过的。格式嘛,我就不排版了,大家凑活看吧。 1. 安装linux 的头文件,可以在编译glibc之前再做 * 个人认为这一步得到的头文件应该只和arm有关,和arm什么板子,什么型号没多大关系 * 解压缩,打补丁 cd ~/tars/SourceDir tar -zxf ../linux-2.4.5.tar.gz cd linux zcat ../../patch-2.4.5-rmk7.gz | patch -p1 打补丁不知道为什么出错,先不打了。原来是patch-2.6.26.5.bz2是给linux-2.6.26打补丁的,不是给2.6.26.5打补丁的 * 修改 Makefile 建议先删除 .config 文件, 否这以后会遇到麻烦。没用 * 将Makefile中ARCH := ......改为:ARCH=arm #。似乎没用 * 执行一下 make clean。一开始不需要 * 我的做法:make ARCH=arm menuconfig * 选择load一个arm板子的config文件,我load的ARCH/arm/configs/assabet_defconfig,然后保存为.config。退出 * make ARCH=arm dep * 加上ARCH=arm是因为不加的话会有好多提问,像是否支持多cpu等,后面的dep不知道什么意思,待查。 * 产生了include/linux/autoconf.h,但是没有version.h。不知道是不是现在的版本不需要。 * 网上查的make dep的意思,不过还是不太明白 * dependence 依赖。 make dep的意思就是说:如果你使用程序A(比如支持特殊设备),而A需用到B(比如B是A的一 个模块/子程序)。 而你在做make config的时候将一个设备的驱动 由内核支持改为module,或取消支持,这将可能影响到B的一个参数 的设置,需重新编译B,重新编译或连接A....如果程序数量非常多, 你是很难手工完全做好此工作的。 所以,你要make dep。如果你make menu或make config或make xconfig后,直接reboot,会更快。 只是你的内核根本没有任何改变。^=^ make xconfig; make dep; make clean; make bzImage; make modules; make modules_install * 又作了一次,发现make dep现在不起作用,要用make ARCH=arm来建立头文件。 * 要用make ARCH=arm CROSS_COMPILE=/mnt/home/arm/armtools/bin/arm-linux-来编译,必须先编译好bootstrap gcc后才能这样用 * 拷贝头文件,感觉不需要拷贝,编译glibc的时候加上路径就好了。 cp -dR include/linux ~/armtools/arm-linux/include cp -dR include/asm-arm ~/armtools/arm-linux/include/asm 1. 编译安装binutils  * 解压缩 cd ~/tars/SourceDir tar -zxf ../binutils-2.11.gz * 编译 cd ~/tars/BuildDir mkdir binutils cd binutils ../../SourceDir/binutils-2.11/configure --target=arm-linux --prefix=~/armtools make all install * 这一步没什么问题,很顺利 2. 编译安装gcc 的c 编译器   * 解压缩 cd ~/tars/SourceDir tar -zxf ../gcc-2.95.3.tar.gz * 修改gcc 的t-linux 文件在t-linux文件中的TARGET_LIBGCC2_CFLAGS上加上__gthr_posix_h inhibit_libc cd gcc-2.95.3/ gcc/config/arm mv t-linux t-linux-orig sed ‘s/TARGET_LIBGCC2_CFLAGS =/TARGET_LIBGCC2_CFLAGS = -D__gthr_posix_h -Dinhibit_libc/‘ <> t-linux-core
cp ./t-linux-core ./t-linux

*

编译

cd ~/tars/BuildDir
mkdir gcc-core
cd gcc-core
../../SourceDir/gcc-2.95.3/configure \
--target=arm-linux \
--prefix=~/armtools \
--enable-languages=c \
--with-local-prefix=~/armtools/arm-linux \
--without-headers \
--with-newlib \
--disable-shared
make all install

* ./configure 时需要gmp和mpfr库支持,下载,安装,顺利,已做成pet包。换了一台机重新编译的时候,发现编译mpfr的时候会出错,找不到库,还是要加上下面那个路径。
* ./configure 通过
* make 出错,查看arm-linux/libgcc/config.log,说找不到libmpfr.so.1,估 计是做的pet包没有将ld搜索路径加上,先加上环境变量试下,回头再修改pet包。 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
* 加上后上个问题没有了,又有新问题。
* config.log says:
* configure:2747: checking for /mnt/hdc1/build/gcc-core/./gcc/xgcc -B/mnt/hdc1/build/gcc-core/./gcc/ -B/mnt/hdc1/armtools/arm-linux/bin/ -B/mnt/hdc1/armtools/arm-linux/lib/ -isystem /mnt/hdc1/armtools/arm-linux/include -isystem /mnt/hdc1/armtools/arm-linux/sys-include option to accept ANSI C
configure:2817: /mnt/hdc1/build/gcc-core/./gcc/xgcc -B/mnt/hdc1/build/gcc-core/./gcc/ -B/mnt/hdc1/armtools/arm-linux/bin/ -B/mnt/hdc1/armtools/arm-linux/lib/ -isystem /mnt/hdc1/armtools/arm-linux/include -isystem /mnt/hdc1/armtools/arm-linux/sys-include -c -O2 -g -g -O2 conftest.c >&5
conftest.c:10:19: error: stdio.h: No such file or directory
conftest.c:11:23: error: sys/types.h: No such file or directory
conftest.c:12:22: error: sys/stat.h: No such file or directory
conftest.c:15: error: expected ‘=‘, ‘,‘, ‘;‘, ‘asm‘ or ‘__attribute__‘ before ‘*‘ token
conftest.c:44: error: expected declaration specifiers or ‘...‘ before ‘FILE‘
configure:2823: $? = 1
* try to add --disable-libiberty and make it
* 上面的没用,改--disable-shared为--disable-threads,似乎这个错误过去了
* 发现修改t-linux里的-D__gthr_posix_h这个现在已经没用了。-Dinhibit_libc这个参数似乎可以用--with-newlib代替。
* 然后又出找不到crti.o的错误。重新加上--disable-shared,搞定
* 然后又出问题,c编译器不能建立可执行文件。找不到crt1.o。查看发现gcc编译完后进行了编译测试,但这时候还没有这些启动文件,据说这些是编译glibc产生的。
* configure时加上--disable-libmudflap,不加似乎也行,不要理会错误,直接make install就可以把gcc装上了。似乎有问题,make glibc的时候找不到头文件。
* 加上--disable-libssp --disable-libgomp终于编译通过了,make install没有问题。
* 重新make一次,不知道怎么又出错了,奇怪。而且make clean有的时候只显示几行,而有的时候显示一大堆,不知道为什么。
* 改成make all-gcc和make install-gcc通过,不知道行不行。
3. 编译glibc
1. 因为现在glibc包不支持arm,所以要加上port包,解压port包到glibc原目录,改名为ports。
2.

CC=arm-linux-gcc AR=arm-linux-ar RANLIB=arm-linux-ranlib ../../SourceDir/glibc-2.2.3/configure --host=arm-linux \
--prefix=~/armtools/arm-linux --enable-add-ons --with-headers=~armtools/arm-linux/include

3. 报错configure: error: forced unwind support is required
4. 回到gcc,try make all-target-libgcc make install-target- libgcc 通过,但上述问题还在。第二次编译产生cannot compute sizeof(long double) 的错误。这个错误又好像不 是这个引起。结果是下面两句写错了。还是不对,加上--build=i686-pc-linux-gnu后通过
5. 加上两句echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache,然后 configure加上 --cache-file=config.cache,通过。这两句可以直接把双引号里的内容写到configure后面做参 数,而不用--cache- file参数。
6. make又出问题,找不到../include/limits.h,打补丁http://sources.redhat.com/bugzilla/show_bug.cgi?id=5442后通过。

glibc/libc/configure.in
if test -n "$sysheaders"; then
ccheaders=`$CC -print-file-name=include`
- SYSINCLUDES="-nostdinc -isystem $ccheaders \
+ SYSINCLUDES="-nostdinc -isystem $ccheaders -isystem $ccheaders-fixed \

1. 再出问题,找不到errno.h,copy linux源目录下include/asm-generic到armtools/arm-linux/include下,通过
2. /usr/local/arm-linux/include/asm/memory.h:191: error: expected ‘=‘, ‘,‘, ‘;‘, ‘asm‘ or ‘__attribute‘ before ‘unsigned‘
/usr/local/arm-linux/include/asm/memory.h:196: error: expected ‘=‘, ‘,‘, ‘;‘, ‘asm‘ or ‘__attribute‘ before ‘void‘
3. 找不到解决方法,尝试修改autoconf.h里的#define CONFIG_ENABLE_WARN_DEPRECATED 1为0,无效
4. 找了好久,分析源码,终于找到问题所在,但在网上查不到解决办法,只能自己改了,应该不会有其它问题。这个问题的原因是 ioperm.c(port里arm 中带的)包含了linux头文件asm/page.h,page.h又包含了linux /memory.h,memory.h包含了linux /compiler.h,这里面有个条件编译,如果定义了__KERNEL__就会包含 compiler_gcc4.h等等,反正会定义一个 __deprecated属性,而如果没有定义__KERNEL__就不会定义这个属性,但是 memory.h里两次用到这个属性,结果就出问题。我的改法是把memory.h里的 __deprecated改成 __attribute__((deprecated))。第二次编译又没发现这个问题,奇怪。可能是升级了linux版本的原因。
5. 然后不出所料,还是有问题,: Assembler messages:
:2: Error: bad instruction `docargs_6‘
:2: Error: bad instruction `undocargs_6‘,这个网上有解决办法。
6.

改ports/sysdeps/unix/sysv/linux/arm/nptl/sysdep-cancel.h里增加蓝色的两行
# define DOCARGS_5 DOCARGS_4
# define UNDOCARGS_5 UNDOCARGS_4

# define DOCARGS_6 DOCARGS_5
# define UNDOCARGS_6 UNDOCARGS_5

# ifdef IS_IN_libpthread
# define CENABLE bl PLTJMP(__pthread_enable_asynccancel)
# define CDISABLE bl PLTJMP(__pthread_disable_asynccancel)

7. lowlevelloch.c:34出问题

在ports/sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.h里加一行
#include 
#include 
+#include 

8. 终于make通过了,make install
9. 通过,开始编译gcc
10.

../../SourceDir/gcc-2.95.3/configure --target=arm-linux --prefix=~/armtools \
--enable-languages=c,c++ --with-local-prefix=~armtools/arm-linux

11. make
12. make install
13. 成功
14. 编译一个例子,然后准备开始第二次编译工具链
15. 看到有新的linux,换用新的linux,结果好多问题,include/asm下的东西都移走了,移到arch/arm/include/asm下了,编译glibc又出好多新问题
16. 第二次编译glibc产生cannot compute sizeof(long double) 的错误。这个是 configure自动识别build类型错误,并且认为不是交叉编译造成的,加上--build=i686-pc-linux-gnu后通过 configure,但make产生redefinition of ‘__copysign‘ 的错误,改成--build=i386-linux- gnu后通过
17. 又出config-name.h找不到的问题,这个文件应该是configure产生的。
18. 看样子是和我用虚拟机有关,编译工具链个人感觉极考人品,顺不顺利看人品了。
19. 上面发现的问题看来是和configure检查系统环境出现偏差有关
20. 改成CC=arm-linux-gcc AR=arm-linux-ar RANLIB=arm-linux- ranlib ../glibc-2.7/configure --host=arm-linux --prefix=/mnt/hdc1 /armtools1 --enable-add-ons --with-headers=/mnt/hdc1/build/linux-2.6.27 /include:/mnt/hdc1/build/linux-2.6.27/arch/arm /include cross_compiling=yes build_alias=i386-linux-gnu后配置通过,make出现问题。还是 因为linux改了目录结构引起的,拷贝需要的文件后ok
21. 在编译gcc的时候不小心输错目录,在原文件目录下进行了编译,结果出错。一定不能在源文件目录下编译。
22. 第三次编译glibc出ld找不到-lgcc的问题,是没有make all-target-libgcc和make install-target-libgcc
23. 找不到-lgcc_eh,原因是没打补丁,又要退回去重做。

Linux 交叉编译工具链

上一篇:C#笔试(程序设计)


下一篇:曲面的外在几何(三)---曲面上的曲线(更新中……)