這個帖子主要目的是分享我制作交叉編譯工具鏈的過程,它是在參考了網上大量的資料以及我的實踐后,修改、整理而成的。之所以以powerpc為例,是因為項目使用的是Freescalse的MPC8315E處理器,實際上稍作修改就可以用在所有的處理器類型上(如ARM,MIPS等)。
目錄:
1. 基本概念
2.准備工作
3.制作過程
3.1 安裝內核頭文件
3.2 交叉編譯Binutils軟件包
3.3 交叉編譯臨時的GCC
3.4 交叉編譯Glibc
3.5 交叉編譯GCC
4. 總結
1.基本概念
什么是交叉編譯工具鏈,這是許多第一次接觸嵌入式開發的童鞋需要理解的首要問題。通常我們已經習慣在X86平台上運行gcc,對源程序進行編譯,編譯得到的目標程序,仍然是在X86平台上跑的。而交叉編譯工具鏈就是,需要在某個平台上,對源程序進行編譯,但是得到的目標程序卻是在另外一個平台上運行的。
我們已經知道,在某個平台對程序進行編譯后,得到的目標程序,默認也是在該平台運行的(例如X86)。所以我們通常需要在現有平台(通常我們把這個平台稱為 Host)的基礎上,制作出一個交叉編譯工具鏈(包括gcc、binutils、glibc),得到新的gcc仍然是在該平台(通常我們把該平台稱為Host)上運行的,但是當利用新的gcc去對某個源程序進行編譯時,得到的目標程序是在目標平台上運行的(通常我們把該平台稱為Target)。
現在我們知道,制作交叉編譯工具鏈少不了要先編譯一個gcc和 binutils(包括鏈接器ld strip等工具),但是僅僅是這兩個還不夠的。我們知道,在對源程序進行編譯時,少不了要依賴一些庫,例如C運行時庫(glibc),而你的Host中的庫,它是針對Host體系結構的。例如你的X86中的庫,其機器指令一定是X86的。這樣,你的交叉編譯工具鏈中,必須有目標平台的庫。你肯定已經想到了,先編譯好gcc和binutils,然后用這個gcc編譯目標平台的庫,然后就可以在這個庫的基礎上,編譯目標平台的程序了。
現在你已經可以想到制作一個交叉編譯工具鏈的步驟了,但是很快你會看到,在gcc的編譯過程中,我們需要編譯2遍,這是為什么呢? 一個全面的gcc(支持各種語言的),需要目標平台的C庫(glibc)的一些頭文件,但是這個新的gcc編譯出來之前,我們又沒有安裝目標平台的庫 (glibc)。所以我們先編譯一個基本的gcc(僅僅支持C語言),然后用這個gcc編譯目標平台的glibc,注意此時得到的glibc是目標平台的。最后,再在這個庫的基礎上,重新編譯一個全面的gcc。除此之外,我們還要准備好內核頭文件,這樣我們就可以直接使用內核的一些宏,數據結構定義,數據類型,等等。
在有了這些概念的基礎上,下面的操作就相對比較簡單了。這里需要提醒的是,同樣的編譯參數,不同的編譯環境,或者不同的gcc binutilsglibc版本,都可能編譯不成功。根據我的經驗,制作交叉編譯工具鏈,一帆風順就成功是很少見的。因此在編譯過程中,如果遇到失敗,耐心+細心的分析config.log,Makefile,可以幫助你定位問題。尤其對於新手來說,千萬不要急於求成,妄想直接復制一下命令行,一步步編譯就成功。我建議只是先看完一遍,對自己要做什么,和每一步的目的有個大概的了解,然后再開始。欲速則不達,這個道理很簡單,恐怕只有多品位幾次才能體會。另外,千萬不要以超級用戶(root)的身份來制作交叉編譯工具鏈,否則一不小心用target平台的庫,把Host平台上的庫給覆蓋了,后果可是很嚴重哦!
2. 准備工作
cd $HOME mkdir ppc #工具鏈的頂層目錄,你也可以命名為ARM cd ppc mkdir sources cd sources wget http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.bz2 wget http://ftp.gnu.org/gnu/glibc/glibc-2.14.tar.bz2 wget http://ftp.gnu.org/gnu/glibc/glibc-linuxthreads-2.5.tar.bz2 wget http://ftp.gnu.org/gnu/gcc/gcc-4.6.2/gcc-4.6.2.tar.bz2 wget ftp://ftp.kernel.org/pub/linux/kernel/v3.x/linux-3.1.5.tar.bz2 wget ftp://ftp.gnu.org/gnu/gmp/gmp-5.0.2.tar.bz2 wget http://www.mpfr.org/mpfr-current/mpfr-3.1.0.tar.bz2 mkdir ../tools export TARGET=powerpc-linux #如果是ARM平台,則為arm-linux export TOOLS=~/ppc/tools export SOURCES=~/ppc/sources export PATH=$TOOLS/bin:$PATH #這一步是必需的,只有將帶target alias 即powerpc-linux-前綴的工具放到PATH中,才能使用整個交叉編譯環境。 export LANGUAGE=C #下面兩個參數編譯glibc時默認使用的locale export LC_ALL=C
3.制作過程
3.1 安裝內核頭文件(編譯glibc時用到)
cd $SOURCES tar jvxf linux-3.1.5.tar.bz2 #把內核頭文件安裝到$TOOLS/$TARGET/usr/include中。 make ARCH=powerpc INSTALL_HDR_PATH=$TOOLS/$TARGET/usr headers_install #安裝后目錄中的內容: ls -p $TOOLS/$TARGET/usr/include asm/ asm-generic/ drm/ linux/ mtd/ rdma/ scsi/ sound/ video/ xen/
3.2 交叉編譯Binutils軟件包
配置源碼:
cd $SOURCES tar jvxf binutils-2.22.tar.bz2 mkdir binutils-build cd binutils-build ../binutils-2.22/configure \ --prefix=$TOOLS \ --target=$TARGET #生成的工具可以處理target平台的二進制代碼,但是這些工具仍然運行在編譯這些工具的平台上(編譯平台類型由--build參數指定,但是一般不用指定,而是由源代碼中的config.guess來猜測。編譯后生成的工具的運行平台由--host參數指定,默認與--build值相同)。
編譯並安裝:
make make install mkdir $TOOLS/include
復制相關的頭文件,以后如果要交叉編譯gdb時會用到。
cp ../binutils-xxx/include/libiberty.h $TOOLS/include
安裝完成后,$TOOLS目錄如下:
ls -p $TOOLS bin/ info/ lib/ man/ powerpc-linux/ share/
此處需要注意的是:$TOOLS/bin/ 和 $TOOLS/$TARGET/bin(TARGET就是powerpc-linux)的內容
ls -p $TOOLS/bin powerpc-linux-addr2line powerpc-linux-c++filt powerpc-linux-ld powerpc-linux-objdump powerpc-linux-size powerpc-linux-ar powerpc-linux-embedspu powerpc-linux-nm powerpc-linux-ranlib powerpc-linux-strings powerpc-linux-as powerpc-linux-gprof powerpc-linux-objcopy powerpc-linux-readelf powerpc-linux-strip ls -p $TOOLS/$TARGET/bin ar as ld nm objcopy objdump ranlib strip
這些文件雖然在不同的目錄,有不同的名字,但其實是一個文件:
md5sum $TOOLS/bin/powerpc-linux-as 3f77cbaaa417e2f59059114457d7d074 bin/powerpc-linux-as md5sum $TOOLS/$TARGET/bin/as 3f77cbaaa417e2f59059114457d7d074 powerpc-linux/bin/as
除此之外,binutils把用於生成目標平台的代碼的鏈接腳本安裝到$TOOLS/$TARGET/ldscripts目錄下。
3.3 交叉編譯臨時的GCC
由於編譯gcc依賴 gmpmpfr這兩個庫,因此在編譯gcc之前,首先安裝這兩個庫到系統中(如果你的系統已經安裝了這兩個庫,則下面的安裝步驟可省略。).
tar jvxf gmp-5.0.2.tar.bz2 cd gmp-5.0.2 ./configure --enable-cxx --enable-mpbsd --prefix=/usr make make check sudo make install
如果沒有指定--prefix=/usr,那么gmp默認安裝到/usr/local目錄下,而mpfr默認到/usr目錄下搜索這個庫。因此這里通過--prefix=/usr,把他們都安裝到/usr下。見mpfr的FAQ: http://www.mpfr.org/faq.html
cd $SOURCES tar jvxf mpfr-3.1.0.tar.bz2 ./configure --enable-thread-safe --prefix=/usr \ make make check sudo make install
現在第一次編譯GCC:
cd $SOURCES
tar jvxf gcc-4.6.2.tar.bz2
mkdir gcc-bootstrap-build
cd gcc-bootstrap-build
../gcc-4.6.2/configure \
--target=$TARGET \ #編譯后生成的gcc可以生成的目標代碼的執行平台(即編譯生成的gcc可以生成target對應的平台的代碼)。
--prefix=$TOOLS --disable-nls --disable-shared \
--disable-multilib --disable-decimal-float --disable-threads --disable-libmudflap \
--disable-libssp --disable-libgomp --without-headers --with-newlib \
--enable-languages=c
這一步編譯的是一個bootstarp類型的gcc(一個在當前主機上運行的、使用自帶的newlib庫的gcc,其實是為了解決gcc和glibc間的 “雞、蛋問題”,:) 。),因此不會使用上一步生成的binutils工具使用的當前系統的binutils,但是當我們編譯glibc 最后一次編譯gcc時,需要使用上面生成的binutils。
這里的--with-package 的含義是drop newlib into the tree and do a combined build of both at once which breaks the circular dependency. 所以我們編譯的gcc被成為bootstrap 即編譯的是一個能引導整個編譯環境的基本功能編譯器。
編譯安裝gcc庫:
make all-gcc make all-target-libgcc make install-gcc #把編譯好的gcc分別安裝到$TOOLS/bin/ 和 $TOOLS/$TARGET/bin make install-target-libgcc #把gcc的庫文件及頭文件安裝到$TOOLS/lib/$TARGET/$GCC_VERSION/目錄中。這里$GCC_VERSION表示gcc版本號。
3.4 交叉編譯Glibc
下面用剛才編譯出的臨時gcc交叉編譯glibc,這個glibc可運行在目標平台的(由host參數指定)。
cd $SOURCES tar jvxf glibc-2.14.tar.bz2 tar jvxf glibc-linuxthreads-2.5.tar.bz2 --directory=glibc-2.14
編譯之前,修改glibc-2.9的Makeconfig,否則會出錯。
vim glibc-2.14/Makeconfig
把下面兩行:
gnulib := -lgcc $(libgcc_eh) #libgcc_eh是用於處理C++異常的代碼 static-gnulib := -lgcc -lgcc_eh $(libunwind)
改成:
gnulib := -lgcc static-gnulib := -lgcc
配置glibc:
mkdir glibc-build
cd glibc-build
CC=$TOOLS/bin/${TARGET}-gcc ../glibc-2.9/configure --prefix=/ \
--host=$TARGET \ #生成的軟件的運行平台
--build=$(../glibc-2.9/scripts/config.guess) \ #編譯該軟件的平台類型,當host和build不同時,即為交叉編譯: 在build對應的平台上編譯可運行於host平台上的軟件。
--with-headers=$TOOLS/$TARGET/usr/include/ \ #編譯glibc時需要使用kernel的頭文件,其中有關於powerpc相關的頭文件
--with-binutils=$TOOLS/$TARGET/bin \ #注意,一定要是$TARGET下的bin目錄,這里面的程序沒有target-alias
--disable-profile libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes
編譯並安裝:
make make install_root=$TOOLS/$TARGET prefix="" install
用這種方法(--prefix=/usr 然后用install_root指定安裝位置)編譯安裝的glibc可以直接拷貝到目標機上運行。
首先,CC=...,binutils=... 指定了用我們新編譯好的gcc和binutils,因此得到的glibc是目標平台的。(CC在下一步應該unset)
最后,打開$TOOLS/$TARGET/lib/libc.so
把 GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a ) 改為:
GROUP ( libc.so.6 libc_nonshared.a )
3.5 交叉編譯GCC
現在,庫已經准備好了,編譯一個全面的gcc
cd $SOURCES
mkdir gcc-full-build
cd gcc-full-build
../gcc-4.6.2/configure --target=$TARGET --host=$(../gcc-4.6.2/config.guess)
--prefix=$TOOLS
--with-headers=$TOOLS/$TARGET/usr/include/ #這是必需的,因為powerpc-linux-gcc 會到--with-headers指定的目錄查找頭文件。這個選項會將相關目錄復制到$TOOLS/$TARGET/sys-include目錄下。
--enable-languages=cc++ --disable-libgomp
--disable-multilib --disable-nls --enable-shared
這里沒有指定--with-newlib,所以使用的是$TOOL/$TARGET下的glibc庫,以及$TOOL/$TARGET/bin中的binutils工具。這里使用的是系統的gcc,而不是第一遍生成的powerpc-linux-gcc(因為最終的gcc是運行在本機上的。) 第一遍生成的gcc只用於編譯glibc。
交叉編譯gcc(powerpc-linux-gcc)被安裝在$TOOL目錄下,所以不能直接使用$TOOL/$TARGET/bin/gcc,因為交叉編譯gcc所調用的一些輔助程序如cc1是放在$TOOL/libexec目錄下的。
make all make install
現在整個交叉編譯環境就建立起來了,可以將$TOOL/bin目錄放到PATH變量中,這樣就可以使用powerpc-linux-gcc即目錄中的其它工具為目標板編譯任何程序了。
4. 總結
上面的幾個步驟中只有編譯glibc時是交叉編譯過程,需要用到交叉編譯器powerpc-linux-gcc和相應的帶target alias的binutils。
交叉編譯是指在本機上本次編譯生成的代碼運行在目標主機上,這個過程才叫交叉編譯過程。上面的編譯bintuils和gcc的過程都不是交叉編譯過程,因為它們生成的程序是在當前主機上運行。非交叉編譯過程使用的都是本機上的gcc和binutils。但是上面編譯后獲得的binutils和gcc是交叉編譯環境中必不可少的組件(當然,還包括glibc)。一旦使用交叉編譯環境中的gcc,則它會自動使用相應的binutils和glibc(因為它們都放在交叉編譯環境的目錄中$TOOL/$TARGET)。
現在,你就可以在X86平台上運行powerpc-linux-gcc,編譯一個C程序,注意得到的目標程序是在 PowerPC平台上執行的。
