免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1193 | 回复: 0
打印 上一主题 下一主题

DIY自己的GNU交叉工具链(i386-arm) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-09-09 00:50 |只看该作者 |倒序浏览

               
               
               
               
               
               
               
               
               
               
               
               
               
               
               
               
               
               
                嵌入式设备由于不具备一定的处理器能力和存储空间,程序开发一般用PC来完成,然后将可执行文件下载到嵌入式系统中运行。这是目前嵌入式程序开发的不二选
择——Host/target模式。但这引发了一个问题:由于Host和Target的处理器体系结构不同,我们不能直接用PC上既有的程序开发工具,必
须使用跨平台开发工具,即在Host上生成能在Target上运行格式的目标文件。
与在PC上进行程序开发类似,嵌入式系统开发也需要编译器、链接器、解释程序等。本文讨论GNU跨平台开发工具链的建立,包括: ld, gas, ar, gcc, glibc.
自己建立交叉编译环境是一件很头疼的事(处理版本的依赖性, 漫长的编译过程...),如果你不想经历这样的痛苦,可以选择网上编译好了的工具链进行安装.如果你用的是Debian/Ubuntu的发行版, 推荐使用
Emdebian
. 如果使用uClinux, 也可安装arm-elf-tools.
关于Emdebian和arm-elf-tools的更多介绍, 情看本blog的
这篇文章!
前提: 建立工作目录



在为目标板开发及定制软件的过程中, 最好将项目要用到的代码, 文档, 及生成的目标文件按一定的组织放在统一的, 结构化的目录中. 这里采用《构建嵌入式Linux系统》中推荐的项目目录安排:
目录
内容
bootldr
目标板的引导加载程序
build-tools
建立跨平台开发工具链所需要用到的包和目录
debug
调试工具及相关包
doc
项目相关的文档
images
使用在目标板上的引导加载程序和内核的二进制映像, 以及根文件系统
kernel
将在目标板上进行评估的各个内核版本
rootfs
目标板的内核在运行时看到的根文件系统
sysapps
目标板需要用到的系统应用程序
tmp
进行实验时或临时文件会用到此目录
tools
放置跨平台开发工具链以及C链接库
上表列出的是一个完备的目录结构, 使用于一个实际的项目. 本文关注的是建立跨平台工具链,  它使用下面的目录:
build-tools, tools, kernel

其中, 在build-tools建立下列目录:
$ mkdir build-binutils build-boot-gcc build-gcc build-glibc gcc-patch
设置环境变量
前面建立的项目空间目录众多, 我们可以设置一些环境变量以方便后面的工作. 你可以在命令行中设定这些环境变量. 当然,
更简单的方法是建立一个名为RunFirst.sh的bash脚本. 将该脚本放在项目根目录中, 每次登录时运行它既可进入你的工作环境: $
./RunFirst.sh
#!/bin/bash
      
