基于龍芯64位的嵌入式交叉編譯工具的構(gòu)建

摘要

以構(gòu)建mips嵌入式交叉編譯工具mipself-*為例,詳細(xì)說明了如何在Linux系統(tǒng)下使用newlib庫創(chuàng)建靈活配置的嵌入式交叉編譯工具的通用方法。該方法具有速度快方便移植的特點,可以用于創(chuàng)建arm,ppc以及mips64交叉編譯工具!

引言

隨著嵌入式電子產(chǎn)品的大量應(yīng)用以及Linux操作系統(tǒng)的廣泛使用,基于Linux操作系統(tǒng)的嵌入式開發(fā)環(huán)境已經(jīng)越來越多的企業(yè)、學(xué)校以及科研機(jī)構(gòu)中使用。交叉編譯器能夠在一個平臺上生成另一個平臺上的可執(zhí)行代碼,它是嵌入式開發(fā)環(huán)境中必不可少的工具。
目前,大多數(shù)嵌入式交叉編譯工具都基于glibc和Linux內(nèi)核構(gòu)建的ARCH-linux-(ARCH可以是ppc,mips或mips64)形式的交叉編譯器。這種交叉編譯器主要針對運(yùn)行Linux操作系統(tǒng)的機(jī)器,它依賴于指定的C語言庫glibc,且需要有MMU的支持,使得這種編譯器編譯出來的目標(biāo)文件占用較大空間。
本文構(gòu)建的交叉編譯工具鏈采用ARCH-elf-
。其特點是:

  1. 靈活性強(qiáng),ARCH-elf-*是一個獨立的編譯體系,不依賴于指定的C語言庫glibc,可以使用newlib等其他C語言庫,不需要操作系統(tǒng)的支持,可移植到很多CPU結(jié)構(gòu)上。
  2. 支持基于UCLinux的操作系統(tǒng),且不需要MMU支持,編譯出來的目標(biāo)文件占空間相對要少。
  3. 速度比ARCH-linux-*要快。

編譯前的準(zhǔn)備

cygwin環(huán)境的搭建

cygwin是一個在Windows平臺下運(yùn)行的Unix模擬環(huán)境,是cygnus solutions公司開發(fā)的自由軟件。他們寫了一個共享庫,也就是cygwin1.dll,把win32api中沒有的Unix風(fēng)格的調(diào)用封裝在里面。

也就是說,他們基于win32api寫了一個Unix系統(tǒng)庫的模擬層。這樣,只要把這些工具的源代碼和這個共享庫連接在一起,就可以使用Unix主機(jī)上的交叉編譯器來生成可以在Windows平臺上運(yùn)行的工具集。

以這些移植到Windows平臺上的開發(fā)工具為基礎(chǔ),cygnus又逐步把其他的工具軟件移植到Windows上來。我們也可以查看cygwin官網(wǎng),上面寫道:

cygwin 是:
提供在Windows上實現(xiàn)Linux類似功能的gnu和開源工具的龐大工具集。
一個DLL(cygwin1.dll)提供POSIX API功能的東西。

我們可以在cygwin官網(wǎng)https://www.cygwin.com/下載它的安裝程序 setup-x86.exe。

cygwin的安裝教程很多,需要注意的一點是,當(dāng)我們選擇一個download set的時候,我們需要選擇一個國內(nèi)的鏡像地址,否則下載將會很慢。我們可以選擇:http://www.cygwin.cn。如果找不到這個地址,也可以使用阿里云鏡像http://mirrors.aliyun.com/cygwin/選擇完成后,點擊“下一步”即可。

Cygwin5.png

最后,我們選擇需要下載安裝的組件包,為了使我們安裝的Cygwin能夠編譯程序,我們需要安裝gcc編譯器,默認(rèn)情況下,gcc并不會被安裝,我們需要選中它來安裝。為了安裝gcc,我們用鼠標(biāo)點開組件列表中的“Devel”分支,在該分支下,有很多組件,我們必須的是:

  • binutils
  • gcc
  • make
  • gdb

安裝完成以后,打開cygwin,如果可以在里面執(zhí)行命令make,gcc等,則說明cygwin環(huán)境已經(jīng)搭建成功。

源代碼準(zhǔn)備

