Building arm-linux toolchain for ARM/XSCALE
1.准备配套版本的资源。 binutils-2.16gcc-3.4.6glibc-2.3.6glibc-threads-2.3.6linux-2.6.11 注意:(1)可以根据上述资源间的版本依赖关系,选择另外一套方案;(2)linux-2.6.11内核源代码是为了编译gcc和glibc时提供相关的头文件,可以选择其他版本的内核,但应注意内核
1.准备配套版本的资源。
binutils-2.16
gcc-3.4.6
glibc-2.3.6
glibc-threads-2.3.6
linux-2.6.11
注意:(1)可以根据上述资源间的版本依赖关系,选择另外一套方案;(2)linux-2.6.11内核源代码是为了编译gcc和glibc时提供相关的头文件,可以选择其他版本的内核,但应注意内核版本与gcc等工具的版本依赖关系。另外,所选的内核版本最好与要移植到目标系统的内核版本一致。(3)建立工具所用的主机(host)上应该也安装有一套用于编译生成本地glibc的内核头文件,通常在/usr/src/目录下的源码里。[1]
2.目录组织与环境变量设置
Liod_XSBase ($PRJROOT) | ||||
toolchain ($PREFIX) | build-tools | kernel | ||
arm-linux ($TARGET_PREFIX) | build-binutils | build-boot-gcc | ...... | linux-2.6.11 |
上表是建立toolchain过程中的目录层次结构。Liod_XSBase是工程的顶层目录;toolchain文件夹将包含最终生成的toolchain工具,其下的arm-linux用于存放目标相关的头文件和库;build-tools目录下存放binutils-2.16.tar.gz等资源包,以及build过程中所用的临时文件夹,如build-gcc, build-glibc等;kernel目录下为linux内核源代码。
(1)设置环境变量:
export PROJECT=Liod_XSBase
export PRJROOT=/home/aaronwong/${PROJECT}
export TARGET=arm-linux #toolchain目标类型
export PREFIX=${PRJROOT}/toolchain #toolchain安装路径
export TARGET_PREFIX=${PREFIX}/${TARGET} #存放目标相关的头文件和库
export PATH=${PREFIX}/bin:${PATH}
cd $PRJROOT
(2)建立必要的文件夹:
cd ${PRJROOT}/build-tools
mkdir build-binutils build-boot-gcc build-glibc build-gcc build-glibc-headers
3. 安装binutils
cd ${PRJROOT}/build-tools
tar xvfz binutils-2.16.tar.gz
cd build-binutils
../binutils-2.16/configure --target=${TARGET} --prefix=${PREFIX}
make
make install
安装完毕会在如下目录生成如下工具:
[aaronwong@localhost bin]$ pwd
/home/aaronwong/Liod_XSBase/toolchain/bin
[aaronwong@localhost bin]$ ls
arm-linux-addr2line arm-linux-ld arm-linux-ranlib arm-linux-strip
arm-linux-ar arm-linux-nm arm-linux-readelf
arm-linux-as arm-linux-objcopy arm-linux-size
arm-linux-c++filt arm-linux-objdump arm-linux-strings
4. Bootstrap compiler gcc Setup
(1) kernel和库头文件
mkdir ${PRJROOT}/kernel
make ARCH=arm menuconfig
(选择正确的processor and system type,例中为PXA270)
System type--> ARM system type (PXA2xx-based)
--> Intel PXA2xx Implementations ---> Select target board (Intel HCDDBBVA0 Development Platform)
(configure后保存配置文件为mainstone_deconfig)
make mainstone_deconfig
make include/linux/version.h
这样就得到了内核相关的头文件。
注意,一定要check在源码目录下是否生成了正确的include/linux/version.h文件[1]。如果缺少该文件,则在后面编译glibc时会出现如下错误: checking installed Linux kernel header files... TOO OLD! configure: error: GNU libc requires kernel header files from Linux 2.0.10 or later to be installed before configuring. The kernel header files are found usually in /usr/include/asm and /usr/include/linux; make sure these directories use files from Linux 2.0.10 or later. This check uses <linux/version.h>, so make sure that file was built correctly when installing the kernel header files. To use kernel headers not from /usr/include/linux, use the configure option –with-headers. |
下面的操作将内核头文件拷贝到${TARGET_PREFIX}的相应位置。
mkdir ${TARGET_PREFIX}/include
cp -a include/linux ${TARGET_PREFIX}/include
cp -a include/asm-arm ${TARGET_PREFIX}/include/asm
cp -a include/asm-generic ${TARGET_PREFIX}/include(/asm-generic)
========================================================
接下来是为bootstrap compiler安装适当的glibc头文件[1][2]:
cd ${PRJROOT}/build-tools
tar xvfj glibc-2.3.6.tar.bz2
tar xvfj glibc-linuxthreads-2.3.6.tar.bz2 –directory=glibc-2.3.6
修改glibc相关文件:
vim glibc-2.3.6/Makeconfig
# - remove any occurrances of (contain) "-lgcc_eh" [3][4]
vim glibc-2.3.6/sysdeps/arm/dl-machine.h
# - change "static Elf32_Addr" to "auto inline Elf32_Addr" [4]
wget http://www.schnozzle.org/~coldwell/toolchain/ioperm.c.diff
patch -d glibc-2.3.6 -p1 < ioperm.c.diff
参考资料[3]中还建议作如下修正: vim config.make.in
# change "slibdir=@...@" to "slibdir=@libdir@" vim Makeconfig # change all occurrances of "O2" to "O" vim configure # change all occurrances of "O2" to "O" touch sysdeps/arm/framestate.c (本文的步骤中并未作上述修改)
|
说明: (1)如果不去掉Makeconfig中的"-lgcc_eh",会在编译glibc时发生如下错误: /home/aaronwong/Liod_XSBase/toolchain/bin/../lib/gcc/arm-linux/3.4.6/../../../../arm-linux/bin/ld: cannot find -lgcc_eh collect2: ld returned 1 exit status make[2]: *** [/home/aaronwong/Liod_XSBase/build-tools/build-glibc/iconv/iconvconfig] 错误 1 make[2]: Leaving directory `/home/aaronwong/Liod_XSBase/build-tools/glibc-2.3.6/iconv‘ make[1]: *** [iconv/others] 错误 2 make[1]: Leaving directory `/home/aaronwong/Liod_XSBase/build-tools/glibc-2.3.6‘ make: *** [all] 错误 2 (2)上面对ioperm.c文件打补丁,实际上是做了如下修改: vim sysdeps/unix/sysv/linux/arm/ioperm.c 如果不作这一修正,则在编译glibc时会发生如下错误: ../sysdeps/unix/sysv/linux/arm/ioperm.c: In function `init_iosys‘: ../sysdeps/unix/sysv/linux/arm/ioperm.c:103: error: `BUS_ISA‘ undeclared (first use in this function) ../sysdeps/unix/sysv/linux/arm/ioperm.c:103: error: (Each undeclared identifier is reported only once ../sysdeps/unix/sysv/linux/arm/ioperm.c:103: error: for each function it appears in.)
../sysdeps/unix/sysv/linux/arm/ioperm.c:103: error: initializer element is not constant ../sysdeps/unix/sysv/linux/arm/ioperm.c:103: error: (near initialization for `iobase_name[1]‘) ../sysdeps/unix/sysv/linux/arm/ioperm.c:104: error: initializer element is not constant ../sysdeps/unix/sysv/linux/arm/ioperm.c:104: error: (near initialization for `ioshift_name[1]‘) make[2]: *** [/home/aaronwong/Liod_XSBase/build-tools/build-glibc/misc/ioperm.o] 错误 1 make[2]: Leaving directory `/home/aaronwong/Liod_XSBase/build-tools/glibc-2.3.6/misc‘ make[1]: *** [misc/subdir_lib] 错误 2 make[1]: Leaving directory `/home/aaronwong/Liod_XSBase/build-tools/glibc-2.3.6‘ make: *** [all] 错误 2
|
然后进行configure和make以生成所需的头文件。
mkdir build-glibc-headers
cd build-glibc-headers/
../glibc-2.3.6/configure --host=${TARGET} --prefix="/usr" /
--enable-add-ons=linuxthreads –with-headers=${TARGET_PREFIX}/include
make cross-compiling=yes install_root=${TARGET_PREFIX} prefix="" /
install-headers
mkdir -p ${TARGET_PREFIX}/include/gnu
touch ${TARGET_PREFIX}/include/gnu/stubs.h [1]
touch ${TARGET_PREFIX}/include/bits/stdio_lim.h [2]
说明: configure时由于并没有将CC指向一个已有的cross-compiler,因此必须指定cross-compiling=yes选项以告诉并非要建立本地的库文件。头文件的安装目录由install_root指定为${TARGET_PREFIX}。 |
(2)Compile Bootstrap compiler gcc
tar xvfj gcc-3.4.6.tar.bz2
(上面是下载flow.c.diff,需要手动下载才能成功)
wget http://www.schnozzle.org/~coldwell/toolchain/t-linux.diff
patch -d gcc-3.4.6 -p1 < flow.c.diff
patch -d gcc-3.4.6 -p1 < t-linux.diff
cd build-boot-gcc/
../gcc-3.4.6/configure --target=${TARGET} --prefix=${PREFIX} --with-headers=${TARGET_PREFIX}/include --disable-shared –enable-languages=c
make all-gcc
make install-gcc
说明: (1)如果不修改flow.c,则得到的gcc在编译glibc时会发生如下错误: ../sysdeps/generic/s_fmax.c: In function `__fmax‘: ../sysdeps/generic/s_fmax.c:28: internal compiler error: in elim_reg_cond, at flow.c:3273 Please submit a full bug report, with preprocessed source if appropriate. See <URL:http://gcc.gnu.org/bugs.html> for instructions. make[2]: *** [/home/aaronwong/Liod_XSBase/build-tools/build-glibc/math/s_fmax.o] 错误 1 make[2]: Leaving directory `/home/aaronwong/Liod_XSBase/build-tools/glibc-2.3.6/math‘ make[1]: *** [math/others] 错误 2 make[1]: Leaving directory `/home/aaronwong/Liod_XSBase/build-tools/glibc-2.3.6‘ make: *** [all] 错误 2
(2)关于对t-linux文件的修正。上面的t-linux.diff并未给TARGET_LIBGCC_CFLAGS加上-D_gthr_posix.h和-Dinhibit_libc选项。
参考资料[5][6]指出,如果是第一次在主机上建立交叉编译工具,由于并没有相应的用于目标系统的libc头文件,因而必须进行“-Dinhibit_libc hack”,即: #Edit gcc/config/arm/t-linux #add -D_gthr_posix.h and -Dinhibit_libc to TARGET_LIBGCC_CFLAGS #configure with extra parameter --disable-threads. 如果这样做,则可以使用参考资料[3]的附录A提供的modified t-linux文件,并可如下configure: ../gcc-3.4.6/configure --target=${TARGET} --prefix=${PREFIX} --with-headers=${TARGET_PREFIX}/include --disable-shared –enable-languages=c –disable-threads (也可使用—without-headers和--with-newlib选项,用于指定编译时不要寻找glibc头文件[1],一说使用了--with-newlib则不必“-Dinhibit_libc hack”[7],这里使用—with-newlib选项并非指定newlib作为目标系统的C库,而仅是为了让bootstrap gcc能正确编译,后面还可为目标系统选择任一C库) ============================= 参考资料[1][2]则给出了建立libc头文件的办法,如前所述,这样就不必对t-linux进行“-Dinhibit_libc hack”了。本文使用了这种方法。 (3)参考资料[3]中还修改了gcc/config/arm/linux-elf.h,把定义LIBGCC_SPEC的行删除了。本文并未作此修正。 (4)参考资料[3]等文献还在configure时指定了—enable-multilib选项,实际上这是一个缺省选项(可查阅configure脚本)。
|
这时在${PREFIX}/bin(即toolchain/bin)下生成了:
-rwxr-xr-x 1 aaronwong aaronwong 200135 06-23 15:59 arm-linux-cpp
-rwxr-xr-x 2 aaronwong aaronwong 198948 06-23 15:59 arm-linux-gcc
-rwxr-xr-x 2 aaronwong aaronwong 198948 06-23 15:59 arm-linux-gcc-3.4.6
-rwxr-xr-x 1 aaronwong aaronwong 15803 06-23 15:59 arm-linux-gccbug
-rwxr-xr-x 1 aaronwong aaronwong 62688 06-23 15:59 arm-linux-gcov
另外,在${PREFIX_TARGET}/bin(即toolchain/arm-linux/bin)下得到:
-rwxr-xr-x 1 aaronwong aaronwong 198948 06-23 15:59 gcc
5. GNU C Library Setup
cd ${PRJROOT}/build-tools/build-glibc
CC=arm-linux-gcc AR=arm-linux-ar RANLIB=arm-linux-ranlib AS=arm-linux-as LD=arm-linux-ld ../glibc-2.3.6/configure --host=${TARGET} --prefix="/usr" --with-headers=${TARGET_PREFIX}/include --enable-add-ons=linuxthreads --enable-shared --wihout-fp --build=i686-pc-linux-gnu
说明: (1)如果目标处理器没有FPU(浮点运算单元),那么在编译glibc时可以加上—without-fp选项,以在C库中内建FPU仿真功能[1]。 (2)—build选项并不是必须的,它指定进行编译glibc操作的主机类型,但如果不指定该选项,则可能在configure时出现如下错误信息: checking size of long double... configure: error: cannot compute sizeof (long double), 77 See `config.log‘ for more details. 这时必须要加入选项—build才能通过configure。可以查看config.log文件得知--build选项的值,其值因主机而异。详细可参考: http://sources.redhat.com/ml/crossgcc/2004-05/msg00003.html (2)—prefix="/usr"选项是为了让configure脚本指定库文件在目标系统上根文件系统中的存放位置。这样,才能保证调用该库的程序在目标平台上的相应位置找到对应的库文件。设置--prefix为/usr的结果是使得dynamic linker将在目标系统的/lib目录下搜寻共享库。当然,为了避免把glibc库安装到host的/usr目录,在make install时会另外修正安装目录[1]。 (3)可以加上--enable-kernel=VERSION 这一选项,来指定将glibc编译成与版本号大于或等于VERSION的内核相兼容。 (4)由于之前为编译bootstrap gcc在产生必要的C库头文件时,已经将glibc以及glibc-threads源代码解压到相应目录下并对相关文件进行了patch,所以这里直接进入build-glibc目录进行configure。
如果你在编译bootstrap gcc时使用的是“-Dinhibit_libc hack”的办法,即之前没有对glibc进行patch,则在进行configure之前,需要和前面产生C库头文件时一样修改glibc的相关文件。否则在make时会发生错误。 (5)关于glibc的endian的问题(感谢酷!學園討論區的朋友们)。 big-endian和little-endian是指CPU在内存中对字节的存取顺序。大多数处理器只支持一种endian模式,例如PXA270中的xscale核心只支持little-endian,这时所有的程序(包括操作系统,库文件)都必须编译成相应的处理器所支持的endian模式(对于PXA270就是little-endian),才能在处理器上正常运行。当然也有大小端模式都支持的处理器如IXP4xx系列(也是xscale核心)。 如果要编译big-endian格式的库,可以给用于编译glibc的工具添加相应选项,如下[3]: CC="arm-linux-gcc -mbig-endian" / AS="arm-linux-as -mbig-endian" / LD="arm-linux-ld -EB" 缺省情况下是编译little-endian格式的库。
|
下面是编译和安装。
make
make install_root=${TARGET_PREFIX} prefix="" install
(这里修正了glibc库文件的安装路径,将glibc库安装到${TARGET_PREFIX}/lib目录下)
最后,还需要修正libc.so脚本中库的链接路径:
cd ${TARGET_PREFIX}/lib
cp libc.so libc.so.orig #备份
vim libc.so
=============================
/* GNU ld script
Use the shared library, but some functions are only in
the static library, so try that secondarily. */
OUTPUT_FORMAT(elf32-littlearm)
GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a )
==============================
上面是原始文件。
去掉“/lib/”,修改后如下:
=============================
/* GNU ld script
Use the shared library, but some functions are only in
the static library, so try that secondarily. */
OUTPUT_FORMAT(elf32-littlearm)
GROUP ( libc.so.6 libc_nonshared.a )
=============================
说明[1]: lib.so文件实际上是一个链接脚本,用于链接应用程序和C库。之前的make install命令认为库文件被安装在一个根文件系统上,因此在libc.so中使用了绝对路径来引用这些库。 而实际上,我们的目标系统所要使用的C库文件和lib.so库文件都装在了host上的${TARGET_PREFIX}/lib目录下,因此,为了目标系统上运行的程序能找到对应的动态库,必须修正库的链接路径。 上面的修改实际上使用了相对路径进行链接,这样,只要lib.so文件与实际的库文件在同一目录下(相对路径保持不变),就是有效的链接。当然,也可以根据目标系统根文件系统的组织使用绝对路径进行链接。 |
6. Full Compiler Setup
下面为目标系统编译支持C和C++的交叉编译工具。
cd ${PRJROOT}/build-tools/build-gcc
../gcc-2.3.6/configure –target=$TARGET –prefix=${PREFIX} –enable-languages=c,c++
说明[1]: 如果环境变量TARGET_PREFIX的值不是${PREFIX}/${TARGET},那么必须使用--with-headers和--with-libs选项告诉configuration脚本glibc的头文件和库文件的安装路径。 |
make all
make install
7. Toolchain定格[1]
-
${PRJROOT}/toolchain目录的一些内容
bin
交叉编译工具(如arm-linux-gcc, arm-linux-ld等)
arm-linux
目标相关的文件
include
交叉编译工具的头文件
info
gcc info files
lib
交叉编译工具的库文件
man
交叉编译工具的manuals
share
交叉编译工具和库的共享文件
[说明] 其中最重要的两个目录是bin和arm-linux,前者包含了所有的交叉编译工具,后者则包含要用于目标系统的软件组件(目标系统中要用到 的头文件和库)。
-
${PRJROOT}/toolchain/arm-linux目录的一些内容
bin
glibc相关的目标二进制文件和脚本
etc
要放置到目标系统/etc目录的文件,这里只有rpc文件
include
为目标系统编译应用程序时所需的头文件
info
glibc info files
lib
目标系统的/lib目录
libexec
Binary helpers
sbin
目标系统的/sbin目录
share
一些子目录和文件的共享文件
sys-include
glibc还没有把主要的目标头文件安装到include目录时,gcc配置脚本用来拷贝目标头文件的一个目录
[说明] 其中最重要的两个目录是include和lib,前者包含了为目标系统编译应用程序时所需的头文件,后者则包含目标系统的运行时库。
事实上,在${PRJROOT}/toolchain/arm-linux/bin目录下,还有一些是host utilities的拷贝,为了把它们与编译glibc时所得到的目标二进制文件区分开来,参考资料[1]推荐把它们转移到另一个目录下。它们是as, ar, gcc, ld, nm, ranlib和strip。可使用file命令来检查:
file as ar gcc ld nm ranlib strip
as: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for ...
ar: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for ...
gcc: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for ...
......
(这一步是可选的操作,这里我们跳过,详细情况可见参考资料[1])
最后,用rm -rf命令删除建立toolchain过程中的临时文件夹(${PRJROOT}/build下的build-*文件夹)。
8. Toolchain测试
这里只作了最简单和最基本的测试,看我们建立的cross-compiling toolchain是否能将最简单的helloworld程序成功编译为目标系统的格式。
===========================================
[aronwong@localhost testprj]$ arm-linux-gcc -o hello hello.c
[aronwong@localhost testprj]$ ls
hello hello.c
[aaronwong@localhost testprj]$ file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.0.0, dynamically linked (uses shared libs), for GNU/Linux 2.0.0, not stripped
===========================================
上述hello的文件信息显示为“GNU/Linux 2.0.0”,可能是因为在编译 glibc前configure时没有指定—enable-kernel选项(仅为推测,有待证实):
--enable-kernel=VERSION : compile for compatibility with kernel not older than VERSION
9. 后记
(1) 本文介绍了建立arm-linux交叉编译工具的基本方法和一般步骤。
(2) 本文使用arm-linux作为TARGET的名字,而非arm-elf。arm-linux工具编译所得的结果为Linux/ARM(即标准ARMLinux)所支持的ELF格式的映像文件。而arm-elf工具则编译得到flat(平坦模式)的二进制映像,支持Cygnus‘ ELF格式,是操作系统无关的交叉编译工具[5]。
(3) 本文并没有达到真正意义上的为某个处理器核心例如XSCALE量身定制toolchain的目的。事实上,要真正为某个处理器核心量身定制一个toolchain,首先要
了解该处理器核心的特性(例如有无MMU,big-endian或little-endian支持,有无FPU以及有无特殊指令集支持等),另外还要参考gcc, glibc等相关文档(http://www.gnu.org/),以在建立工具时进行最佳配置。
(4) 对于所建立的交叉编译工具链的测试,更严格有效的测试是对目标系统的内核进行编译,并在构建好目标系统后,能编译出在目标平台上正常运行的应用程序。
更多推荐
所有评论(0)