export PRJROOT=/home/zp/project/EmbeddedLinux
export TARGET=arm-linux
export PREFIX=${PRJROOT}/tools
export TARGET_PREFIX=${PREFIX}/${TARGET}
export PATH=${PREFIX}/bin:${PATH}
cd PRJROOT
PRJROOT  :指定了项目的根目录.
TARGET  :指定了目标般类型, 如果要更换目标板类型(比如换成PowerPC), 那么需要更改TARGET, 并重新编译工具链.
PREFIX  :为后面用到的建立工具链的命令提供了一个"指针", 它指向目标板工具程序将被安装的目录.
TARGET_PREFIX  :指定与目标板相关的头文件和链接库将被安装的位置.
PATH  :指定可执行文件的位置. 当交叉编译工具被建立后, 它告诉Bash首先在我们自己建立的工具链目录中寻找可执行文件.
执行最后一条命令就会移动到项目的根目录.
注意运行脚本的方式: 一般是有两种方法source file.sh或./file.sh. 如果直接调用脚本名,
当前shell会生成一个子shell去执行脚本, 子shell在脚本运行完后退出. 不会对当前shell产生影响.
也就是说当前设置的环境变量只对子shell起作用. 所以不用这种方法运行. 而使用source.
source命令使得脚本在当前shell的上下文中被执行.
本工具链的各组件的版本为:
gcc : 2.95.3
binutils : 2.10.1
glibc : 2.2.3 + libc-linuxthreads-2.2.3
kernel : 2.4.21 + patch-2.4.21-rmk2
建立工具链的步骤:
  • 设置内核头文件
  • 设置binutils
  • 设置引导编译器boot-gcc
  • 设置C链接库glibc
  • 设置完整的编译起gcc
    尽管将设置内核头文件放在第一步, 实际上要等到设置glibc时才会用到头文件. 只需保证设置内核头文件在设置glibc之前即可. 如果考虑到设置工作目录, 将设置内核头文件排在第一步比较合适.
    (一)设置内核头文件
    首先进入kernel目录,下载
    linux-2.4.21
    内核源码,以及
    patch-2.4.21-rmk2
    patch-2.4.21-rmk2是针对ARM的补丁, 关于它的介绍可以参考
    这里


    这里使用的2.4.21版的内核, 如果使用2.4.18版之前(含2.4.18)的内核, 解压之后建立的目录名为linux, 不带版本号.
    如果使用多个版本的内核, 最好把它linux目录改名,添加版本号. 否则如果解压另一版本(低于2.4.19)的内核, 会覆盖linux目录.
    2.4.19版之后的目录名会加上版本号.

    解压内核, 并打补丁:

    $ cd linux-2.4.21
    $ patch -p1
    在内核源码数根目录配置内核:
    $ make  ARCH=arm  CROSS_COMPILE=arm-linux-  menuconfig
      ARCH=arm指定目标系统的体系结构,这里是针对ARM,如果你不指定  ARCH=arm,那么会针对你的本地系统(X86)配置内核.
    由于你没有建立交叉编译器, 你无法编译内核. 在这里只是对内核进行基本的配置: 在这里只需选定
           目标系统的处理器类型.
    推荐用menuconfig进行配置, 关于配置内核的工具, 参考本blog
    这篇文章
    的前半部分.
    配置结束, 退出menuconfig时, 它提示你运行 make dep, 其实不需要运行它. 因为此处只是简单的配置, 不编译内核.
    配置完退出并保存, 检查一下的内核目录中的 include/linux/version.h 和 include/linux/autoconf.h
    文件是不是生成了, 这是编译 glibc 是要用到的, version.h 和 autoconf.h 文件的存在,也说明了你生成了正确的头文件.
    设置内核头文件
    此后编译完整的gcc过程中需要用到内核头文件. 编译过程中系统默认在sys-linux目录中搜索头文件.
    我们可以在配置gcc编译时增加 --with-headers选项来设定包含头文件目录, 但这会使得头文件被拷贝到sys-linux目录中.
    比较好的方法是创建符号连接以避免拷贝冗余文件:
    1, 将内核源码术的asm-arm目录和linux目录拷贝到$TARGET_PREFIX/include目录:
    $ cd $PRJROOT/kernel/linux-2.4.21
    $ cp -dR include/asm-arm $TARGET_PREFIX/include/asm
    $ cp -dR include/linux $TARGET_PREFIX/include/linux
    2, 在内核源码数建立下列符号连接:
    $ cd include
    $ ln -s asm-arm asm
    $ cd asm
    $ ln -s arch-epxa arch
    $ ln -s proc-armv proc
    3, 在$TARGET_PREFIX/目录中建立下面的符号连接:
    $ cd $TARGET_PREFIX
    $ ln -s include sys-linux
    4,在$TARGET_PREFIX/目录中建立下面的符号连接:
    $ cd $TARGET_PREFIX/include/asm
    $ ln -s arch-epxa arch
    $ ln -s proc-armv proc

    (二)设置binutils


    此步骤不需要设置内核头文件!
    进入build-tools目录, 下载
    binutils-2.10.1


    (1)进入build-binutils目录配置和编译binutils
    $../binutils-2.10.1/configure --target=$TARGET --prefix=$PREFIX
    --target 选项是指出我们生成的是 arm-linux 的工具,--prefix 是指出我们可执行文件安装的位置。
    注意: 配置binutils需要用到flex, 若没有安装flex, 配置过程中会提示:
    ../../binutils-2.10.1/binutils/configure: line 1945: flex: command not found
    checking for flex... lex
    checking for yywrap in -ll... no
    checking lex output file root... ../../binutils-2.10.1/binutils/configure: line 2033: lex: command not found
    configure: error: cannot find output from lex; giving up
    需要安装flex, 在Debian/Ubuntu中,直接运行:
    $ sudo apt-get install flex
    下面是从源代码安装的方法:
    ------------------------------------------------------------------------------
    先安装lex,到GNU网站:http://mirror.vmmatrix.net/GNU-ftp/non-gnu/下载
    flex
    的源码安装。
    在编译flex的过程中,make又提示错误:
    make: yacc: Command not found
    make: *** [parse.c] Error 127
    需要yacc,于是下载Berkeley的
    byacc
    源码。
    so,安装binutils时,若需要flex, yacc,执行下列步骤
    1,获得yacc, flex源码
    2,安装yacc: ./configure --> make --> make install
    3,安装flex: ./confugre --> make --> make install
        完成后,再继续安装binutils:
    1,./configure --target=..., --prefix=...
    2,make
    3,make install
    ------------------------------------------------------------------------------
    (2) 前面的配置产生Makefile之后, 只需运行:
    $ make
    $ make install
    OK, 前面已经安装好了binutils, 下面我们看看安装了哪些程序:
    $ ls $PREFIX/bin
    arm-linux-addr2line
    arm-linux-c++filt
    arm-linux-nm      
    arm-linux-ranlib   arm-linux-strings
    arm-linux-ar        
    arm-linux-gasp     arm-linux-objcopy
    arm-linux-readelf  arm-linux-strip
    arm-linux-as        
    arm-linux-ld      
    arm-linux-objdump  arm-linux-size
    关于上面的命令, 可以参看本人blog的
    GNU binutils笔记
    (三)设置初始编译器(boot-gcc)

    此步骤不需要设置内核头文件!
    进入build-tools目录, 下载
    gcc-2.95.3,
    并到
    LinuxFromScratch
    网站下载这三个补丁:
    gcc-2.95.3-2.patch
    gcc-2.95.3-no_fixinc-1.patch
       
    gcc-2.95.3-returntype_fix-1.patch

    解压GCC,并打补丁:
    $cd gcc-2.95.3$patch -p1$patch -p1$patch -p1
    echo timestamp > gcc/cstamp-h.in
    如果你用的是Ubuntu默认的gcc(既安装build-essential中的gcc, 该gcc的版本目前是4.0.2). 在编译时候会提示"invalid lvalue in increment"错误. 最好使用gcc-3.3.
    Ubuntu中安装gcc-3.3: $ sudo apt-get install gcc-3.3
          只有调用不同版本的gcc, 只需: $ export CC=gcc-3.3
    对t-linux文件作一些修改:
    -----------------------------
    修改$PRJROOT/gcc-2.95.3/gcc/config/arm/t-linux,把
    TARGET_LIBGCC2-CFLAGS = -fomit-frame-pointer -fPIC
    这一行改为
    TARGET_LIBGCC2-CFLAGS = -fomit-frame-pointer -fPIC -Dinhibit_libc -D__gthr_posix_h
    注意是arm目录下的!在/gcc/config目录下也有一个t-linux文件,别弄混淆了。
    -----------------------------
    上面的修改方案来自IBM developerworks上的“如何为嵌入式开发建立交叉编译环境”. 但我在编译过程中老是碰见"libgcc1.S:438: asm/unistd.h: No such file or directory"这样的错误. 原先以为是本地GCC的问题, 用gcc-2.95.3, gcc-3.3等编译, 还是如此. 又以为需要在这里设置内核头文件, 拷贝, 建立符号连接... 什么方法都试过, 还是不行. 我KAO...
    实际上, 应该这样修改t-linux:
    在TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC语句之前添加"T_CFLAGS = -Dinhibit_libc -D__gthr_posix_h"
    成为:
          T_CFLAGS = -Dinhibit_libc -D__gthr_posix_h
    TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC
    -Dinhibit_libc等同于在命令中加上"--with-newlib", 它告诉配置工具不要使用glibc,
    因为当前的glibc并不是针对目标板的. 从字面上看, 它是告诉配置工具"使用一个新的c库来作为目标板的C链接库.
    然而当前我们并没有可用的C链接库, 所以这个选项只是让GCC能够正确编译, 生成一个初始编译器而已. 随后可以选择C链接库.
    -D__gthr_posix_h我不知道是什么意思, 反正如果不更改上面的语句, 就会有如下的错误:
    ../../gcc-2.95.3/gcc/gthr-posix.h:37: pthread.h: No such file or directory
    可能是去掉posix thread支持吧.
    配置
    $ cd $PRJROOT/build-tools/build-boot-gcc/
    $ ../gcc-2.95.3/configure --target=$TARGET --prefix=$PREFIX --without-headers --enable-language=c --disable-threads
    --target, --prefix 和配置 binutils 的含义是相同的. --without-headers
    就是指不需要头文件, 因为是交叉编译工具, 不需要本机上的头文件. --with-newlib在前面已经介绍了,
    即便更改了t-linux文件, 也可以在这里设置--with-newlib. -enable-languages=c是指我们的
    boot-gcc 只支持 c
    语言. --disable-threads 是去掉 thread 功能, 这个功能需要 glibc 的支持.
    如果用的比较老版本gcc (比如gcc-2.95.3) 在配置过程中会提示这样的错误:
    Config.guess failed to determine the host type.  You need to specify one.
    这是由于config.guess, config.sub版本太老. config.guess是用来检测host类型的, 运行它就能得知host类型.
    解决方法: 到ftp://ftp.gnu.org/pub/pub/gnu/config/下载新版的config.guess和config.sub:
           http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
    http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
    用它们替换gcc-2.95.3目录下的老文件.
    现在运行 $./config.guess:
    i686-pc-linux-gnu
    编译, 安装
    $ make all-gcc
    $ make install-gcc
    注意:为什么不用 all-in-one 的 gcc-$VGCC.tar.gz 呢?
          
    all-in-one 的 gcc 包里面有 chill, fortran, java 等语言的编译器,虽然在下面 configure
    时指定 -enable-languages=c,但编译时还是把所有的都编译一便,这不是我们需要的,而且它也总会有错误。因此我们只编译 C
    语言的编译器。后面第二次编译的时候也是这个问题,我们只编译 C 和 C++ 的编译器。
    (四) 设置C链接库glibc
    进行到这一步的时候, 需要保证正确地设置了内核头文件, 因为glibc要用到内核头文件!
    进入build-tools目录, 下载
    glibc-2.2.3
    以及
    glibc-linuxthreads-2.2.3.tar.gz
    解压glibc, 并打补丁
    $ cd $PRJROOT/build-tools
    $ tar -xvzf glibc-2.2.3.tar.gz
    $ tar -xzvf glibc-linuxthreads-2.2.3.tar.gz --directory=glibc-2.2.3
    配置glibc
    $ cd build-glibc
    $ CC=arm-linux-gcc ../glibc-2.2.3/configure --host=$TARGET  --prefix="/usr"--enable-add-ons --with-headers=$TARGET_PREFIX/include
    CC=arm-linux-gcc 把 CC 变量设成你刚编译完的boostrap
    gcc, 用它来编译你的glibc.
    --host=$TARGET 告诉该链接库在目标系统上执行, 而非在本地主机.
    --prefix="/usr" 告诉配置脚本在目标板的根文件系统中glibc的位置.
    --enable-add-ons 告诉配置脚本使用我们下载的附加包. 已经将glibc-linuxthreads-2.2.3放入了
    glibc 源码目录中. 由于我们只添加了一个附加包, 这里--enable-add-ons等价于
    --enable-add-ons=linuxthreads. (如果使用glibc-2.1.x, 需要使用glibc-crypt附加包,
    就得使用: --enable-add-ons=linuxthreads, crypt选项).
    --with-headers 告诉
    glibc 我们的linux 内核头文件的目录位置.
    编译并安装glibc
    $ make
    $ make install_root=$TARGET_PREFIX prefix="" installinstall_root 指定了安装链接库组件的目录, 将glibc安装到与我们项目相关的目录, 而非/usr目录.
    如果不指定prefix="", 那么glibc会被安装到$TARGET_PREFIX/usr/lib目录中. 指定prefix使glibc被安装到 $TARGET_PREFIX/lib目录.
    修改$TARGET_PREFIX/lib目录中的libc.so
    $ cd $TARGET_PREFIX/lib
    $ cat libc.so
    libc.so的内容:
    /* GNU ld script
       Use the shared library, but some functions are only in
       the static library, so try that secondarily.  */
    GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a )
    将/lib/绝对目录去掉, 既将"GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a )"改为:
    GROUP ( libc.so.6 libc_nonshared.a )
    由于嵌入式系统存储功能的限制, 使用glibc生成的可执行文件比较大, 可以使用uClibc. 文末介绍了使用uClibc.
    (五) 建立完整的GCC
    前面建立的boot-gcc只支持c, 而且不能使用glibc运行时库, 现在我们已经可以建立完整的gcc, 支持c, c++, 运行时库.
    配置GCC
    $ cd $PRJROOT/build-tools/build-gcc
    $ ../gcc-2.95.3/configure --target=$TARGET --prefix=$PREFIX --enable-languages=c,c++
    编译并安装完整的GCC
    $ make all
    $ make install
    这里安装的gcc将覆盖原来的gcc.
    (六) 安装uClibc

    这里
    下载uClibc库. 当前最新版是
    0.9.28
    . 下载uClibc包, 保存到build-tools目录. 使用uClibc可以不考虑gnu工具版本依赖问题, 找最新的uClibc用.
    将它解压, 进入uClibc-0.2.98目录, 对uClibc进行配置. 注意: uClibc不支持在自身目录之外操作. 所以这里的配置, 安装不要新建安装目录, 就在uClibc本身的目录中进行.
    配置uClibc
    $ cd $PRJROOT/build-tools/uClibc-0.2.98
    $ make CROSS=arm-linux- menuconfig
    使用menuconfig工具进行配置, 这和linux内核配置很相. 实际上uClibc的配置系统来自Roman Zippel的内核配置系统.
    CROSS设置为"arm-linux-".
    进入熟悉的menuconfig界面后, 需要进行一些设置. 我有一块ARM9的开发板, 所以这里的配置都针对ARM920T. 这里只列出了部分设置. 具体可参考menuconfig中的帮助信息.
    Target Architecture (arm)
    Target Architecture Features and Options  --->
    Target Processor Type (Arm 920T)
    Target Processor Endianness (Little Endian)
  • Target CPU has a memory management unit (MMU)
  • Enable floating point number support
  • Target CPU has a floating point unit (FPU)
  •    Enable full C99 math library support
    ($(PRJROOT)/kernel/linux-2.4.21) Linux kernel header location
    这里的内核头文件位置设置为先前包含内核头文件的路径.
    General Library Settings  --->
  • Generate Position Independent Code (PIC)
    要想使用动态库, 必须选上PIC.
  • Enable support for shared libraries
    如果你想将所有的应用程序静态连接, 那么你可以关闭动态库支持. 但如果以后要用动态库时, 又得重新编译uClibc了(glibc也是如此). 最好选上对动态库的支持. 你可以在gcc选项中加上-static来使用静态库.

    Library Installation Options  --->
    (/lib) Shared library loader path
    指定共享库的目录: 一般为/lib. 这样uClibc安装后将把共享库安装到/lib目录.
    (/) uClibc runtime library directory
    ($(PRJROOT)/tools/uclibc) uClibc development environment directory
    ...
    如同用menuconfig配置内核, 所有的配置都位于.config文件中. 将它删除就能恢复到缺省配置.
    编译, 安装uClibc
    $ make CROSS=arm-linux-
    $ make CROSS=arm-linux- PREFIX="" install
    所有的uClibc组件将被安装到$PRJROOT/tools/uclibc目录.
    运行 $ ls -l /lib/*uC*看看:
    -rwxr-xr-x  1 root root  21504 2006-05-28 21:28 ld-uClibc-0.9.28.so
    lrwxrwxrwx  1 root root     19 2006-05-28 21:28 ld-uClibc.so.0 -> ld-uClibc-0.9.28.so
    -rw-r--r--  1 root root 228420 2006-05-28 21:28 libuClibc-0.9.28.so
    (七)最后的检查工作及总结
    到目前为止, 工具链已经可以使用了. 下面的内容是为了熟悉工具链以及项目目录.
    $ ls $PRJROOT/tools
    有这些目录: arm-linux  bin  include  info  lib  man  share
    arm-linux : 目标板专用文件.
    bin          :   交叉开发工具
    include   :    供交叉开发工具使用的头文件
    info         :   gcc的info文件
    lib           :    供交叉开发工具使用的链接库
    man        :   交叉开发工具的在线说明手册
    share      :  交叉开发工具与链接库共享的文件. 目前为空.
    这里面最重要的就是bin和arm-linux目录
    bin目录包含了交叉开发工具链所有的工具程序, 我们将会在主机上使用它们来为目标板开发正许.
    arm-linux包含了用在目标板上的所有组件. 主要是目标板的头文件和运行时链接库. arm-linux包含下列目录:
    ...
    参考资料
    (1)《构建嵌入式Linux系统,o'reilly.
    (2) IBM developerworks上的这篇
    如何为嵌入式开发建立交叉编译环境
    (3)
    http://www.ailis.de/~k/docs/crosscompiling/
    "ARM cross-compiling howto"
    (4)
    The GNU Toolchain for ARM targets HOWTO
    (5)
    http://venus.billgatliff.com/node/18
                   
                   
                   

    本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/40912/showart_377403.html
  • 您需要登录后才可以回帖 登录 | 注册

    本版积分规则 发表回复

      

    北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年举报专区
    中国互联网协会会员  联系我们:huangweiwei@itpub.net
    感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP