2010年8月20日 星期五

如何編譯ARM Cross Compiler ToolChain

在Embedded系統中, 通常我們會需要做cross development, 而這時則需要cross compiler, 而有時等待網路上其他編譯好的cross compiler會太慢, 或者他們編譯時所選的選項不是你想要的, 這時則必需自行編譯, 我個人的經驗則是完全自行編譯使用, 而不用其他人所編譯好的toolchain, 所以我將我個人的經驗提供給大家做參考。在編譯之前你必需準備好以下的環境及source code:

下載原始碼
1. 安裝好一個PC Linux, 你可以用virtual machine來建立, 個人不太建議使用cygwin這類的環境來做開發環境。
2. 必需在PC Linux上按裝軟體如下:
  • x86版本的gcc compiler (必須包含g++)
  • 公共程式texi2html
  • 公共程式gawk
  • 函式庫zlib
3. 下載kernel source code (這裏我使用2.6.35當範例) 。
4. 下載gcc source code (這裏我使用4.5.0當範例) 。
5. 下載glibc source code (這裏我使用2.11.2當範例) 。
6. 下載binutil source code (這裏我使用2.20.0當範例) 。
7. 下載glibc ARM patched file (glibc-ports-2.11.tar.bz2)。
8. 下載gmp source code (這裏我使用5.0.1當範例) 。
9. 下載mpfr source code (這裏我使用3.0.0當範例) 。
10. 下載mpc source code (這裏我使用0.8.2當範例, 在GCC 4.5.0之後必需使用mpc)。

編譯流程
以下則是整個編譯過程的主要步驟, 你必須依照以下的順序, 否則你將無法順利編譯成功:
1. 將下載下來的source code全部解壓縮至各別的目錄。
2. 先編譯binutils, 並將其按裝在你的PC Linux上 (這裏假設按裝的目錄為/usr/local/arm-linux-gnueabi), 這個原始碼是ld, as, ranlib等的程式碼。
3. 編譯gmp並將其按裝至以上binutil按裝目錄之下的tools目錄。
4. 宣告一個系統變數 export LD_LIBRARY_PATH=/usr/local/arm-linux-gnueabi/tools/lib。
5. 編譯mpfr並將其按裝至以上binutil按裝目錄之下的tools目錄。
6. 編譯mpc並將其按裝至以上binutil按裝目錄之下的tools目錄。
7. 增加系統search變數 export PATH=$PATH:/usr/local/arm-linux-gnueabi/bin。
8. 編譯gcc為static以及只有 c 語言的支援, 並將其按裝至PC Linux。
9. 編譯glibc為shared library並將其按裝至PC Linux, 若是要編譯多種版本library (little endian, big endian version), 則必須要在這個步驟重覆執行, 使用不同的參數來產生不同版本的library。
10. 再編譯一次gcc, 但是將其編譯為shared library, 並有c++的支援, 至此就大功告成。

編譯gmp步驟如下:
1. 建立一個子目錄為 /usr/local/arm-linux-gnueabi/tools
2. 進入gmp的source code directory
3. 建立一個目錄為build-arm, 並進入該目錄
4. 執行命令如下:
CPPFLAGS=-fexceptions ../configure --prefix=/usr/local/arm-linux-gnueabi/tools --enable-cxx --with-gnu-ld
5. 執行make & make install

編譯mpfr步驟如下:
1. 進入mpfr的source code directory
2. 建立一個目錄為build-arm, 並進入該目錄
3. 執行命令如下:
../configure --prefix=/usr/local/arm-linux-gnueabi/tools --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld
4. 執行make & make install

編譯mpc步驟如下:
1. 進入mpc的source code directory
2. 建立一個目錄為build-arm, 並進入該目錄
3. 執行命令如下:
../configure --prefix=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools
4. 執行make & make install

編譯binutil的步驟如下:
1. 進入binutils的source code directory
2. 建立一個月錄build-arm, 並且進入該目錄
3. 執行命令如下:
../configure --prefix=/usr/local/arm-linux-gnueabi --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --enable-shared --disable-multilib --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-mpc=/usr/local/arm-linux-gnueabi/tools
3-1. 假如你要支援多種版本的library (little-endian & big-endian), 則所下的參數會是如下 :
../configure --prefix=/usr/local/arm-linux-gnueabi --build=i686-pc-linux --host=i686-pc-linux --target=arm-linux-gnueabi --enable-shared --enable-multilib --with-lib-path=/usr/local/arm-linux-gnueabi/lib:/usr/local/arm-linux-gnueabi/lib/be:/usr/local/arm-linux-gnueabi/lib/soft-float:/usr/local/arm-linux-gnueabi/lib/be/soft-float --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-mpc=/usr/local/arm-linux-gnueabi/tools
主要是多一個--with-lib-path的選項。
4. 之後執行make & make install

