建立ARM交叉编译环境 (arm-none-linux-gnueabi-gcc with EABI)
昨天終于把交叉編譯環境、移植內核和制作root文件系統在arm開發板上順利跑通了。期間有的步驟很順利,但更多的是被諸多問題困擾,比如最后一個不起眼的小問題導致文件系統無法加載,郁悶了我一個星期,最終通過分析慢慢發現了這個bug。還有各源碼包版本的問題,而且網上很多介紹都是基于舊版本的。我這里全部用最新或較新的版本,當然,至于新舊版本到底哪個更好更合適,這是個智者見智的論題,不在本文討論之列。我堅信很多人也遇到過或者即將遇到我曾經歷過的錯誤和問題,因此我覺得把我過去兩周做過的相關工作,詳細地寫下來,希望對大家有所幫助!
?
首先是平臺和環境?,我過去兩周都是基于vmware?中ubuntu 10.04的,但是我發現ubuntu這個很火的桌面linux發行版本并不適合進行嵌入式開發,典型的麻煩就是系統缺少很多庫、服務等等,需要自己手動安裝,增加了不少額外的工作,和我以前用的SuSe 9比麻煩不少。不過所以現在我干脆全新裝了一個SUSE 11.2?Enterprise Server 32bit,把過去的工作重復一遍,邊編譯邊紀錄,力求不遺漏細節!
?
過程預覽:
1,準備工作,包括下載源碼包、補丁、建立文件夾和設置環境變量等
2,建立內核頭文件
3,建立binutils
4,建立bootstrap gcc
5,建立glibc
6,建立完整版本gcc
7,測試hello world
?
現在就讓我們開始吧!??
?
1 準備工作
我使用的源碼包和補丁?如下:
linux-2.6.34.tar.bz2
binutils-2.20.tar.gz
gcc-4.3.5.tar.bz2
glibc-2.11.tar.gz
glibc-linuxthreads-2.5.tar.bz2
glibc-ports-2.11.tar.bz2
glibc-2.11.2-gcc_fix-1.patch
至于怎么得到這些源碼包,找google吧!
?
建立工作目錄?
自己選一個合適的地方,建立一個總文件夾Embedded,并且在其下建立 build-tools、kernel和tools三個子文件夾、我們以后的操作就都在這里進行了。
$ mkdir Embedded
$ cd Embedded
$ mkdir build-tools ?? kernel ? ? tool
$ ls
build-tools ?? kernel ? ? tool
?
各文件夾作用如下:
build-tools : 保存binutils、gcc 和 glibc的源代碼和用來編譯這些源代碼的目錄。
kernel??????? : 保存內核源代碼和補丁。
tools????????? : 保存編譯好的交叉編譯工具和庫文件。
然后在build-tools文件夾中建立如下子文件夾:
?
$ cd?build-tools?
$ mkdir?build-binutils???build-boot-gcc???build-glibc??build-gcc?
build-binutils??? :編譯binutils的目錄
build-boot-gcc : 編譯gcc 啟動部分的目錄
build-glibc??????? :編譯glibc的目錄
build-gcc????????? :編譯完整gcc的目錄
?
設置環境變量:
這里設置環境變量只是為了方便,因為每個工具的config都需要輸入類似的變量,不如放在環境變量里。
在命令行下打開vi? ~/.bashrc,在文檔最后輸入下面幾行,然后注銷當前用戶,重新登錄
export PRJROOT=/home/jinglelong/MySoftware/Embedded
export TARGET=arm-none-linux-gnueabi
export PREFIX=$PRJROOT/tools
export TARGET_PREFIX=$PREFIX/$TARGET
export PATH=$PREFIX/bin:$PATH
?
?
各變量的具體意義如下:
PRJROOT???????????????????? : 整個工程的根目錄,這里當然是Embeded了
TARGET???????????????????????: 目標文件對應的體系結構,arm-linux代表編譯出來的target只能在arm體系結構中運行
PREFIX????????????????????????: 設置目標文件夾的路徑前綴
TARGET_PREFIX????????? : 設置目標文件夾的路徑前綴路徑
PATH???????????????????????????: 添加可執行文件的路徑,這里主要是只中間編譯工具等
?
?
2 建立內核include文件
$ ln -s? /home/jinglelong/MySoftware/Embedded/kernel/linux-2.6.34/include/linux? $TARGET_PREFIX/include/linux?
$ ln -s? /home/jinglelong/MySoftware/Embedded/kernel/linux-2.6.34/include/asm-generic/????? $TARGET_PREFIX/include/asm-generic?
$ ln -s? /home/jinglelong/MySoftware/Embedded/kernel/linux-2.6.34/arch/arm/include/asm/? $TARGET_PREFIX/include/asm?
編譯生成version頭文件
這個是編譯glibc時必須的,使用命令:make include/linux/version.h
?
?
3 建立binutils
解壓binutils源碼到文件夾: $PRJROOT/build-tools/binutils-2.20
?
配置:
cd $PRJROOT/build-tools/build-binutils
$ ../binutils-2.20/configure --target=$TARGET --prefix=$PREFIX
編譯:make
出錯:
../../binutils-2.20/gas/config/tc-arm.c: In function ‘make_mapping_symbol’:
../../binutils-2.20/gas/config/tc-arm.c:2489: error: suggest braces around empty body in an ‘if’ statement
打開文件binutils-2.20/gas/config/tc-arm.c,把2490行的語句,用一對大括號括起來就可以了
?
安裝: make install
完成后檢查一下$PREFIX文件夾,是不是多了三個子文件夾,bin, lib, share? 打開bin,發現里面生成了14個可執行文件:
?
[root@localhost bin]# ls
arm-none-linux-gnueabi-addr2line? arm-none-linux-gnueabi-as???arm-none-linux-gnueabi-gprof? arm-none-linux-gnueabi-nm?????? arm-none-linux-gnueabi-objdump? arm-none-linux-gnueabi-readelf? arm-none-linux-gnueabi-strings arm-none-linux-gnueabi-ar???????? arm-none-linux-gnueabi-c++filt? arm-none-linux-gnueabi-ld???? arm-none-linux-gnueabi-objcopy? arm-none-linux-gnueabi-ranlib?? arm-none-linux-gnueabi-size???? arm-none-linux-gnueabi-strip
他們的功能分別是:
add2line???????? :將你要找的地址轉成文件和行號,它要使用 debug 信息。
ar??????????????????:產生、修改和解開一個存檔文件
as??????????????????:gnu的匯編器
c++filt????????????:C++ 和 java 中有一種重載函數,所用的重載函數最后會被編譯轉化成匯編的標,c++filt 就是實現這種反向的轉化,根據標號得到函數名。
gprof ???????????? :gnu 匯編器預編譯器。
ld???????????????????:gnu 的連接器
nm???????????????? :列出目標文件的符號和對應的地址
objcopy?????????? :將某種格式的目標文件轉化成另外格式的目標文件
objdump????????? :顯示目標文件的信息
ranlib? ??????????? :為一個存檔文件產生一個索引,并將這個索引存入存檔文件中
readelf??????? ??? :顯示 elf 格式的目標文件的信息
size??????????????? :顯示目標文件各個節的大小和目標文件的大小
strings??????????? :打印出目標文件中可以打印的字符串,有個默認的長度,為4
strip??????????????? :剝掉目標文件的所有的符號信息
?
4 建立bootstrap gcc
首先,我們為什么要建立bootstrap gcc,而不能一次性成功?原因有兩點:
?
一是由于平臺本身的gcc編譯器和我們要建立的gcc版本不同,第一次用平臺本身的編譯器去build目標版本的gcc編譯器的時候,新生成的目標編譯器(相當于初始編譯器編譯鏈接生成的可執行文件)必然帶有初始編譯器的特征。而當我們用新生成的編譯器再次編譯自身時,便可去掉這種差異性。
?
二是因為gcc編譯器依賴于glibc,而當前我們的glibc是基于本機的,所以我們首先要build基于arm體系結構的glibc,再在glibc的基礎上生成基于arm體系結構的gcc。
?
這一步是最容易出錯的,對每一步都必須謹慎,不要犯粗心之類的低級錯誤。
?
解壓源碼
解壓gcc源碼到build-tool文件夾下
?
修改源碼:
gcc-4.3.5
CRTSTUFF_T_CFLAGS_S = $(CRTSTUFF_T_CFLAGS) -fPIC -Dinhibit_libc -D__gthr_posix_h
確保本機已經安裝了mpc, mpfr, gmp, 如果沒有,則在yast里面安裝好再往后走。
?
配置:
../gcc-4.3.5/configure --target=$TARGET --prefix=$PREFIX --without-headers --enable-languages=c --disable-threads --with-newlib --disable-shared --disable-libmudflap --disable-libssp
?
編譯:make all-gcc
安裝gcc: make install?
再編譯安裝libgcc,這個是后面編譯glibc必須的。
編譯:make all-target-libgcc
安裝libgcc: make install-target-libgcc
?
我看到網上很多文章在這一步有很多錯誤,一種是直接用make命令編譯gcc下所有內容,這個是沒有必要的,而且容易出錯。我在ubuntu和suse下都無法完成編譯,而在fedora下通過了;第二種情況是沒有編譯libgcc,這會導致后面編譯glibc無法通過。
?
安裝完成后,在$PREFIX/bin下又多了幾個文件,
arm-none-linux-gnueabi-cpp?????????: gnu的 C 的預編譯器
arm-none-linux-gnueabi-gcc?????????: gnu的 C 語言編譯器
arm-none-linux-gnueabi-gcc-4.3.5 : gnu的 C 語言編譯器,其實和arm-linux-gcc是一樣的
arm-none-linux-gnueabi-gccbug??? :? 一個可執行腳本,具體作用未知。
arm-none-linux-gnueabi-gcov??????? : gcc 的輔助測試工具,用來分析和優化程序
?
?
5 建立glibc?
解壓源碼:
把glibc源碼解壓到build-tool下,把glibc-linuxthreads-2.5.tar.bz2解壓到glibc根目錄下,把glibc-ports-2.11.tar.bz2解壓到glibc根目錄下,并且命名為ports
?
進入文件夾build-glibc,創建config.cache文件,并且在文件中輸入以下內容
libc_cv_forced_unwind=yes
libc_cv_c_cleanup=yes
libc_cv_arm_tls=yes
?
配置:
BUILD_CC="gcc" CC=$TARGET-gcc ../glibc-2.11/configure --host=$TARGET --target=$TARGET --prefix=/usr --enable-add-ons --disable-profile --cache-file=config.cache --with-binutils=$PREFIX/bin/ --with-headers=$TARGET_PREFIX/include/
?
編譯:make
出錯:/arm-linux/bin/ld: cannot find -lgcc_eh
打開glibc根目錄下Makeconfig文件,去掉第541,546行中的-lgcc_eh,重新make
?
安裝:
make install_root=$TARGET_PREFIX prefix="" install
?
修改libc.so:
用vi或gedit打開libc.so文件,將文件中的:
GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a? AS_NEEDED ( /lib/ld-linux.so.2 ) )?
更改為
GROUP ( libc.so.6 libc_nonshared.a )
保存后退出
?
?
6 建立完整版gcc
有了前面的經驗,現在就簡單多了,進入目錄build-gcc,
?
配置:
../gcc-4.3.5/configure --target=$TARGET --prefix=$PREFIX --enable-languages=c,c++ --disable-libgomp
?
編譯:
make all
?
安裝:?
make install
?
安裝完成后,在$PREFIX/bin下多了gnu的c++編譯器:
arm-none-linux-gnueabi-gcc
arm-none-linux-gnueabi-c++??
??
?
?
7 驗證工具鏈
創建,編譯生成一個hello world程序helloworld, 查看elf文件信息:
$? arm-none-linux-gnueabi-readelf? -d helloworld
是不是看到了ARM的信息?更直接的,就是把這個helloworld和相關依賴的動態庫拷到開發板上,看它是不是真的能helloworld!
?
8 總結
這次在SUSE 11.2上編譯安裝工具鏈,整個過程非常順利,其實我相信只要環境,配置等正確,常見linux發行版上都會比較順利。不過我還是建議直接下在編譯好的工具鏈,省下了不少麻煩,而且可靠性也能保證。最后,希望本教程對大家有所幫助,如果有什么遺漏或錯誤之處,希望大家能批評指正!
?
?
參考文獻:
1,?如何為嵌入式開發建立交叉編譯環境:?http://www.ibm.com/developerworks/cn/linux/l-embcmpl/?
?
?
?自http://blog.csdn.net/wyjkk/article/details/5792048
總結
以上是生活随笔為你收集整理的建立ARM交叉编译环境 (arm-none-linux-gnueabi-gcc with EABI)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu10.4安装交叉编译器arm
- 下一篇: crosstool-ng 编译交叉工具链