New

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            21 Posts :: 0 Stories :: 4 Comments :: 0 Trackbacks

          常用鏈接

          留言簿(1)

          我參與的團(tuán)隊(duì)

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          最簡單的方法就是下載一個(gè)buildtoolchain.tar.gz安裝就行了,有時(shí)候就是想自己親身體驗(yàn)一下安裝定制過程,Linux的樂趣也在這點(diǎn)。從網(wǎng)上搜集了些資料,以備日后查閱。    
          如何為嵌入式開發(fā)建立交叉編譯環(huán)境


           


            
           
          < language=java type=text/java script>      
           
           


           
          梁元恩 , 軟件工程師


          2005 年 9 月

                  在進(jìn)行嵌入式開發(fā)之前,首先要建立一個(gè)交叉編譯環(huán)境,這是一套編譯器、連接器和libc庫等組成的開發(fā)環(huán)境。文章通過一個(gè)具體的例子說明了這些嵌入式交叉編譯開發(fā)工具的制作過程。

          隨著消費(fèi)類電子產(chǎn)品的大量開發(fā)和應(yīng)用和Linux操作系統(tǒng)的不斷健壯和強(qiáng)大,嵌入式系統(tǒng)越來越多的進(jìn)入人們的生活之中,應(yīng)用范圍越來越廣。

          在裁減和定制Linux,運(yùn)用于你的嵌入式系統(tǒng)之前,由于一般嵌入式開發(fā)系統(tǒng)存儲大小有限,通常你都要在你的強(qiáng)大的pc機(jī)上建立一個(gè)用于目標(biāo)機(jī)的交 叉編譯環(huán)境。這是一個(gè)由編譯器、連接器和解釋器組成的綜合開發(fā)環(huán)境。交叉編譯工具主要由 binutils、gcc 和 glibc 幾個(gè)部分組成。有時(shí)出于減小 libc 庫大小的考慮,你也可以用別的 c 庫來代替 glibc,例如 uClibc、dietlibc 和 newlib。建立一個(gè)交叉編譯工具鏈?zhǔn)且粋€(gè)相當(dāng)復(fù)雜的過程,如果你不想自己經(jīng)歷復(fù)雜的編譯過程,網(wǎng)上有一些編譯好的可用的交叉編譯工具鏈可以下載。

          下面我們將以建立針對arm的交叉編譯開發(fā)環(huán)境為例來解說整個(gè)過程,其他的體系結(jié)構(gòu)與這個(gè)相類似,只要作一些對應(yīng)的改動。我的開發(fā)環(huán)境是,宿主機(jī) i386-redhat-7.2,目標(biāo)機(jī) arm。

          這個(gè)過程如下

          1. 下載源文件、補(bǔ)丁和建立編譯的目錄

          2. 建立內(nèi)核頭文件

          3. 建立二進(jìn)制工具(binutils)

          4. 建立初始編譯器(bootstrap gcc)

          5. 建立c庫(glibc)

          6. 建立全套編譯器(full gcc)

          下載源文件、補(bǔ)丁和建立編譯的目錄

          1. 選定軟件版本號

          選擇軟件版本號時(shí),先看看glibc源代碼中的INSTALL文件。那里列舉了該版本的glibc編譯時(shí)所需的binutils 和gcc的版本號。例如在 glibc-2.2.3/INSTALL 文件中推薦 gcc 用 2.95以上,binutils 用 2.10.1 以上版本。

          我選的各個(gè)軟件的版本是:

          linux-2.4.21+rmk2
          binutils-2.10.1
          gcc-2.95.3
          glibc-2.2.3
          glibc-linuxthreads-2.2.3

          如果你選的glibc的版本號低于2.2,你還要下載一個(gè)叫g(shù)libc-crypt的文件,例如glibc-crypt-2.1.tar.gz。 Linux 內(nèi)核你可以從www.kernel.org 或它的鏡像下載。

          Binutils、gcc和glibc你可以從FSF的FTP站點(diǎn)ftp://ftp.gun.org/gnu/ 或它的鏡像去下載。在編譯glibc時(shí),要用到 Linux 內(nèi)核中的 include 目錄的內(nèi)核頭文件。如果你發(fā)現(xiàn)有變量沒有定義而導(dǎo)致編譯失敗,你就改變你的內(nèi)核版本號。例如我開始用linux-2.4.25+vrs2,編譯 glibc-2.2.3 時(shí)報(bào) BUS_ISA 沒定義,后來發(fā)現(xiàn)在 2.4.23 開始它的名字被改為 CTL_BUS_ISA。如果你沒有完全的把握保證你改的內(nèi)核改完全了,就不要動內(nèi)核,而是把你的 Linux 內(nèi)核的版本號降低或升高,來適應(yīng) glibc。

          Gcc 的版本號,推薦用 gcc-2.95 以上的。太老的版本編譯可能會出問題。Gcc-2.95.3 是一個(gè)比較穩(wěn)定的版本,也是內(nèi)核開發(fā)人員推薦用的一個(gè) gcc 版本。

          如果你發(fā)現(xiàn)無法編譯過去,有可能是你選用的軟件中有的加入了一些新的特性而其他所選軟件不支持的原因,就相應(yīng)降低該軟件的版本號。例如我開始用 gcc-3.3.2,發(fā)現(xiàn)編譯不過,報(bào) as、ld 等版本太老,我就把 gcc 降為 2.95.3。太新的版本大多沒經(jīng)過大量的測試,建議不要選用。


           
           

          2. 建立工作目錄

          首先,我們建立幾個(gè)用來工作的目錄:

          在你的用戶目錄,我用的是用戶liang,因此用戶目錄為 /home/liang,先建立一個(gè)項(xiàng)目目錄embedded。

          $pwd 
          /home/liang
          $mkdir embedded
           


          再在這個(gè)項(xiàng)目目錄 embedded 下建立三個(gè)目錄 build-tools、kernel 和 tools。

          build-tools-用來存放你下載的 binutils、gcc 和 glibc 的源代碼和用來編譯這些源代碼的目錄。

          kernel-用來存放你的內(nèi)核源代碼和內(nèi)核補(bǔ)丁。

          tools-用來存放編譯好的交叉編譯工具和庫文件。

          $cd embedded
          $mkdir  build-tools kernel tools
           


          執(zhí)行完后目錄結(jié)構(gòu)如下:

          $ls embedded
          build-tools kernel tools
           


          3. 輸出和環(huán)境變量

          我們輸出如下的環(huán)境變量方便我們編譯。

          $export PRJROOT=/home/liang/embedded
          $export TARGET=arm-linux
          $export PREFIX=$PRJROOT/tools
          $export TARGET_PREFIX=$PREFIX/$TARGET
          $export PATH=$PREFIX/bin:$PATH
           


          如果你不慣用環(huán)境變量的,你可以直接用絕對或相對路徑。我如果不用環(huán)境變量,一般都用絕對路徑,相對路徑有時(shí)會失敗。環(huán)境變量也可以定義在.bashrc文件中,這樣當(dāng)你logout或換了控制臺時(shí),就不用老是export這些變量了。

          體系結(jié)構(gòu)和你的TAEGET變量的對應(yīng)如下表


           

          你可以在通過glibc下的config.sub腳本來知道,你的TARGET變量是否被支持,例如:

          $./config.sub  arm-linux
          arm-unknown-linux-gnu
           


          在我的環(huán)境中,config.sub 在 glibc-2.2.3/scripts 目錄下。

          網(wǎng)上還有一些 HOWTO 可以參考,ARM 體系結(jié)構(gòu)的《The GNU Toolchain for ARM Target HOWTO》,PowerPC 體系結(jié)構(gòu)的《Linux for PowerPC Embedded Systems HOWTO》等。對TARGET的選取可能有幫助。

          4. 建立編譯目錄

          為了把源碼和編譯時(shí)生成的文件分開,一般的編譯工作不在的源碼目錄中,要另建一個(gè)目錄來專門用于編譯。用以下的命令來建立編譯你下載的binutils、gcc和glibc的源代碼的目錄。

          $cd $PRJROOT/build-tools
          $mkdir build-binutils build-boot-gcc build-gcc build-glibc gcc-patch
           


          build-binutils-編譯binutils的目錄
          build-boot-gcc-編譯gcc 啟動部分的目錄
          build-glibc-編譯glibc的目錄
          build-gcc-編譯gcc 全部的目錄
          gcc-patch-放gcc的補(bǔ)丁的目錄

          gcc-2.95.3 的補(bǔ)丁有 gcc-2.95.3-2.patch、gcc-2.95.3-no-fixinc.patch 和gcc-2.95.3-returntype-fix.patch,可以從 http://www.linuxfromscratch.org/ 下載到這些補(bǔ)丁。

          再將你下載的 binutils-2.10.1、gcc-2.95.3、glibc-2.2.3 和 glibc-linuxthreads-2.2.3 的源代碼放入 build-tools 目錄中

          看一下你的 build-tools 目錄,有以下內(nèi)容:

          $ls
          binutils-2.10.1.tar.bz2     build-gcc   gcc-patch
          build-binutls            build-glibc          glibc-2.2.3.tar.gz
          build-boot-gcc           gcc-2.95.3.tar.gz glibc-linuxthreads-2.2.3.tar.gz
           


           
           

          建立內(nèi)核頭文件

          把你從 www.kernel.org 下載的內(nèi)核源代碼放入 $PRJROOT /kernel 目錄

          進(jìn)入你的 kernel 目錄:

          $cd $PRJROOT /kernel
           


          解開內(nèi)核源代碼

          $tar -xzvf linux-2.4.21.tar.gz
           


          $tar -xjvf linux-2.4.21.tar.bz2
           


          小于 2.4.19 的內(nèi)核版本解開會生成一個(gè) linux 目錄,沒帶版本號,就將其改名。

          $mv linux linux-2.4.x
           


          給 Linux 內(nèi)核打上你的補(bǔ)丁

          $cd linux-2.4.21
          $patch -p1 < ../patch-2.4.21-rmk2
           


          編譯內(nèi)核生成頭文件

          $make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig

          你也可以用 config 和 xconfig 來代替 menuconfig,但這樣用可能會沒有設(shè)置某些配置文件選項(xiàng)和沒有生成下面編譯所需的頭文件。推薦大家用 make menuconfig,這也是內(nèi)核開發(fā)人員用的最多的配置方法。配置完退出并保存,檢查一下的內(nèi)核目錄中的 include/linux/version.h 和 include/linux/autoconf.h 文件是不是生成了,這是編譯 glibc 是要用到的,version.h 和 autoconf.h 文件的存在,也說明了你生成了正確的頭文件。

          還要建立幾個(gè)正確的鏈接

          $cd include
          $ln -s asm-arm asm
          $cd asm
          $ln -s arch-epxa arch
          $ln -s proc-armv proc
           


          接下來為你的交叉編譯環(huán)境建立你的內(nèi)核頭文件的鏈接

          $mkdir -p $TARGET_PREFIX/include
          $ln -s $PRJROOT/kernel/linux-2.4.21/include/linux  $TARGET_PREFIX/include/linux
          $in -s $PRJROOT/kernel/linux-2.4.21/include/asm-arm  $TARGET_PREFIX/include/asm
           


          也可以把 Linux 內(nèi)核頭文件拷貝過來用

          $mkdir -p $TARGET_PREFIX/include
          $cp -r $PRJROOT/kernel/linux-2.4.21/include/linux  $TARGET_PREFIX/include
          $cp -r $PRJROOT/kernel/linux-2.4.21/include/asm-arm  $TARGET_PREFIX/include  
           


           
           

          建立二進(jìn)制工具(binutils)

          binutils是一些二進(jìn)制工具的集合,其中包含了我們常用到的as和ld。

          首先,我們解壓我們下載的binutils源文件。

          $cd $PRJROOT/build-tools
          $tar -xvjf binutils-2.10.1.tar.bz2
           


          然后進(jìn)入build-binutils目錄配置和編譯binutils。

          $cd build-binutils
          $../binutils-2.10.1/configure --target=$TARGET --prefix=$PREFIX
           


          --target 選項(xiàng)是指出我們生成的是 arm-linux 的工具,--prefix 是指出我們可執(zhí)行文件安裝的位置。

          會出現(xiàn)很多 check,最后產(chǎn)生 Makefile 文件。

          有了 Makefile 后,我們來編譯并安裝 binutils,命令很簡單。

          $make
          $make install
           


          看一下我們 $PREFIX/bin 下的生成的文件

          $ls $PREFIX/bin
          arm-linux-addr2line arm-linux-gasp arm-linux-objdump  arm-linux-strings
          arm-linux-ar   arm-linux-ld  arm-linux-ranlib  arm-linux-strip
          arm-linux-as   arm-linux-nm  arm-linux-readelf 
          arm-linux-c++filt  arm-linux-objcopy arm-linux-size
           


          我們來解釋一下上面生成的可執(zhí)行文件都是用來干什么的

          add2line - 將你要找的地址轉(zhuǎn)成文件和行號,它要使用 debug 信息。

          Ar-產(chǎn)生、修改和解開一個(gè)存檔文件

          As-gnu 的匯編器

          C++filt-C++ 和 java 中有一種重載函數(shù),所用的重載函數(shù)最后會被編譯轉(zhuǎn)化成匯編的標(biāo)號,c++filt 就是實(shí)現(xiàn)這種反向的轉(zhuǎn)化,根據(jù)標(biāo)號得到函數(shù)名。

          Gasp-gnu 匯編器預(yù)編譯器。

          Ld-gnu 的連接器

          Nm-列出目標(biāo)文件的符號和對應(yīng)的地址

          Objcopy-將某種格式的目標(biāo)文件轉(zhuǎn)化成另外格式的目標(biāo)文件

          Objdump-顯示目標(biāo)文件的信息

          Ranlib-為一個(gè)存檔文件產(chǎn)生一個(gè)索引,并將這個(gè)索引存入存檔文件中

          Readelf-顯示 elf 格式的目標(biāo)文件的信息

          Size-顯示目標(biāo)文件各個(gè)節(jié)的大小和目標(biāo)文件的大小

          Strings-打印出目標(biāo)文件中可以打印的字符串,有個(gè)默認(rèn)的長度,為4

          Strip-剝掉目標(biāo)文件的所有的符號信息


           
           

          建立初始編譯器(bootstrap gcc)

          首先進(jìn)入 build-tools 目錄,將下載 gcc 源代碼解壓

          $cd $PRJROOT/build-tools
          $tar -xvzf  gcc-2.95.3.tar.gz
           


          然后進(jìn)入 gcc-2.95.3 目錄給 gcc 打上補(bǔ)丁

          $cd gcc-2.95.3
          $patch -p1< ../gcc-patch/gcc-2.95.3.-2.patch
          $patch -p1< ../gcc-patch/gcc-2.95.3.-no-fixinc.patch
          $patch -p1< ../gcc-patch/gcc-2.95.3-returntype-fix.patch
          echo timestamp > gcc/cstamp-h.in
           


          在我們編譯并安裝 gcc 前,我們先要改一個(gè)文件 $PRJROOT/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

          你如果沒定義 -Dinhibit,編譯時(shí)將會報(bào)如下的錯誤

          http://www.cnblogs.com/gcc-2.95.3/gcc/libgcc2.c:41: stdlib.h: No such file or directory
          http://www.cnblogs.com/gcc-2.95.3/gcc/libgcc2.c:42: unistd.h: No such file or directory
          make[3]: *** [libgcc2.a] Error 1
          make[2]: *** [stmp-multilib-sub] Error 2
          make[1]: *** [stmp-multilib] Error 1
          make: *** [all-gcc] Error 2
           


          如果沒有定義 -D__gthr_posix_h,編譯時(shí)會報(bào)如下的錯誤

          In file included from gthr-default.h:1,
                           from http://www.cnblogs.com/gcc-2.95.3/gcc/gthr.h:98,
                           from http://www.cnblogs.com/gcc-2.95.3/gcc/libgcc2.c:3034:
          http://www.cnblogs.com/gcc-2.95.3/gcc/gthr-posix.h:37: pthread.h: No such file or directory
          make[3]: *** [libgcc2.a] Error 1
          make[2]: *** [stmp-multilib-sub] Error 2
          make[1]: *** [stmp-multilib] Error 1
          make: *** [all-gcc] Error 2
           


          還有一種與-Dinhibit同等效果的方法,那就是在你配置configure時(shí)多加一個(gè)參數(shù)-with-newlib,這個(gè)選項(xiàng)不會迫使我們必須使用newlib。我們編譯了bootstrap-gcc后,仍然可以選擇任何c庫。

          接著就是配置boostrap gcc, 后面要用bootstrap gcc 來編譯 glibc 庫。

          $cd ..; cd build-boot-gcc
          $../gcc-2.95.3/configure --target=$TARGET --prefix=$PREFIX \
          >--without-headers  --enable-languages=c --disable-threads
           


          這條命令中的 -target、--prefix 和配置 binutils 的含義是相同的,--without-headers 就是指不需要頭文件,因?yàn)槭墙徊婢幾g工具,不需要本機(jī)上的頭文件。-enable-languages=c是指我們的 boot-gcc 只支持 c 語言。--disable-threads 是去掉 thread 功能,這個(gè)功能需要 glibc 的支持。

          接著我們編譯并安裝 boot-gcc

          $make all-gcc
          $make install-gcc
           


          我們來看看 $PREFIX/bin 里面多了哪些東西

          $ls $PREFIX/bin
           


          你會發(fā)現(xiàn)多了 arm-linux-gcc 、arm-linux-unprotoize、cpp 和 gcov 幾個(gè)文件。

          Gcc-gnu 的 C 語言編譯器

          Unprotoize-將 ANSI C 的源碼轉(zhuǎn)化為 K&R C 的形式,去掉函數(shù)原型中的參數(shù)類型。

          Cpp-gnu的 C 的預(yù)編譯器

          Gcov-gcc 的輔助測試工具,可以用它來分析和優(yōu)程序。

          使用 gcc3.2 以及 gcc3.2 以上版本時(shí),配置 boot-gcc 不能使用 --without-headers 選項(xiàng),而需要使用 glibc 的頭文件。


           
           

          建立 c 庫(glibc)

          首先解壓 glibc-2.2.3.tar.gz 和 glibc-linuxthreads-2.2.3.tar.gz 源代碼

          $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
           


          然后進(jìn)入 build-glibc 目錄配置 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 變量設(shè)成你剛編譯完的boostrap gcc,用它來編譯你的glibc。--enable-add-ons是告訴glibc用 linuxthreads 包,在上面我們已經(jīng)將它放入了 glibc 源碼目錄中,這個(gè)選項(xiàng)等價(jià)于 -enable-add-ons=linuxthreads。--with-headers 告訴 glibc 我們的linux 內(nèi)核頭文件的目錄位置。

          配置完后就可以編譯和安裝 glibc

          $make
          $make install_root=$TARGET_PREFIX prefix="" install
           


          然后你還要修改 libc.so 文件


          GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a)

          改為
          GROUP ( libc.so.6 libc_nonshared.a)

          這樣連接程序 ld 就會在 libc.so 所在的目錄查找它需要的庫,因?yàn)槟愕臋C(jī)子的/lib目錄可能已經(jīng)裝了一個(gè)相同名字的庫,一個(gè)為編譯可以在你的宿主機(jī)上運(yùn)行的程序的庫,而不是用于交叉編譯的。


           
           

          建立全套編譯器(full gcc)

          在建立boot-gcc 的時(shí)候,我們只支持了C。到這里,我們就要建立全套編譯器,來支持C和C++。

          $cd $PRJROOT/build-tools/build-gcc
          $../gcc-2.95.3/configure --target=$TARGET --prefix=$PREFIX --enable-languages=c,c++
           


          --enable-languages=c,c++ 告訴 full gcc 支持 c 和 c++ 語言。

          然后編譯和安裝你的 full gcc

          $make all
          $make install
           


          我們再來看看 $PREFIX/bin 里面多了哪些東西

          $ls $PREFIX/bin
           


          你會發(fā)現(xiàn)多了 arm-linux-g++ 、arm-linux-protoize 和 arm-linux-c++ 幾個(gè)文件。

          G++-gnu的 c++ 編譯器。

          Protoize-與Unprotoize相反,將K&R C的源碼轉(zhuǎn)化為ANSI C的形式,函數(shù)原型中加入?yún)?shù)類型。

          C++-gnu 的 c++ 編譯器。

          到這里你的交叉編譯工具就算做完了,簡單驗(yàn)證一下你的交叉編譯工具。

          用它來編譯一個(gè)很簡單的程序 helloworld.c

          #i nclude

          int main(void)
          {
           printf("hello world\n");
           return 0;
          }

          $arm-linux-gcc helloworld.c -o helloworld
          $file helloworld
          helloworld: ELF 32-bit LSB executable, ARM, version 1,
          dynamically linked (uses shared libs), not stripped
           


          上面的輸出說明你編譯了一個(gè)能在 arm 體系結(jié)構(gòu)下運(yùn)行的 helloworld,證明你的編譯工具做成功了。
          posted on 2011-12-05 09:14 H.C 閱讀(486) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 忻州市| 盐山县| 商河县| 马公市| 兴海县| 西乌珠穆沁旗| 绥滨县| 蓬溪县| 黄山市| 双牌县| 永登县| 西乌珠穆沁旗| 临沧市| 宽城| 宽甸| 武威市| 樟树市| 兴城市| 霍林郭勒市| 灯塔市| 临漳县| 织金县| 县级市| 丰都县| 咸丰县| 德清县| 疏勒县| 鄂州市| 河津市| 南溪县| 游戏| 珠海市| 鄯善县| 漳浦县| 大邑县| 克拉玛依市| 灵璧县| 德化县| 开封县| 华蓥市| 梧州市|