宿主機(jī)是用來開發(fā)和調(diào)試嵌入式系統(tǒng)應(yīng)用程序的計算機(jī)系統(tǒng)。宿主機(jī)硬件可以用PC機(jī),配置linux操作系統(tǒng)。
Linux下的交叉編譯環(huán)境主要包括以下幾個重要部分:

  1. 針對目標(biāo)系統(tǒng)的二進(jìn)制工具binutils
  2. 針對目標(biāo)系統(tǒng)的動態(tài)編譯器gcc
  3. 針對目標(biāo)系統(tǒng)的標(biāo)準(zhǔn)C庫
    因此,在生成目標(biāo)機(jī)工具鏈的過程中,需要準(zhǔn)備如下源代碼:
  4. 用于生成目標(biāo)系統(tǒng)的二進(jìn)制工具集binutils
  5. 用于生成目標(biāo)系統(tǒng)的編譯器gcc源代碼,mpfr,gmp,mpc
  6. newlib源代碼
  7. make工具以及l(fā)inux內(nèi)核頭文件

準(zhǔn)備build目錄和安裝目錄

首先,將下載的源代碼放于/home/me/gnuu下并解壓源代碼文件,解壓后會生成binutils,gcc,newlib。然后建立相關(guān)目錄。


1.png

構(gòu)建過程

生成目標(biāo)二進(jìn)制工具

目標(biāo)系統(tǒng)的二進(jìn)制工具binutils是通過對源碼binutils--2.32進(jìn)行編譯和安裝而生成。生成的目標(biāo)系統(tǒng)二進(jìn)制工具,如ld、objdump等將安裝到/home/me/gnuu中。

修改binutils源碼

Bfd目錄下的config.bfd


2.png

Gas目錄下的configure


3.png

Gas目錄下的configure.ac


4.png

Ld目錄下的configure.tgt


5.png

什么是BFD

BFD是binary file descriptor library的簡稱,它的目標(biāo)是希望通過一種統(tǒng)一的接口來處理不同的目標(biāo)文件格式。

現(xiàn)在GCC(更具體地講是GNU匯編器GAS,GNU Assembler)、鏈接器ld、調(diào)試器GDB及binutils的其他工具都通過BFD庫來處理目標(biāo)文件,而不是直接操作目標(biāo)文件。這樣做最大的好處是將編譯器和鏈接器本身同具體的目標(biāo)文件格式隔離開來。

一旦我們需要支持一種新的目標(biāo)文件格式,只需要在BFD庫里面添加一種格式就可以了,而不需要修改編譯器和鏈接器。到目前為止,BFD庫支持大約25種處理器平臺,將近50種目標(biāo)文件格式。

我們利用objcopy(操作),objdump(讀取、分析),readelf(讀取),就是全仰仗BFD的功能。

什么是ABI

我們很熟悉API,如果我們想使用某些庫或操作系統(tǒng)的功能,將使用API。API由數(shù)據(jù)類型/結(jié)構(gòu),常量,函數(shù)等組成,您可以在代碼中使用它們來訪問該外部組件的功能。比如POSIX接口,就有一套它遵循的API標(biāo)準(zhǔn)。當(dāng)我們要使用一個POSIX接口,我們可以從其頭文件知道其接口名,以及參數(shù)類型和返回值類型,這些都是確定好的,我們使用時只能遵循它,而不能改變它。

ABI非常相似,可以將其視為API的編譯版本(或作為機(jī)器語言級別的API)。編寫源代碼時,可以通過API訪問庫。編譯代碼后,您的應(yīng)用程序?qū)⑼ㄟ^ABI訪問庫中的二進(jìn)制數(shù)據(jù)。ABI定義了編譯的應(yīng)用程序?qū)⒂糜谠L問外部庫的結(jié)構(gòu)和方法(就像API一樣),僅在較低級別上。

有時,ABI的變化是不可避免的。發(fā)生這種情況時,任何使用該庫的程序都將無法運(yùn)行,除非重新編譯它們以使用新版本的庫。如果ABI發(fā)生變化但API沒有變化,則新舊庫版本有時稱為“源兼容”。

ABI是一系列約定的集合,例如GNU/Linux約定函數(shù)調(diào)用的頭六個整型參數(shù)放在寄存器RDI, RSI, RDX, RCX, R8和R9上;其他額外的參數(shù)推入棧,返回值保存在RAX中??梢哉f調(diào)用慣例(calling convention)就是ABI。因此,ABI是和具體CPU架構(gòu)和OS相關(guān)的。

舉個例子:

  • o32與n64即純粹的32位與64位模式,二者除指針與變量類型的長度差異外,n64還用寄存器來傳遞更多的參數(shù),性能有所提高。
  • n32則是32位數(shù)據(jù)結(jié)構(gòu)和64位指令的結(jié)合體。MIPS N32 ABI在保留 MIPS N64 ABI 的幾乎所有特性的情況下(主要是寄存器和堆棧中的函數(shù)參數(shù)傳遞約定),重點在于僅將long long與double類型編譯為64位,其余指針與變量類型設(shè)定與o32相同(例如,指針和long int是32位的),因此更接近MIPS N64 ABI。

在binbuild目錄下執(zhí)行

sudo ../binutils-2.32/configure --prefix=/home/me/gnuu/bininstall --target=mips-loongson-elf  --disable-nls --disable-werror --enable-shared --enable-threads=posix --enable-interwork --enable-multilib --enable-languages=c,c++,fortran
make
make install

通過源碼編譯編譯器,我們就會使用到Configure腳本配置工具,它是autoconf的工具的基本應(yīng)用。

'configure'腳本有大量的命令行選項。對不同的軟件包來說,這些選項可能會有變化,但是許多基本的選項是不會改變的。帶上'--help'選項執(zhí)行'configure'腳本可以看到可用的所有選項。盡管許多選項是很少用到的,但是當(dāng)你為了特殊的需求而configure一個包時,知道他們的存在是很有益處的。

--build=BUILD

指定軟件包安裝的系統(tǒng)平臺。如果沒有指定,默認(rèn)值將是'--host'選項的值。

--host=HOST

