|
1 引言 由于一般嵌入式開(kāi)發(fā)系統(tǒng)存儲(chǔ)容量有限,在裁減和定制Linux,運(yùn)用于嵌入式系統(tǒng)前,通常需要在PC機(jī)上建立一個(gè)用于目標(biāo)機(jī)的交叉編譯環(huán)境,也就是將各種二進(jìn)制工具程序集成為工具鏈,其中包括如GNU的鏈接器(ld)、GNU的匯編器(as)、ar(產(chǎn)生修改和解開(kāi)一個(gè)存檔文件)、C編譯器(gcc)以及C鏈接庫(kù)(glibc)。本文以在Linux系統(tǒng)上針對(duì)目標(biāo)機(jī)arm為例,介紹了跨平臺(tái)開(kāi)發(fā)工具鏈的建立過(guò)程。 2 基本概念 2.1 什么是交叉編譯? 簡(jiǎn)單地說(shuō),交叉編譯就是在一個(gè)平臺(tái)上生成在另一個(gè)平臺(tái)上執(zhí)行的代碼。這里的平臺(tái)包括體系結(jié)構(gòu)(Architecture)和操作系統(tǒng)(OS)。同一個(gè)體系結(jié)構(gòu)可以運(yùn)行不同的操作系統(tǒng),同樣,同一個(gè)操作系統(tǒng)也可以在不同的體系結(jié)構(gòu)上運(yùn)行。舉例來(lái)說(shuō),x86 Linux平臺(tái)是Intel x86 體系結(jié)構(gòu)和Linux for x86操作系統(tǒng)的統(tǒng)稱。 2.2 為什么要用交叉編譯? 原因有兩個(gè)。一是目標(biāo)平臺(tái)所需要的bootloader以及OS核心還沒(méi)有建立時(shí),需要作交叉編譯。二是目標(biāo)機(jī)設(shè)備不具備一定的處理器能力和存儲(chǔ)空間,即單獨(dú)在目標(biāo)板上無(wú)法完成程序開(kāi)發(fā),所以只好求助宿主機(jī)。這樣可以在宿主機(jī)上對(duì)即將在目標(biāo)機(jī)上運(yùn)行的應(yīng)用程序進(jìn)行編譯,生成可以在目標(biāo)機(jī)上運(yùn)行的代碼格式,然后移植到目標(biāo)板上,也就是目前嵌入式程序開(kāi)發(fā)的Host/Target模式。 2.3 對(duì)于i386的理解 如果單純說(shuō)i386、i686,就是指平時(shí)所說(shuō)的CPU類(lèi)型。從Linux內(nèi)核設(shè)計(jì)上講,i386是架構(gòu),i486/586/686這些CPU的架構(gòu)都是i386,所以很多linux方面的設(shè)計(jì)都是基于i386。簡(jiǎn)單地說(shuō),i386跟ppc,alpha,arm等放在一起時(shí)就是指架構(gòu),跟i586,i686放在一起指處理器型號(hào),一個(gè)是橫向的,一個(gè)是縱向的。 3 建立過(guò)程 3.1 選定軟件版本 要想選用適當(dāng)?shù)陌姹?,以保證建立的工具鏈可用,就必須找到適合主機(jī)和目標(biāo)板的組合。這些可以自己測(cè)試,也可以從網(wǎng)上尋找已經(jīng)測(cè)試過(guò)的版本組合,即binutils、gcc、glibc的版本組合。我用的宿主機(jī)為redhat-9.0,目標(biāo)機(jī)arm,選擇的版本如下: -------------------------------------------------------------------------------- binutils- gcc-core-2.95.3.tar.gz 包含GCC的主體部分。 gcc-g++2.95.3.tar.gz 可以使GCC編譯C++程序。 glibc- glibc-linuxthreads- linux- -------------------------------------------------------------------------------- 你可以嘗試選定更新的版本,編譯無(wú)法通過(guò)時(shí),依次使用較舊的版本。即時(shí)發(fā)現(xiàn)新版本組合能夠編譯成功,仍然需要測(cè)試建立的工具鏈?zhǔn)欠窨梢允褂谩?/span> 你可以從FTP網(wǎng)站ftp://ftp.gnu.org/gnu/或者任何其他的鏡像網(wǎng)站下載GNU工具鏈的各個(gè)組件:binutils包位于binutils目錄,gcc包位于gcc目錄,而glibc包與glibc-linuxthreads包放在glibc目錄。下面給出上面選用的各個(gè)版本的下載路徑。 -------------------------------------------------------------------------------- binutils- ftp://ftp.gnu.org/gnu/binutils/binutils- gcc-core-2.95.3.tar.gz ftp://ftp.gnu.org/gnu/gcc/gcc-2.95.3/gcc-core-2.95.3.tar.gz gcc-g++2.95.3.tar.gz ftp://ftp.gnu.org/gnu/gcc/gcc-2.95.3/gcc-g++-2.95.3.tar.gz glibc- ftp://ftp.gnu.org/gnu/glibc/glibc- glibc-linuxthreads- ftp://ftp.gnu.org/gnu/glibc/glibc-linuxthreads- linux- ftp://ftp.kernle.org/pub/linux/kernel/v2.4/linux- ftp://ftp.arm.linux.org.uk/pub/linux/arm/kernel/v2.4/patch- -------------------------------------------------------------------------------- 3.2 建立工作目錄 我的用戶名為lqm,所以所有的工作都在/home/lqm下面建立完成。 ************************************************************ $cd /home/lqm 進(jìn)入工作目錄 $pwd 查看當(dāng)前目錄 /home/lqm $mkdir embedded-system 創(chuàng)建工具鏈文件夾 $ls 查看/home/lqm建立的所有文件 embedded-system ************************************************************ 現(xiàn)在已經(jīng)建立了頂層文件夾embedded-system,下面在此文件夾下建立如下幾個(gè)目錄: -------------------------------------------------------------------------------- setup-dir 存放下載的壓縮包 src-dir 存放binutils、gcc、glibc解壓之后的源文件 kernel 存放內(nèi)核文件,對(duì)內(nèi)核的配置和編譯工作也在此完成 build-dir 編譯src-dir下面的源文件。這是GNU推薦的源文件目錄與編譯目錄分離的做法。 tool-chain 交叉編譯工具鏈的安裝位置 program 存放編寫(xiě)程序 doc 說(shuō)明文檔和腳本文件 -------------------------------------------------------------------------------- 下面建立目錄,并且下載源文件。 ************************************************************ $pwd /home/lqm/ $cd embedded-system $mkdir setup-dir src-dir kernel build-dir tool-chain program doc $ls build-dir doc kernel program setup-dir src-dir tool-chain $cd setup-dir $wget ftp://ftp.gnu.org/gnu/binutils/binutils- $wget ftp://ftp.gnu.org/gnu/gcc/gcc-2.95.3/gcc-core-2.95.3.tar.gz $wget ftp://ftp.gnu.org/gnu/gcc/gcc-2.95.3/gcc-g++-2.95.3.tar.gz $wget ftp://ftp.gnu.org/gnu/glibc/glibc- $wget ftp://ftp.gnu.org/gnu/glibc/glibc-linuxthreads- $wget ftp://ftp.kernel.org/pub/linux/kernel/v2.4/linux- $wget ftp://ftp.arm.linux.org.uk/pub/linux/arm/kernel/v2.4/ patch- $ls binutils- patch- $cd ../build-dir $mkdir build-binutils build-gcc build-glibc 建立編譯目錄 ************************************************************ 3.3 輸出環(huán)境變量 在建立與使用某些工具程序時(shí),可能會(huì)用到這些目錄的路徑,如果設(shè)計(jì)一個(gè)簡(jiǎn)短的命令腳本,設(shè)定適當(dāng)?shù)沫h(huán)境變量,則可以簡(jiǎn)化操作過(guò)程。下面就建立命令腳本hjbl: ************************************************************ $pwd /home/lqm/embedded-system/build-dir $cd ../doc $mkdir scripts $cd scripts $emacs hjbl 用文本編輯器emacs編譯環(huán)境變量腳本 -------------------------------------------------------------------------------- 在隨后打開(kāi)的emacs編輯窗口中輸入下面內(nèi)容(如果在命令行界面下,則必須要用到vi文本編輯器,emacs則不可以): export PRJROOT=/home/lqm/embedded-system export TARGET=arm-linux export PREFIX=$PRJROOT/tool-chain export TARGET_PREFIX=$PREFIX/$TARGET export PATH=$PREFIX/bin:$PATH 保存后關(guān)閉emacs窗口,如果要在目前的窗口中執(zhí)行此腳本,即讓環(huán)境變量生效,還需要執(zhí)行下面的語(yǔ)句: -------------------------------------------------------------------------------- $. hjbl(注意:在點(diǎn)和hjbl之間有一個(gè)空格) $cd $PRJROOT 驗(yàn)證環(huán)境變量是否生效 $ls build-dir doc kernel program setup-dir src-dir tool-chain ************************************************************ 該環(huán)境變量的作用時(shí)間僅僅在Terminal當(dāng)前窗口,如果將窗口關(guān)閉,開(kāi)啟一個(gè)新的窗口,則環(huán)境變量實(shí)效,需要重新執(zhí)行下面的命令: $. /home/lqm/embedded-system/doc/scripts/hjbl 說(shuō)明: TARGET變量用來(lái)定義目標(biāo)板的類(lèi)型,以后會(huì)根據(jù)此目標(biāo)板的類(lèi)型來(lái)建立工具鏈。參看表1。目標(biāo)板的定義與主機(jī)的類(lèi)型是沒(méi)有關(guān)系的,但是如果更改TARGET的值,GNU工具鏈必須重新建立一次。 PREFIX變量提供了指針,指向目標(biāo)板工具程序?qū)⒈话惭b的目錄。 TARGET_PREFIX變量指向與目標(biāo)板相關(guān)的頭文件和鏈接庫(kù)將被安裝的目錄。 PATH變量指向二進(jìn)制文件(可執(zhí)行文件)將被安裝的目錄。 表1 TARGET變量值
3.4 內(nèi)核頭文件的配置 內(nèi)核頭文件的配置是建立工具鏈的第一步。它與后面將要執(zhí)行的其他步驟有著類(lèi)似性,大多需要執(zhí)行下面幾步操作: 1、 解壓縮包 2、 為跨平臺(tái)開(kāi)發(fā)設(shè)定包的配置 3、 建立包 4、 安裝包 ************************************************************ $pwd /home/lqm/embedded-system/ $cd kernel $tar xvzf ../setup-dir/ linux- $gunzip ../setup-dir/ patch- $cd linux- $patch –p1 < ../../setup-dir/patch- $make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig 配置 $make dep -------------------------------------------------------------------------------- 變量ARCH和CROSS_COMPILE的值與目標(biāo)板的架構(gòu)類(lèi)型有關(guān)。如果使用PPC目標(biāo)板,則ARCH=ppc CROSS_COMPILE=ppc-linux-。如果使用i386目標(biāo)板,則ARCH=i386 CROSS_COMPILE=i386-linux-。 make menuconfig是以文本菜單方式配置。 make xconfig是以圖形界面方式配置。 make config是純文本方式界面配置。 一般選擇make menuconfig,注意在選項(xiàng)System Types中選擇正確的硬件類(lèi)型。配置完退出并保存,檢查一下的內(nèi)核目錄中的 kernel/linux- 然后,建立工具鏈需要的include目錄,并將內(nèi)核頭文件復(fù)制過(guò)去。 -------------------------------------------------------------------------------- $cd include $ln -s asm-arm asm #可以查看一下,經(jīng)過(guò)編譯可以自動(dòng)生成。如果已經(jīng)生成連接,則不必寫(xiě) $cd asm $ln -s arch-epxa arch #同上說(shuō)明 $ln -s proc-armv proc #同上說(shuō)明 #這些是針對(duì)makefile文件作出的修改 $mkdir –p $TARGET_PREFIX/include $cp –r $PRJROOT/kernel/linux- $cp –r $PRJROOT/kernrl/linux- ************************************************************ 注意: 1、不必再每次重新設(shè)定內(nèi)核配置之后重建工具鏈,除非你變更了處理器或系統(tǒng)的類(lèi)型。工具鏈只需要一組可供目標(biāo)板使用的有效頭文件即可,這些頭文件在前面的程序中早就已經(jīng)提供了。 2、asm-linux文件夾放到目標(biāo)文件夾$TARGET_PREFIX/include/時(shí)要更改名稱為asm,因?yàn)榕渲梦募?/span>include包含都是<asm/*.h>方式。這也是交叉編譯的不同之處。否則就會(huì)出現(xiàn)類(lèi)似下面的錯(cuò)誤提示: -------------------------------------------------------------------------------- ......... done _udivsi3 _divsi3 _umodsi3 _modsi3 _dwmd_lnx libgcc1.s:438:asm/unistd.h:No such file or directory make [1] *** [libgcc1-asm.a] error 1 -------------------------------------------------------------------------------- 3.5 binutils(二進(jìn)制工具程序)的設(shè)置 binutils包中的工具常用來(lái)操作二進(jìn)制目標(biāo)文件。該包中最重要的兩個(gè)工具就是GNU匯編器as和鏈接器ld。 ************************************************************ $cd $PRJROOT/src-dir $tar xvzf ../setup-dir/binutils- $cd $PRJROOT/build-dir/build-binutils $../../src-dir/binutils- $make $make install $ls $PREFIX/bin 驗(yàn)證安裝的結(jié)果是否正確 arm-linux-addr2line arm-linux-ld arm-linux-readelf arm-linux-ar arm-linux-nm arm-linux-size arm-linux-as arm-linux-objcopy arm-linux-strings arm-linux-c++filt arm-linux-objdump arm-linux-strip arm-linux-gasp arm-linux-ranlib ************************************************************ 注意:每個(gè)工具的文件名的前綴都是前面為TARGET變量設(shè)定的值。如果目標(biāo)板是i386-linux,那么這些工具的文件名前綴就會(huì)是i386-linux-。這樣就可以根據(jù)目標(biāo)板類(lèi)型找到正確的工具程序。 3.6 初始編譯器的建立 開(kāi)始只能建立支持C語(yǔ)言的引導(dǎo)編譯器,因?yàn)槿鄙?/span>C鏈接庫(kù)(glibc)的支持。等到glibc編譯好之后,可以重新編譯gcc并提供完整的C++支持。 ************************************************************ $cd $PRJROOT/setup-dir $mv gcc-core-2.95.3.tar.gz gcc-2.95.3.tar.gz #重命名 $cd $PRJROOT/src-dir $tar xvzf ../setup-dir/gcc-2.95.3.tar.gz $cd $PRJROOT/build-dir/build-gcc $../../src-dir/gcc-2.95.3/configure --target=$TARGET --prefix=$PREFIX --without-headers -------------------------------------------------------------------------------- 因?yàn)槭墙徊婢幾g器,還不需要目標(biāo)板的系統(tǒng)頭文件,所以需要使用 --without-headers這個(gè)選項(xiàng)。--enable-language=c用來(lái)告訴配置腳本,需要產(chǎn)生的編譯器支持何種語(yǔ)言,現(xiàn)在只能支持C語(yǔ)言。--disable-threads是因?yàn)?/span>threads需要glibc的支持。 準(zhǔn)備好了Makefile文件,進(jìn)行編譯之前,需要修改src-dir/gcc-2.95.3/gcc/config/arm/t-linux文件,在TARGET_LIBGCC2_CFLAGS中添加兩個(gè)定義:-Dinhibit_libc -D__gthr_posix_h,否則會(huì)報(bào)錯(cuò)。 -------------------------------------------------------------------------------- $make $make install ************************************************************ 3.7 建立C庫(kù)(glibc) 這一步是最為繁瑣的過(guò)程。目標(biāo)板必須靠它來(lái)執(zhí)行或者是開(kāi)發(fā)大部分的應(yīng)用程序。glibc套件常被稱為C鏈接庫(kù),但是glibc實(shí)際產(chǎn)生很多鏈接庫(kù),其中之一是C鏈接庫(kù)libc。因?yàn)榍度胧较到y(tǒng)的限制,標(biāo)準(zhǔn)GNU C鏈接庫(kù)顯得太大,不適合應(yīng)用在目標(biāo)板上。所以需要尋找C鏈接庫(kù)的替代品,比如uClibc。在這里,現(xiàn)以標(biāo)準(zhǔn)GNU C為例建立工具鏈。 ************************************************************ $cd $PRJROOT/src-dir $tar xvzf ../setup-dir/glibc- $tar xvzf ../setup-dir/glibc-linuxthreads- $cd $PRJROOT/build-dir/build-glibc $CC=arm-linux-gcc ../../src-dir/glibc- $make $make install_root=$TARGET_PREFIX prefix=”” install -------------------------------------------------------------------------------- 在這里設(shè)定了install_root變量,指向鏈接庫(kù)組件目前所要安裝的目錄。這樣可以讓鏈接庫(kù)及其頭文件安裝到通過(guò)TARGET_PREFIX指定的與目標(biāo)板有關(guān)的目錄,而不是建立系統(tǒng)本身的/usr目錄。因?yàn)橹笆褂?/span>--prefix選項(xiàng)來(lái)設(shè)定prefix變量的值,而且prefix的值會(huì)被附加到install_root的值之后,成為鏈接庫(kù)組件的安裝目錄,所以需要重新設(shè)定prefix的值。這樣所有的glibc組件將會(huì)安裝到$TARGET_PREFIX指定的目錄下。 -------------------------------------------------------------------------------- $cd $TARGET_PREFIX/lib $cp ./libc.so ./libc.so.orig -------------------------------------------------------------------------------- 編輯文件libc.so,更改如下: /* GNU ld script Use the shared library,but some functions are only in the static library,so try that secondarily.*/ GROUP(libc.so.6 libc_nonshared.a) -------------------------------------------------------------------------------- ************************************************************ 3.8 完整編譯器的設(shè)置 現(xiàn)在可以為目標(biāo)板安裝支持C和C++的完整編譯器了。這個(gè)步驟相對(duì)于前面來(lái)建立過(guò)程要簡(jiǎn)單一些。 ************************************************************ $cd $PRJROOT/build-dir/build-gcc $../../src-dir/gcc-2.95.3/configure --target=$TARGET --prefix=$PREFIX $make all $make install ************************************************************ 3.9 完成工具鏈的設(shè)置 ************************************************************ $cd $TARGET_PREFIX/bin $file as ar gcc ld nm ranlib strip 查看文件是否為二進(jìn)制文件 $arm-linux-gcc -print-search-dirs 查看缺省的搜尋路徑 $mv as ar gcc ld nm ranlib strip $PREFIX/lib/gcc-lib/arm-linux/2.95.3 轉(zhuǎn)移文件 $for file in as ar gcc ld nm ranlib strip >do >ln -s $PREFIX/lib/gcc-lib/arm-linux/2.95.3/$file >done ************************************************************ 3.10 使用工具鏈 下面編寫(xiě)一個(gè)簡(jiǎn)單的C程序,使用建立的工具鏈。、 ************************************************************ $cd $PRJROOT/program $emacs hello.c -------------------------------------------------------------------------------- 在文本編輯器emacs中編寫(xiě): #include <stdio.h> int main() { int i; for(i=1;i<9;i++) printf(“Hello World %d times!\n”,i); } 保存退出 -------------------------------------------------------------------------------- $gcc -g hello.c -o hello $gdb (gdb)file hello (gdb)l #include <stdio.h> int main() { int i; for(i=1;i<9;i++) printf(“Hello World %d times!\n”,i); } (gdb)r (gdb)q $arm-linux-gcc -g hello.c -o hello-linux $file hello-linux hello-linux:ELF 32-bit LSB executable,ARM,version 1(ARM),for GNU/Linux ************************************************************ 上面的輸出說(shuō)明你編譯了一個(gè)能在 arm 體系結(jié)構(gòu)下運(yùn)行的 hello-linux,證明你的編譯工具做成功了。 4 總結(jié) 通過(guò)上面的操作,已經(jīng)能夠建立全功能的跨平臺(tái)開(kāi)發(fā)工具鏈,在以后的嵌入式開(kāi)發(fā)中將會(huì)經(jīng)常用到。 說(shuō)明:-----之間為說(shuō)明文字 ***之間為源程序 參考: [1] http://www./arm/arm-2004.htm [2] 構(gòu)建嵌入式LINUX系統(tǒng),中國(guó)電力出版社 [3] http://www-128.ibm.com/developerworks/cn/linux/l-embcmpl/ 如何為嵌入式開(kāi)發(fā)建立交叉編譯環(huán)境,梁元恩 [4] http://www./oldsite/armlinux//docs/toolchain/toolchHOWTO/t2.html The GNU Toolchain for ARM targets HOWTO |
|
|
來(lái)自: darkhero > 《我的圖書(shū)館》