設定環境變數
這時需要先編譯一版使用static library的gcc版本, 再使用這版compiler去編譯glibc為shared library, 之後再回頭再編譯一次gcc為使用shared library, 在編譯之前需設定幾個環境變數如下 :
1. export LD_LIBRARY_PATH=/usr/local/arm-linux-gnueabi/tools/lib
2. export PATH=$PATH:/usr/local/arm-linux-gnueabi/bin
3. 若要支援多版本的編繹器則需要修改設定檔gcc/config/arm/t-linux-eabi,增加內容如下:

MULTILIB_OPTIONS = mlittle-endian/mbig-endian
MULTILIB_DIRNAMES = le be
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib

以下是編譯的流程 :
1. 進入GCC source code目錄並建立一個子目錄build-arm-static
2. 進入build-arm-static目錄, 執行以下指令
../configure --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --prefix=/usr/local/arm-linux-gnueabi --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --without-newlib --enable-long-long --disable-libada --without-cvs --disable-libgomp --disable-libmudflap --disable-libssp --disable-threads --enable-__cxa_atexit --disable-shared --enable-languages=c --with-system-zlib --with-mpc=/usr/local/arm-linux-gnueabi/tools --disable-multilib
2-1.若是要支援多版library則設定如下:
./configure --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --prefix=/usr/local/arm-linux-gnueabi --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --without-newlib --enable-long-long --disable-libada --without-cvs --disable-libgomp --disable-libmudflap --disable-libssp --disable-threads --enable-__cxa_atexit --disable-shared --enable-languages=c --with-system-zlib --with-mpc=/usr/local/arm-linux-gnueabi/tools --enable-multilib
3. 執行make & make install

編譯glibc
接下來是要編譯glibc是較麻煩的一件事, 在編譯glibc之前, 你必須使用kernel來產生一些header files因為在編譯glibc時需要用到, 以下是產生kernel header file的步驟:
1. 進入kernel source code目錄, 修改Makefile中的ARCH及CROSS_COMPILE變數如下 :
ARCH := arm
CROSS_COMPILE := arm-linux-gnueabi
INSTALL_MOD_PATH := `pwd`/installed_modules
1-1. 你若要產生多種版本的library (little endian & big endian), 那你必須更改兩個檔案 (glibc-source-code/Makeconfig & glibc-source-code/config.make.in), 以便在編譯完要按裝時可以得到適當的按裝, 其修改內容如下:
  • glibc-source-code/Makefconfig 第193行加入以下內容:
slibdir = $(libdir)
  • glibc-source-code/config.make.in mark掉第13行 #slibdir = @libc_cv_slibdir@, 加入一行如下:
slibdir = @libdir@
1-2. 在新版的glibc中gcc_eh並未準備好,所以無法使用,必須修Makeconfig檔以防止在編譯glibc時會連結gcc_eh, 你必須將該檔中的-lgcc_eh移除即。
2. 執行make menuconfig並選擇一種ARM平台, 之後將設定存下
3. 執行make headers_install以取得編譯glibc時要使用的kernel header files, 這時這些的include files將會被產生在kernel source code的目錄之下的usr/include目錄
4. 進入glibc的source code根目錄, 並建立一個目錄為build-arm-le, 並將glibc-ports-2.11.tar.bz2解壓縮的內容拷貝至glibc的目錄並設為ports的目錄
5. 進入build-arm-le的目錄, 並執行以下指令 :
CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ CPP="arm-linux-gnueabi-gcc -E" LD=arm-linux-gnueabi-ld STRIP=arm-linx-gnueabi-strip AR=arm-linux-gnueabi-ar NM=arm-linux-gnueabi-nm AS=arm-linux-gnueabi-as RANLIB=arm-linux-gnueabi-ranlib ARCH=arm ../configure --prefix=/usr/local/arm-linux-gnueabi --target=arm-linux-gnueabi --host=arm-linux-gnueabi --build=i686-pc-linux --disable-profile --enable-add-ons --with-__thread --enable-threads=posix --enable-multilib --without-cvs --enable-shared --without-gd --with-headers=/home/work/kernel/linux-2.6.35/usr/include --libdir=/usr/local/arm-linux-gnueabi/lib libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes
5-1.進入build-arm-be的目錄, 並執行以下指令:
CC="arm-linux-gnueabi-gcc -mbig-endian" CXX="arm-linux-gnueabi-g++ -mbig-endian" CPP="arm-linux-gnueabi-gcc -E -mbig-endian" LD="arm-linux-gnueabi-ld -EB" STRIP=arm-linx-gnueabi-strip AR=arm-linux-gnueabi-ar NM=arm-linux-gnueabi-nm AS="arm-linux-gnueabi-as -EB" RANLIB=arm-linux-gnueabi-ranlib ARCH=arm ../configure --prefix=/usr/local/arm-linux-gnueabi --target=arm-linux-gnueabi --host=arm-linux-gnueabi --build=i686-pc-linux --disable-profile --enable-add-ons --with-__thread --enable-threads=posix --enable-multilib --without-cvs --enable-shared --without-gd --with-headers=/home/work/kernel/linux-2.6.35/usr/include --libdir=/usr/local/arm-linux-gnueabi/lib/be libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes
6. 執行make & make install, 至此即可完成glibc的建立, 你將可以在/usr/local/arm-linux-gnueabi目錄下, 可以發現include & lib的目錄內容已包含glibc的內容
7.  將kernel/usr/include目錄之下的所有檔案拷貝至/usr/local/arm-linux-gnueabi/include目錄之下

編譯gcc for shared library
最後的動作則是要再重新編譯一次gcc使它支援shared library, 在重新編譯之前, 有幾個動作必須要完成, 以下是這些動作的內容:
1. 進入/usr/local/arm-linux-gnueabi/arm-linux-gnueabi的目錄, 特別注意是有兩層的arm-linux-gnueabi, 不可弄錯
2. 執行指令如下:
> mv lib lib.bak
> ln -s ../lib lib
> ln -s ../include include
> cp -a lib.bak/* ../lib
這時可以正式開始來重新編譯gcc, 其步驟如下:
1. 進入gcc source code目錄, 並建立一個目錄為build-arm-shared, 之後進入build-arm-shared目錄
2. 執行以下指令 :
../configure --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --prefix=/usr/local/arm-linux-gnueabi --with-arch=armv6 --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --without-newlib --enable-long-long --disable-libada --without-cvs --disable-libgomp --disable-libmudflap --disable-libssp --enable-threads --enable-__cxa_atexit --enable-shared --enable-languages=c,c++ --with-system-zlib --with-mpc=/usr/local/arm-linux-gnueabi/tools --disable-multilib
2-1.若要支援多版library執行指令如下:
../configure --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --prefix=/usr/local/arm-linux-gnueabi --with-arch=armv6 --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --without-newlib --enable-long-long --disable-libada --without-cvs --disable-libgomp --disable-libmudflap --disable-libssp --enable-threads --enable-__cxa_atexit --enable-shared --enable-languages=c,c++ --with-system-zlib --with-mpc=/usr/local/arm-linux-gnueabi/tools --enable-multilib
3. 執行make ; make install

至此就大功告成, 你可以寫一個簡單的hello程式來試試, 記得這是default share library的cross compiler, 所以你執行ap時也要記得將shared library也拷貝至embedded computer

最後有一個公共程式很好用,那就是 ldd 它可將應用程式及 library 所用到的 library 例出來,default 的 ldd 有點問題無法執行,這邊可以自行寫一個 shell script 檔名就是 arm-linux-gnueabi-ldd 其內容如下:

#!/bin/sh
arm-linux-gnueabi-objdump -x $@ | grep NEEDED

並將這執行放至 /usr/local/arm-linux-gnueabi/bin 目錄之下即可。

2 則留言:

  1. 您好~~我有個問題想請教
    為什麼最後要作這些動作呢?
    > mv lib lib.bak
    > ln -s lib ../lib
    > ln -s include ../include
    > cp -a lib.bak/* ../lib
    因為ln動作的意義有點怪怪的感覺

    回覆刪除
  2. 因為這是在做glibc和gcc時所使用到的目錄不同, 在arm-minux/arm-linux之下的include & lib通常是為kernel的相關檔案, 而在compile AP時原則上應是用不到才對

    回覆刪除