天天看點

TQ2440開發闆學習紀實(0.0)--- GNU ARM交叉彙編環境的搭建與測試1 binutils簡介2 binutils安裝2.1 準備binutils源碼2.2 配置,編譯,安裝3 目标機器是裸機下的彙程式設計式開發4 目标機器是Linux系統下的彙程式設計式開發

對ARM進行裸機開發時,彙編是必不可少的,因為C語言無法直接操作CPU的内置寄存器,也就無法完成很多硬體初始化的功能,如記憶體控制器的初始化。

市面上大多數講解ARM彙編開發的書籍都把ADS作為開發環境,是以使用的彙編語言也就是ARMASM,開發平台也限制到了Windows。然而在嵌入式領域,Linux作為開發環境更加普及,那麼如何在Linux下進行ARM的彙程式設計式設計呢?

其實,Linux平台上早就有了支援ARM的彙編器,那就是著名的binutils軟體包,它包含了彙編、連結、二進制操作等所有的ARM彙編環境。

1 binutils簡介

binutils是支援Unix類系統彙編與連結開源軟體包,提供彙編、連結等二進制檔案操作工具。其官方首頁是: http://www.gnu.org/software/binutils。著名的GCC編譯器本身并不提供彙編和連結功能,而是依賴于這個binutils才能完成進階語言的編譯、彙編、連結過程。它提供的工具包括:

* GNU連結器ld

* GNU彙編器as

* objcopy 轉換二進制格式

* ar 對象檔案打包管理

* c++filt C++符号名字反mangling

* nm 列印二進制檔案裡的符号資訊

* readelf 讀取分析elf格式檔案資訊

* ranlib 産生archive包的索引

* size 列出二進制檔案的大小

* objdump 列印二進制檔案資訊

* strip 去除二進制檔案中的符号資訊

* strings 列印二進制檔案中的可列印符号

* dlltool 為建構和使用DLL建立檔案

* gprof 顯示配置資訊

* addr2line 把記憶體位址對應到源檔案中的行号(用于調試)

* nlmconv 轉為對象代碼為NLM

* gold 隻用于ELF格式的新生代連接配接器(尚處于測試階段)

對于ARM彙程式設計式開發,as,ld,objcopy是最基本的工具。

2 binutils安裝

對于ARM彙編來說,最常見的就是開發平台是基于X86的LinuxPC機器,而運作平台則是基于ARM的嵌入式硬體平台。這就需要一個能生成ARM機器碼的交叉彙編器和連接配接器。binutils自然能夠滿足這個小小的要求,隻是需要進行編譯安裝就可以了。

2.1 準備binutils源碼

去其官網下載下傳最新的源碼包,截至目前(2016年12月12日)最新的版本為2.27,下載下傳得到的檔案名為: binutils-2.27.tar.bz2。

解壓

tar -jxvf binutils-.tar.bz2
           

得到binutils-2.27檔案夾。

不建議直接在binutils-2.27這個源檔案目錄下直接配置編譯,以免造成混亂。為此,建立一個空目錄build-binutils,用于編譯。

2.2 配置,編譯,安裝

進入build-binutils目錄,執行如下配置指令:

上面指定了binutils的安裝目錄與目标平台,運作完成會在目前目錄下生成Makefile。執行make,make install完成安裝

make
make install
           

與GCC不同,binutils的配置、編譯、安裝非常簡單,很少會出錯。安裝完成後會産生如下目錄結構:

ARM
├── arm-linux-gnueabihf
│   ├── bin
│   │   ├── ar
│   │   ├── as
│   │   ├── ld
│   │   ├── ld.bfd
│   │   ├── nm
│   │   ├── objcopy
│   │   ├── objdump
│   │   ├── ranlib
│   │   ├── readelf
│   │   └── strip
│   └── lib
│       └── ldscripts
│         
├── bin
│   ├── arm-linux-gnueabihf-addr2line
│   ├── arm-linux-gnueabihf-ar
│   ├── arm-linux-gnueabihf-as
│   ├── arm-linux-gnueabihf-c++filt
│   ├── arm-linux-gnueabihf-elfedit
│   ├── arm-linux-gnueabihf-gprof
│   ├── arm-linux-gnueabihf-ld
│   ├── arm-linux-gnueabihf-ld.bfd
│   ├── arm-linux-gnueabihf-nm
│   ├── arm-linux-gnueabihf-objcopy
│   ├── arm-linux-gnueabihf-objdump
│   ├── arm-linux-gnueabihf-ranlib
│   ├── arm-linux-gnueabihf-readelf
│   ├── arm-linux-gnueabihf-size
│   ├── arm-linux-gnueabihf-strings
│   └── arm-linux-gnueabihf-strip
└── share
    └── man
        └── man1

           

