Chinaunix首页 | 论坛 | 博客
  • 博客访问: 840162
  • 博文数量: 85
  • 博客积分: 10016
  • 博客等级: 上将
  • 技术积分: 952
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-19 22:52
文章分类

全部博文(85)

文章存档

2011年(1)

2008年(1)

2007年(60)

2006年(23)

我的朋友

分类: LINUX

2007-06-28 21:36:12

Building arm-linux toolchain for ARM/XSCALE


[作者说明]
本文介绍了手工建立toolchain的方法和过程。随着时间的推移和技术的发展,本文所介绍的方法已体现出如下局限性和落后性:
(1)使用的软件包版本较老;
(2)使用linuxthreads而非新的NPTL线程模型;
(3)所建立的toolchain遵循旧的ABI标准,而非新的ARM EABI标准;
(4)目标系统根文件系统采用传统的布局方式,即所有的库文件都放置在/lib目录下;
(5)并没有实现真正意义上为某一处理器(例如PXA27x系列的iWMMX技术)进行量身定制toolchain的目标。
此外,手工建立toolchain的过程非常繁琐,虽然可以对建立的过程进行全程控制,但稍有疏忽就会导致失败,因此是非常耗时耗力的。
鉴于此,作者撰写了Building arm-linux toolchain for ARM/XScale-iWMMXt(new ABI)一文,介绍了使用crosstool工具自动建立toolchain的方法,并根据pxa27x处理器的iWMMX技术进行了定制,新的toolchain符合ARM ABI v2。

下面介绍了建立arm-linux交叉编译工具的具体步骤,主要参考了《Building Embedded Linux System》一书,以及其他的网络资源。

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内核源代码是为了编译gccglibc时提供相关的头文件,可以选择其他版本的内核,但应注意内核版本与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 , 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 "-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

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
# - Change "BUS_ISA" to be "CTL_BUS_ISA" lines 103 and 104

如果不作这一修正,则在编译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


然后进行configuremake以生成所需的头文件。

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

wget

(上面是下载flow.c.diff,需要手动下载才能成功)

wget

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

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选项的值,其值因主机而异。详细可参考:

(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)关于glibcendian的问题(感谢的朋友们)

big-endianlittle-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

下面为目标系统编译支持CC++的交叉编译工具。

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

交叉编译工具和库的共享文件

[说明] 其中最重要的两个目录是binarm-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配置脚本用来拷贝目标头文件的一个目录

[说明] 其中最重要的两个目录是includelib,前者包含了为目标系统编译应用程序时所需的头文件,后者则包含目标系统的运行时库。


事实上,在${PRJROOT}/toolchain/arm-linux/bin目录下,还有一些是host utilities的拷贝,为了把它们与编译glibc时所得到的目标二进制文件区分开来,参考资料[1]推荐把它们转移到另一个目录下。它们是as, ar, gcc, ld, nm, ranlibstrip。可使用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,可能是因为在编译 glibcconfigure时没有指定—enable-kernel选项(仅为推测,有待证实)

--enable-kernel=VERSION compile for compatibility with kernel not older than VERSION






9. 后记

(1) 本文介绍了建立arm-linux交叉编译工具的基本方法和一般步骤。

(2) 本文使用arm-linux作为TARGET的名字,而非arm-elfarm-linux工具编译所得的结果为Linux/ARM(即标准ARMLinux)所支持的ELF格式的映像文件。而arm-elf工具则编译得到flat(平坦模式)的二进制映像,支持Cygnus' ELF格式,是操作系统无关的交叉编译工具5

(3) 本文并没有达到真正意义上的为某个处理器核心例如XSCALE量身定制toolchain的目的。事实上,要真正为某个处理器核心量身定制一个toolchain,首先要

了解该处理器核心的特性(例如有无MMUbig-endianlittle-endian支持,有无FPU以及有无特殊指令集支持等),另外还要参考gcc, glibc等相关文档(),以在建立工具时进行最佳配置。

(4) 对于所建立的交叉编译工具链的测试,更严格有效的测试是对目标系统的内核进行编译,并在构建好目标系统后,能编译出在目标平台上正常运行的应用程序。


=======================================

参考资料:

[1]Karim Yaghmour, O'Reilly, April 2003

[2] Charles M. “Chip”Coldwell

[3]

[4]

[5]

[6]Klaus Reimer

[7]

[8]


其他推荐的建立交叉编译工具的文章:

[1]

[2]compile cross-compiler gcc-3.4.3 wth glibc-2.3.4 for ARM(920, v4) platform mini howto

[3]定制ToolChain for ARM, 王卫无

[4]

[5]

[6]如何为嵌入式开发建立交叉编译环境,梁元恩

[7]

[8]交叉编译场景分析


有用的网址:

[1]

[2]

[3]

[4]

[5]

=======================================

[注]纰漏之处,恳请指正。


[本文pdf文档点此下载]
[TrackBack]
http://blog.chinaunix.net/u/26710/showart_330554.html
[声明]转载请注明作者及出处。









阅读(10419) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~