LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 2047|回复: 14

2周的奋斗结果——x86交叉工具链构建心得

[复制链接]
发表于 2007-8-22 21:39:26 | 显示全部楼层 |阅读模式
1.        内核头文件:遵循老方法,将内核源码包解压缩并进行配置后,将需要的头文件直接拷贝到目标目录下:
# tar jxvf linux-2.6.10.x.tar.bz2
# cd linux-2.6.10.x
make ARCH=i386 CROSS_COMPILE=i386-linux- menuconfig
变量ARCH=i386告诉编译器目标版的CPU架构是i386类型的;将变量CROSS_COMPILE设置为i386-linux- ,Makefile会在其后添加gcc,使得编译工具变为i386-linux-gcc,这个工具一般在/usr/bin/目录下,但未必是这个名字。有的系统,例如FC6会将其命名为i386-redhat-linux-gcc,这种情况下,shell会提示i386-linux-gcc:command not found,即找不到这个程序。可以在/usr/bin目录下建立一个符号链接:
ln –s /usr/bin/i386-redhat-linux-gcc  /usr/bin/i386-linux-gcc
或者不用管它,因为这一步与交叉编译还没有关系。
2.6的内核与2.4的内核不同,在执行make menuconfig之后不会自动生成version.h这个头文件,而这个头文件在后续的glibc的安装过程中是必需的,所以要手动生成这个文件:
Make  include/linux/version.h
最后将生成的内核头文件复制到目标目录下:
mkdir –p ${TARGET_PREFIX}/include
cp –r include/linux  ${TARGET_PREFIX}/include
cp –r include/asm-i386  ${TARGET_PREFIX}/include/asm
cp –r include/asm-generic  ${TARGET_PREFIX}/include
头文件的设置已经完成,但是不要删除内核源码包,glibc的编译过程需要指定内核源码包的位置,从而搜寻所需要的头文件,包括刚刚生成的version.h

2.        二进制程序binutiles:
首先设置一些变量,这些变量在后续的交叉工具链的建立过程中始终需要。
export PREFIX=~/target
export TARGET=i386-linux
export TARGET_PREFIX=$PREFIX/$TARGET

Binutile的安装过程应该是最顺利的,它需要的条件很少,最终会在目标下安装二进制目标文件。其中最重要的2个工具是汇编器as和连接器ld。
mkdir build-binutiles
cd build-binutiles
对软件包进行配置:
../binutils-2.17/configure --prefix=${PREFIX} \
--target=$TARGET  
prefix和target选项告诉编译器二进制文件的安装位置和这些二进制文件都是面向交叉编译的。
接下来编译并安装:
Make && make install

3.        glibc头文件glibc-linuxthreads的安装
glibc-linuxthreads是linux的附加包,里面包括了一些重要的头文件,这些文件在GCC已经Glibc的安装中都需要。但是,glibc-2.3.6之后的版本放弃了对linuxthreads的支持。
将glibc-linuxthreads解包到glibc的源码目录下面进行安装:
# tar  jxvf  ../tars/glibc-2.3.6.tar.bz2
# tar  jxvf  ../tars/glibc-linuxthreads.tar.bz2  --directory=glibc-2.3.5
glibc的readme建议用户在源码包之外的目录下编译安装,所以建立一个目录
# mkdir  build-glibc-headers  &&  cd  build-glibc-headers
# ../glibc-2.3.5/configure  --prefix=/usr –host=$TARGET –enable-add-ons –with-headers=$TARGET_PREFIX/include
--enable-add-ons选项告诉编译器在编译glibc-linuxthreads的时候添加对附加包的支持
--with-headers=$TARGET_PREFIX/include选项告诉编译器,使用刚刚产生的、将来要用在目标机器上新的头文件,而不是用本来宿主系统下的头文件。
# make  cross-compiling=yes  install_root=$TARGET_PREFIX  prefix=”” install-headers
由于目前使用的编译器还不是交叉编译器,将cross-compiling设置为yes,就不会建立原生的链接库;
install_root=$TARGET_PREFIX告诉编译器头文件的安装目录;
install-headers告诉编译器只安装头文件。

4.        建立引导编译器gcc-3.4.2
解压缩gcc源码包后进入并打1个补丁:
# tar  jxvf  ../tars/gcc-3.4.2.tar.bz2  &&  cd  gcc-3.4.2
patch -Np1 -i ../ gcc-3.4.2-20040916-1-src.diff
GCC的安装说明中明确提出,建议在gcc源码包之外的目录下编译gcc,因此最好不要在源码目录下操作。进入预先设置好的build-boot-gcc进行编译:
# mkdir  build-boot-gcc  &&  # cd  build-boot-gcc
../gcc-3.4.2/configure --prefix=${PREFIX} \
--target=$TARGET  --disable-shared \
--with-newlib  --enable-languages=c
--with-headers=$TARGET_PREFIX/include
--disable-shared这个选项告诉gcc在编译时不要生成共享库。这一项设置与否都不会报错;
--with-newlib选项告诉编译器不要使用glibc,因为针对目标版的glibc尚未编译安装;
--with-headers=$TARGET_PREFIX/include告诉编译器使用刚刚产生的内核头文件,而不是宿主系统上的。
编译以及安装启动编译器;
# make  all-gcc  &&  # make  install

5.        编译安装glibc
glibc在建立工具链的软件中是最“脆弱”的一个,由于glibc与Gcc的兼容性问题,它极易受到GCC版本的影响,因此在安装glibc之前很有必要调查一下匹配比较好的GCC-Glibc的版本组合,包括与即将建立的GCC编译器的版本兼容性和宿主系统GCC的版本兼容性。我这里选择了GCC-3.4.2-Glibc-2.3.5-GCC-4.1.0这个组合,并通过一些补丁程序以及对源码包里文件的修改,工具链最终搭建起来。
首先在Glibc的源码目录为其打6个补丁:
# cd glibc-2.3.5
# patch –Np1 –i ../ glibc-2.3.5-gcc4_fix_elf-1.patch
# patch –Np1 –i ../ glibc-2.3.5-gcc4_fix_iconvdata-1.patch
# patch –Np1 –i ../ glibc-2.3.5-gcc4_fix_string-1.patch
# patch –Np1 –i ../ glibc-2.3.5-gcc4_fix_symbols-1.patch
# patch –Np1 –i ../ glibc-libgcc_eh.patch
# patch –Np1 –i ../ libpthread.diff
其中,第一个补丁是解决elf问题的;第四个补丁修复符号链接问题;第5个补丁解决i386-linux/bin/ld: cannot find –lgcc_eh的错误;第6个补丁用来解决多线程问题:libpthread.a的问题,如图所示:

接下来在glibc源码目录之外的目录下进行编译和安装:
# cd ..  &&  # mkdir build-glibc  &&  # cd build-glibc
# CC=i386-linux-gcc ../glibc-2.3.5/configure --prefix=/usr --host=i386-linux --build=i686-linux \ --enable-add-ons=linuxthreads –with-headers=$TARGET_PREFIX/include --enable-kernel=2.6.10 \
--with-binutils=$PREFIX/bin
CC=i386-linux-gcc:binutils产生的交叉编译工具来编译;
--prefix=/usr:这里的prefix表示glibc安装到目标版根文件系统后,其链接库组件的位置,即,安装到目标版的根文件系统的/usr目录下;
--host=i386-linux:告诉编译器,最终的文件要在x86架构的目标版上运行;
--build=i686-linux:宿主系统的cpu架构,这一项最好不要省略,INSTALL文件中提到,--host和--build最好要一起出现;
--enable-add-ons=linuxthreads:使用linux附加包,在linuxthreads目录下;
--with-headers=$TARGET_PREFIX/include:使用刚生成在目标版上的头文件进行编译;
--enable-kernel=2.6.10:只支持2.6.10版本以上的内核,这样做使得工具链以及以后的目标版程序不再支持2.6.10版本以下的内核,启动时会提示内核太旧。优点是不编译安装与2.6.10版本以前的内核的文件。
--with-binutils=$PREFIX/bin:设定搜索编译器的路径。这一项是很必要的,因为我们已经设置了交叉编译工具为i386-linux-gcc,如果没有这一项,就会在配置的过程中出错:
#>checking size of long double... configure: error: cannot compute sizeof (long  double), 77
在编译过程中会出现有关于UNAME的一些问题,编译器会通知一些形如UNAME_SYSNAME的变量没有声明,从而致使编译中止,为了解决此问题,需要自己亲手做一个文件:
# vim $PREFIX/build/glibc-2.3.5/linuxthreads/sysdeps/generic/config-name.h
在vim编辑器里输入:
#ifdef config_name_h
#define config_name_h

#define UNAME_SYSNAME “Linux”
#define UNAME_RELEASE “2.6.10”
#define UNAME_VERSION “1 SMP FRI JUN 4 16:20:25 CST 2007”
#define UNAME_MACHINE “i386”

#endif /* config_name_h */
这些变量的内容都是根据命令uname –a 输出进行填写。
准备工作做好了,进行编译和安装:
# LC_ALL=c make
# make install_root=$TARGET_PREFIX  prefix=”” install
install_root告诉编译器文件的安装位置;Makefile会将$prefix加在$install_root的后面形成完整的安装路径。由于在install_root中已经给出了安装路径,这里我设置prefix为空。

6.        完整编译器gcc-4.1.0的编译与安装
解压gcc-4.1.0并在源码包之外的目录下进行编译安装:
# tar jxvf ../tars/gcc-4.1.0.tar.bz2  &&  cd gcc-4.1.0
为gcc源码包打1个补丁:
# patch –Np1 –i ../ 88_all_gcc-make-POSIX-syntax-updates.patch
这个补丁是用来修复gcc-4.1.0在glibc-2.3.5下的语法错误,形如:
error: expected ‘)’ before the ‘;’ token
在glibc的安装过程中,由于make install 的命令安装会假定将链接库安装到根文件系统,因此libc.so链接命令脚本中以绝对路径给出了需要的链接库,这会导致编译过程的失败,因此要修改这个文件。
# vim $TARGET_PREFIX/lib/libc.so
去掉所有的/lib/字样
# cd .. &&  mkdir build-gcc  &&  cd  build-gcc
# ../gcc-4.1.0/configure --prefix=$PREFIX --target=i386-linux --enable-languages=c,c++
# make all  &&  make install
交叉工具链到这里就做好了,自己可以编写一个小程序进行测试,如果没什么问题,说明交叉工具链构建成功,已经可以用来构建嵌入式linux系统了。

本人菜鸟初级,水平有限,如有错误请多多指教,呵呵

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2007-8-22 22:11:17 | 显示全部楼层
我没理解错吧,楼主是要建立一个 build=i386-linux host=i386-linux target=i386-linux 的“交叉”工具链么?有什么充足的理由这样做么
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-23 13:48:56 | 显示全部楼层
其实不用这么做,只是为了在嵌入式的时候更加完整一点,呵呵。应该是build=i686-linux target=i386-linux,我做的时候,感觉i686和i386在编译的时候是有区别的。
回复 支持 反对

使用道具 举报

发表于 2007-8-23 20:54:25 | 显示全部楼层
收藏先,以后慢慢研究研究。
回复 支持 反对

使用道具 举报

发表于 2007-8-23 21:38:57 | 显示全部楼层
可以说,完全没意义

要是想练手又没有开发板的话,可以在 linux 上编译目标为 win32 的工具链,就像 mingw32,这样至少算是交叉工具链了。

虽然嵌入式不是没有用 x86 架构 CPU 的,但是用的真的比较少。而且编译器能用与否并不只是由它自身能否被编译通过决定的,更多的时候只有用它去编译其它的代码,能够编译通过,并且能够正常运行,那么才能说它是成功的。这种本地的“交叉”编译器几乎没有失败的可能,即使是本地环境或者库掺进去了它一样可以工作。

如果一定要制作本地的工具链,用它做一遍 lfs 是检验它能否正常工作的好办法,它在切换 root 后可以脱离本地环境,至少可以摆脱本地库的影响。但是即使如此,能够完成本地工具链的编译方法不见得可以完成交叉工具链的制作,因为两种情况实在是有本质的不同的
回复 支持 反对

使用道具 举报

发表于 2007-8-24 10:05:31 | 显示全部楼层
Post by remote fish
可以说,完全没意义

要是想练手又没有开发板的话,可以在 linux 上编译目标为 win32 的工具链,就像 mingw32,这样至少算是交叉工具链了。

虽然嵌入式不是没有用 x86 架构 CPU 的,但是用的真的比较少。而且编译器能用与否并不只是由它自身能否被编译通过决定的,更多的时候只有用它去编译其它的代码,能够编译通过,并且能够正常运行,那么才能说它是成功的。这种本地的“交叉”编译器几乎没有失败的可能,即使是本地环境或者库掺进去了它一样可以工作。

如果一定要制作本地的工具链,用它做一遍 lfs 是检验它能否正常工作的好办法,它在切换 root 后可以脱离本地环境,至少可以摆脱本地库的影响。但是即使如此,能够完成本地工具链的编译方法不见得可以完成交叉工具链的制作,因为两种情况实在是有本质的不同的



支持楼上的说法.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-30 10:01:36 | 显示全部楼层
过一段时间做一个arm的试试。
回复 支持 反对

使用道具 举报

发表于 2007-8-30 10:54:51 | 显示全部楼层
楼主,编译libc.so那不行阿,打开都是乱码~
回复 支持 反对

使用道具 举报

发表于 2007-8-31 13:24:48 | 显示全部楼层
不管有没有实际价值,我相信楼主通过这次实践一定也学到了不少东西!
所以无论如何都得支持一下!
回复 支持 反对

使用道具 举报

发表于 2007-8-31 19:51:02 | 显示全部楼层
我也支持,希望楼主取得进一步的成果再发上来与大家分享。

P.S. 回顾一下才发现,我上面的两个回复似乎措辞不太合适,希望楼主不要见怪,在下绝无不敬之意
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表