其中ARM/bin和ARM/arm-linux-gnueabihf/bin目錄下存放的就是as,ld等二進制工具。實際上這兩個目錄裡的檔案是完全一樣的,隻是名字不同而已。

[smstong@centos192 ~]$ ls ARM/arm-linux-gnueabihf/bin -i
 ar   ld       nm        objdump   readelf
 as   ld.bfd   objcopy   ranlib    strip
[smstong@centos192 ~]$ ls ARM/bin/ -i
 arm-linux-gnueabihf-addr2line   arm-linux-gnueabihf-nm
 arm-linux-gnueabihf-ar          arm-linux-gnueabihf-objcopy
 arm-linux-gnueabihf-as          arm-linux-gnueabihf-objdump
 arm-linux-gnueabihf-c++filt     arm-linux-gnueabihf-ranlib
 arm-linux-gnueabihf-elfedit     arm-linux-gnueabihf-readelf
 arm-linux-gnueabihf-gprof       arm-linux-gnueabihf-size
 arm-linux-gnueabihf-ld          arm-linux-gnueabihf-strings
 arm-linux-gnueabihf-ld.bfd      arm-linux-gnueabihf-strip
           

可見,ARM/arm-linux-gnueabihf/bin/as 和 ARM/bin/arm-linux-gnueabihf-as共享同一個索引節點号,是彼此的硬連結。為了與開發主機上的本地binutils相差別,建議使用長檔案名的那個硬連結。為此,把它們加入可執行路徑:

這樣我們就可以直接使用arm-linux-gnueabihf-as指令了。

3 目标機器是裸機下的彙程式設計式開發

下面直接給出一個具體的例子。

3.1 實驗環境

本次實驗的運作平台為TQ2440開發闆。從Norflash啟動硬體,并且Norflash中已經預裝了u-boot,且u-boot支援通過tftp協定下載下傳程式到指定記憶體位址執行。

開發主機安裝運作了tftp伺服器,檔案根目錄為/var/lib/tftpboot,編譯生成的二進制檔案led.bin被複制到此目錄下。

這就是說,測試程式執行前,u-boot已經完成了硬體平台的基本初始化:SDRAM可用,堆棧環境已經設定,MMU未啟用,是以虛拟位址和實體位址完全相同。

這樣我們的測試程式就可以做的非常簡單。

本次實驗的結果是熄滅底闆上的LED1。因為u-boot啟動後,LED1被自動點亮,我們的測試程式用來熄滅它。

3.2 源碼

源檔案 led.s。

我們都知道,GNU的X86彙編指令與Intel文檔提供的标準格式相差很大,而這種情況在ARM彙編中不再存在,

GNU ARM AS支援ARM公司提供的标準彙編指令格式,而且擴充了一些特有的僞指令。

.equ GPBCON, 
.equ GPBDAT, 
.equ GPBUP,  
.equ UBOOT,  

.section .text
    .global _start
_start:
    ldr r0, =GPBCON
    ldr r1, [r0]
    bic r1, r1, #0xC00
    orr r1, r1, #0x400
    str r1, [r0]

    ldr r0, =GPBUP
    ldr r1, [r0]
    bic r1, r1, #0x20
    str r1, [r0]

    ldr r0, =GPBDAT
    ldr r1, [r0]
    orr r1, r1, #0x20
    str r1, [r0]

    b UBOOT /* 跳轉回Norflash,重新進入u-boot */

.end
           

連結腳檔案 led.lds:

ENTRY(_start)

SECTIONS {
    . = ;
    .text : {       /* text and : must be seperated by space */
        *(.text)
        *(.rodata)
    }
    .data ALIGN(): {
        *(.data)
    }
    .bss ALIGN(): {
        *(.bss)
    }
}
           

Makefile檔案:

AS = arm-linux-gnueabihf-as
LD = arm-linux-gnueabihf-ld
OBJCPY = arm-linux-gnueabihf-objcopy

all: led.bin
    sudo cp led.bin /var/lib/tftpboot/

led.bin: led
    $(OBJCPY) -O binary $< [email protected]

led: led.o
    $(LD)  --script=led.lds -o [email protected] $<

led.o: led.s
    $(AS) -o [email protected] $<

.PHONY: clean
clean:
    rm *.o led led.bin
           

3.3 編譯連結說明

  • 連結時需要指定代碼段的起始記憶體位址為0x30000000,以滿足TQ2440開發闆的記憶體布局,這通過連結腳本led.lds來完成。
  • 連結器生産的可執行程式led為elf格式,這是Linux作業系統下的标準可執行格式,需要作業系統提供的加載器才能加載執行,在開發闆裸機上不能直接運作,這就需要通過objcopy工具把elf格式轉化為單純的二進制格式led.bin。

