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
然后进行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
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.hand -Dinhibit_libcto
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)关于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交叉编译工具的头文件
infogcc
info files
lib交叉编译工具的库文件
man交叉编译工具的manuals
share交叉编译工具和库的共享文件
[说明]其中最重要的两个目录是bin和arm-linux,前者包含了所有的交叉编译工具,后者则包含要用于目标系统的软件组件(目标系统中要用到
的头文件和库)。
${PRJROOT}/toolchain/arm-linux目录的一些内容
binglibc相关的目标二进制文件和脚本
etc要放置到目标系统/etc目录的文件,这里只有rpc文件
include为目标系统编译应用程序时所需的头文件
infoglibc
info files
lib目标系统的/lib目录
libexecBinary
helpers
sbin目标系统的/sbin目录
share一些子目录和文件的共享文件
sys-includeglibc还没有把主要的目标头文件安装到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等相关文档(),以在建立工具时进行最佳配置。
(4)对于所建立的交叉编译工具链的测试,更严格有效的测试是对目标系统的内核进行编译,并在构建好目标系统后,能编译出在目标平台上正常运行的应用程序。
=======================================
参考资料:
[1]Karim Yaghmour, O'Reilly, April
[2] Charles M.
“Chip”Coldwell
[3]
[4]
[5]
[6]Klaus
Reimer
[7]
[8]
其他推荐的建立交叉编译工具的文章:
[1]
[4]
[5]
[7]
有用的网址:
[1]
[2]
[3]
[4]
[5]
=======================================
[注]纰漏之处,恳请指正。
]
[TrackBack]/u/26710/showart_330554.html
[声明]转载请注明作者及出处。