指定軟件運(yùn)行的系統(tǒng)平臺。如果沒有指定。將會運(yùn)行`config.guess'來檢測。

--target=GARGET

指定軟件面向(target to)的系統(tǒng)平臺。這主要在程序語言工具如編譯器和匯編器上下文中起作用。如果沒有指定,默認(rèn)將使用'--host'選項的值。

--prefix=PEWFIX

'--prefix'是最常用的選項。制作出的'Makefile'會查看隨此選項傳遞的參數(shù),當(dāng)一個包在安裝時可以徹底的重新安置他的結(jié)構(gòu)獨立部分。

target選項用來指定目標(biāo)板的類型。由于本次編譯采用了newlib,且生成的編譯器可以獨立運(yùn)行,不需要linux系統(tǒng)支持,因此,目標(biāo)板的類型設(shè)置為mips-elf。如果需要構(gòu)建其它類型的目標(biāo)板,只需要用其它目標(biāo)類型替換mips即可,如arm、ppc等。Prefix選項用來指定安裝目錄。完成安裝后在/home/me/gnuu/bininstall/bin生成如下二進(jìn)制工具:


6.png

生成靜態(tài)庫編譯器

靜態(tài)庫編譯器也稱自舉編譯器bootstrap compiler。因為這時并沒有mips的C語言庫可使用,所以只能先生成一個簡單的mips-elf-gcc編譯器,該編譯器只支持C語言的編譯。用該靜態(tài)編譯器對C語言庫進(jìn)行編譯后,再重新對編譯器進(jìn)行編譯,從而生成完整的交叉編譯工具。

編譯依賴庫

編譯gcc期間可能會存在軟件的依賴,因此需提前下載好gmp,mpfr,mpc庫,下載方法非常簡單。在/home/me/gnuu/gcc-7.4.0/contrib中,存在download_prerequisites這個腳本文件,執(zhí)行它,會自動安裝相關(guān)依賴庫。


xxx.png

在gccbuild目錄下執(zhí)行

sudo ../gcc-7.4.0/configure --target=mips-loongson-elf --enable-threads --disable-libmudflap --disable-libssp --disable-libstdcxx-pch --disable-hosted-libstdcxx --enable-version-specific-runtime-libs --disable-sjlj-exceptions --disable-symvers --enable-__cxa_atexit --disable-fixed-point --disable-decimal-float --enable-interwork --enable-multilib --with-gnu-as --with-gnu-ld  --with-newlib --enable-languages=c,c++ --enable-shared --disable-lto --disable-hosted-libstdcxx --prefix=/home/me/gnuu/gccinstall --with-gxx-include-dir=''\''/'\''include/c++/7.4' --disable-libgomp --disable-libitm --with-build-time-tools=/home/me/gnuu/bininstall/mips-loongson-elf/bin
make all-gcc                 //編譯靜態(tài)gcc
make install-gcc              //安裝靜態(tài)gcc

由于這里還沒有對庫文件進(jìn)行編譯,且對庫文件編譯時需要靜態(tài)編譯工具mips-elf-gcc;因此需要對gcc進(jìn)行編譯時可以不用with-header的選項。但是,為了下步進(jìn)行動態(tài)編譯時不在重新配置gcc,這里將動態(tài)配置時的選項增加進(jìn)來。其中,with-header指定的頭文件是newlib標(biāo)準(zhǔn)庫中的頭文件,而不再使用基于linux的C標(biāo)準(zhǔn)庫文件。Enable-language選項用于指定生成的交叉編譯工具對C和C++編程語言的支持。完成安裝后在/home/me/gnuu/gccinstall/bin中:


7.png

利用靜態(tài)編譯工具編譯嵌入式庫文件

本文主要采用newlib庫取代glibc庫,從而生成一個不依賴于linux操作系統(tǒng)的靈活、方便的編譯器,對newlib的編譯主要是使用靜態(tài)編譯器對newlib進(jìn)行編譯,從而生成支持交叉編譯器的動態(tài)鏈接庫:

sudo ../newlib-3.1.0/configure --prefix=/home/me/gnuu/newinstall --target=mips-loongson-elf  --disable-nls --disable-werror --enable-shared --enable-threads=posix --enable-interwork --enable-multilib --enable-languages=c,c++,fortran
make
make install

生成交叉編譯工具鏈

在生成靜態(tài)gcc時,已經(jīng)添加了所有用到的選項,如使用newlib中的頭文件。生成對C和C++支持編譯器等。因此,只需要對gcc直接進(jìn)行編譯即可,無需重新配置各編譯選項。該步驟完成后,會在install目錄下生成完整的嵌入式交叉編譯工具。

驗證工作

生成好改交叉編譯鏈以后,我們使用它來驗證其正確性:

./mips-loongson-elf-gcc -EL -mabi=64 -c test.c -o test.o
./mips-loongson-elf-gcc -EL -mabi=64 -c sub.c -o sub.o
./mips-loongson-elf-ld -r -T link.OUT test.o sub.o -o test.out
./mips-loongson-elf-objdump -t -x -h test.out > test.txt

通過objdump反匯編來看,是符合要求的:


8.png

注意事項

ar命令

因為,此次編譯的工具鏈,支持的abi有o32、n32,以及64,因此在編譯鏈接以及ar命令的時候一定要通過參數(shù)-mabi=xx,來指定其abi接口類型。

比如,當(dāng)ar的時候沒有指定abi的時候,報的錯:

error adding symbols: archive has no index; run ranlib to add one

究其原因,其實是ar的時候,需要指定目標(biāo)板具體的體系結(jié)構(gòu),--target=elf32-nlittlemips
否則,ar會以默認(rèn)的target來打包程序,而鏈接的時候因為制定了abi為n32,則導(dǎo)致找不到靜態(tài)庫的符號索引表。

鏈接腳本

使用新的gcc時,需要修改鏈接腳本,在其中添加:
OUTPUT_FORMAT("elf32-nlittlemips")
OUTPUT_ARCH(mips)
ENTRY(__start)
用以指定輸出的elf格式和體系結(jié)構(gòu),以及起始函數(shù)。

結(jié)束語

通過ARCH-elf-*搭建跨平臺mips開發(fā)工具鏈的實例,說明了如何使用newlib庫構(gòu)建靈活配置的嵌入式開發(fā)平臺,從而使嵌入式開發(fā)人員可以直接在PC機(jī)上開發(fā)基于嵌入式結(jié)構(gòu)的應(yīng)用程序。
當(dāng)我們需要開發(fā)基于VxWorks庫的嵌入式開發(fā)平臺,可以以至進(jìn)行參考。無非就是target變了,binutils里面的configure等文件做好相應(yīng)更改就可以了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容