整個轉換彙編連結過程:

led.s---(彙編器as)-->led.o---(連接配接器ld)-->led---(objcopy工具)--->led.bin
           

3.4 下載下傳到開發闆執行

#####    Boot for Nor Flash Main Menu   #####
#####     EmbedSky USB download mode     #####

[] Download u-boot or other bootloader to Nand Flash
[] Download Eboot (eboot.nb0) to Nand Flash
[] Download Linux Kernel (zImage.bin) to Nand Flash
[] Download WinCE NK.bin to Nand Flash
[] Download CRAMFS image to Nand Flash
[] Download YAFFS image (root.bin) to Nand Flash
[] Download Program (uCOS-II or TQ2440_Test) to SDRAM and Run it
[] Boot the system
[] Format the Nand Flash
[] Set the boot parameters
[a] Download User Program (eg: uCOS-II or TQ2440_Test)
[b] Download LOGO Picture (.bin) to Nand  Flash
[l] Set LCD Parameters
[n] Enter TFTP download mode menu
[o] Download u-boot to Nor Flash
[r] Reboot u-boot
[t] Test Linux Image (zImage)
[q] quit from menu
Enter your selection: n

#####    Boot for Nor Flash Main Menu   #####
#####     EmbedSky TFTP download mode     #####

[] Download u-boot.bin to Nand Flash
[] Download Eboot (eboot.nb0) to Nand Flash
[] Download Linux Kernel (zImage.bin) to Nand Flash
[] Download WinCE NK.bin to Nand Flash
[] Set TFTP parameters(PC IP,TQ2440 IP,Mask IP...)
[] Download YAFFS image (root.bin) to Nand Flash
[] Download Program (uCOS-II or TQ2440_Test) to SDRAM and Run it
[] Boot the system
[] Format the Nand Flash
[] Set the boot parameters
[a] Download User Program (eg: uCOS-II or TQ2440_Test)
[b] Download LOGO Picture (.bin) to Nand  Flash
[l] Set LCD Parameters
[o] Download u-boot to Nor Flash
[p] Test network (TQ2440 Ping PC's IP)
[r] Reboot u-boot
[t] Test Linux Image (zImage)
[q] Return main Menu
Enter your selection: 
Enter downloads to SDRAM address:

Enter program name:
led.bin
tftp  led.bin
dm9000 i/o: , id: 
MAC: a:b:c:d:e:f
TFTP from server ; our IP address is 
Filename 'led.bin'.
Load address: 
Loading: #
done
Bytes transferred =  ( hex)
## Starting application at  ...
           

可以看到開發闆上的LED1燈已經被熄滅了。

4 目标機器是Linux系統下的彙程式設計式開發

彙程式設計式當然也适合在Linux系統下運作,而且有了OS的支援,彙程式設計式可以通過系統調用的方式非常爽的使用OS提供的API。下面給出一個hello world的例子。

4.1 源碼

源碼非常簡單hello.s:

.section .data
msg:
    .asciz "hello,GNU ARM ASM\n"

.section .text
    .global _start
_start:
    mov r0,#1       /* file fd */
    ldr r1, =msg
    mov r2,#18
    swi #0x900004   /* sys_write(fd,msg,len) */

    mov r0,#0
    swi #0x900001   /* sys_exit(code) */
           

Makefile:

AS = arm-linux-gnueabihf-as
LD = arm-linux-gnueabihf-ld
all: hello
    sudo cp hello /var/lib/tftpboot/
hello: hello.o
    $(LD) -o [email protected] $<

hello.o: hello.s
    $(AS) -o [email protected] $<

.PHONY: clean
clean:
    rm *.o hello
           

4.2 編譯連結說明

預設情況下連結器會把_start作為入口,代碼段基位址預設為0x00010074。因為開發闆運作的是Linux系統,是以會把這個虛拟位址轉換為可用的實體位址。

通過swi軟中斷方式調用Linux核心API,很輕易就實作了列印字元串的功能。在OS下開發程式是多麼幸福的事情!

4.3 系統調用還是标準C庫?

彙程式設計式有兩種使用系統API的方式,一是上面例子中的直接使用swi陷入核心;二是調用标準C庫函數。個人建議還是第一種方法更好,因為第二種方法存在如下問題:

  • 開發環境下,目标機器的标準C庫不一定存在,例如我們目前的環境,由于還沒有編譯安裝标準C庫;這樣交叉連結無法完成連結任務。

也許有人說,使用标準C庫會使得程式具有更強的可移植性。可是别忘了,我們開發的是彙程式設計式,彙程式設計式天生就不具有可移植性!!!