<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > arm gnu 匯編語(yǔ)法

arm gnu 匯編語(yǔ)法

作者: 時(shí)間:2016-11-20 來(lái)源:網(wǎng)絡(luò ) 收藏
盡管在Linux下使用C或C++編寫(xiě)程序很方便,但匯編源程序用于系統最基本的初始化,如初始化堆棧指針、設置頁(yè)表、操作ARM的協(xié)處理器等。初始化完成后就可以跳轉到C代碼執行。需要注意的是,GNU的匯編器遵循AT&T的匯編語(yǔ)法,可以從GNU的站點(diǎn)(www.gnu.org)上下載有關(guān)規范。

一. Linux匯編行結構

任何匯編行都是如下結構:

[:] [} @ comment

[:] [} @ 注釋

Linux ARM 匯編中,任何以冒號結尾的標識符都被認為是一個(gè)標號,而不一定非要在一行的開(kāi)始。

定義一個(gè)add的函數,返回兩個(gè)參數的和。

.section .text, “x”

.global add @ give the symbol add external linkage

add:

ADD r0, r0, r1 @ add input arguments

MOV pc, lr @ return from subroutine

@ end of program

二. Linux 匯編程序中的標號

標號只能由a~z,A~Z,0~9,“.”,_等字符組成。當標號為0~9的數字時(shí)為局部標號,局部標號可以重復出現,使用方法如下:

? 標號f: 在引用的地方向前的標號

? 標號b: 在引用的地方向后的標號

使用局部符號的例子,一段循環(huán)程序

1:

subs r0,r0,#1 @每次循環(huán)使r0=r0-1

bne 1f @跳轉到1標號去執行

局部標號代表它所在的地址,因此也可以當作變量或者函數來(lái)使用。

三. Linux匯編程序中的分段

(1).section偽操作

用戶(hù)可以通過(guò).section偽操作來(lái)自定義一個(gè)段,格式如下:

.section section_name [, flags[, %type[,flag_specific_arguments]]]

每一個(gè)段以段名為開(kāi)始, 以下一個(gè)段名或者文件結尾為結束。這些段都有缺省的標志(flags),連接器可以識別這些標志。(與armasm中的AREA相同)。

下面是ELF格式允許的段標志

<標志> 含義

a 允許段

w 可寫(xiě)段

x 執行段

定義段

.section .mysection @自定義數據段,段名為 “.mysection”

.align 2

strtemp:

.ascii Temp string /n/ 0

(2)匯編系統預定義的段名

.text @代碼段

.data @初始化數據段

.bss @未初始化數據段

.sdata @

.sbss @

需要注意的是,源程序中.bss段應該在.text之前。

四. 定義入口點(diǎn)

匯編程序的缺省入口是 start標號,用戶(hù)也可以在連接腳本文件中用ENTRY標志指明其它入口點(diǎn)。

定義入口點(diǎn)

.section.data

< initialized data here>

.section .bss

< uninitialized data here>

.section .text

.globl _start

_start:



五. Linux匯編程序中的宏定義

格式如下:

.macro 宏名 參數名列表 @偽指令.macro定義一個(gè)宏

宏體

.endm @.endm表示宏結束

如果宏使用參數,那么在宏體中使用該參數時(shí)添加前綴“/”。宏定義時(shí)的參數還可以使用默認值。

可以使用.exitm偽指令來(lái)退出宏。

宏定義

.macro SHIFTLEFT a, b

.if b < 0

MOV a, a, ASR #-b

.exitm

.endif

MOV a, a, LSL #b

.endm

六. Linux匯編程序中的常數

(1)十進(jìn)制數以非0數字開(kāi)頭,如:123和9876;

(2)二進(jìn)制數以0b開(kāi)頭,其中字母也可以為大寫(xiě);

(3)八進(jìn)制數以0開(kāi)始,如:0456,0123;

(4)十六進(jìn)制數以0x開(kāi)頭,如:0xabcd,0X123f;

(5)字符串常量需要用引號括起來(lái),中間也可以使用轉義字符,如: “You are welcome!//n”;

(6)當前地址以“.”表示,在匯編程序中可以使用這個(gè)符號代表當前指令的地址;

(7)表達式:在匯編程序中的表達式可以使用常數或者數值, “-”表示取負數, “~”表示取補,“<>”表示不相等,其他的符號如:+、-、*、/、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、||跟C語(yǔ)言中的用法相似。

七. Linux下ARM匯編的常用偽操作

在前面已經(jīng)提到過(guò)了一些為操作,還有下面一些為操作:

? 數據定義偽操作: .byte,.short,.long,.quad,.float,.string/.asciz/.ascii,重復定義偽操作.rept,賦值語(yǔ)句.equ/.set ;

? 函數的定義 ;

? 對齊方式偽操作 .align;

? 源文件結束偽操作.end;

? .include偽操作;

? if偽操作;

? .global/ .globl 偽操作 ;

? .type偽操作 ;

? 列表控制語(yǔ)句 ;

? 區別于gas匯編的通用偽操作,下面是ARM特有的偽操作 :.reg ,.unreq ,.code ,.thumb ,.thumb_func ,.thumb_set, .ltorg ,.pool

1. 數據定義偽操作

(1) .byte:單字節定義,如:.byte 1,2,0b01,0x34,072,s ;

(2) .short:定義雙字節數據,如:.short 0x1234,60000 ;

(3) .long:定義4字節數據,如:.long 0x12345678,23876565

(4) .quad:定義8字節,如:.quad 0x1234567890abcd

(5) .float:定義浮點(diǎn)數,如:

.float 0f-314159265358979323846264338327/

95028841971.693993751E-40 @ - pi

(6) .string/.asciz/.ascii:定義多個(gè)字符串,如:

.string abcd, efgh, hello!

.asciz qwer, sun, world!

.ascii welcome// 0

需要注意的是:.ascii偽操作定義的字符串需要自行添加結尾字符// 0。

(7) .rept:重復定義偽操作, 格式如下:

.rept 重復次數

數據定義

.endr @結束重復定義

例如:

.rept 3

.byte 0x23

.endr

(8) .equ/.set: 賦值語(yǔ)句, 格式如下:

.equ(.set) 變量名,表達式

例如:

.equ abc 3 @讓abc=3

2.函數的定義偽操作

(1)函數的定義,格式如下:

函數名:

函數體

返回語(yǔ)句

一般的,函數如果需要在其他文件中調用, 需要用到.global偽操作將函數聲明為全局函數。為了不至于在其他程序在調用某個(gè)C函數時(shí)發(fā)生混亂,對寄存器的使用我們需要遵循APCS準則。函數編譯器將處理為函數代碼為一段.global的匯編碼。

(2)函數的編寫(xiě)應當遵循如下規則:

? a1-a4寄存器(參數、結果或暫存寄存器,r0到r3 的同義字)以及浮點(diǎn)寄存器f0-f3(如果存在浮點(diǎn)協(xié)處理器)在函數中是不必保存的;

? 如果函數返回一個(gè)不大于一個(gè)字大小的值,則在函數結束時(shí)應該把這個(gè)值送到 r0 中;

? 如果函數返回一個(gè)浮點(diǎn)數,則在函數結束時(shí)把它放入浮點(diǎn)寄存器f0中;

? 如果函數的過(guò)程改動(dòng)了sp(堆棧指針,r13)、fp(框架指針,r11)、sl(堆棧限制,r10)、lr(連接寄存器,r14)、v1-v8(變量寄存器,r4 到 r11)和 f4-f7,那么函數結束時(shí)這些寄存器應當被恢復為包含在進(jìn)入函數時(shí)它所持有的值。

3. .align .end .include .incbin偽操作

(1).align:用來(lái)指定數據的對齊方式,格式如下:

.align [absexpr1, absexpr2]

以某種對齊方式,在未使用的存儲區域填充值. 第一個(gè)值表示對齊方式,4, 8,16或 32. 第二個(gè)表達式值表示填充的值。

(2).end:表明源文件的結束。

(3).include:可以將指定的文件在使用.include 的地方展開(kāi),一般是頭文件,例如:

.include “myarmasm.h”

(4).incbin偽操作可以將原封不動(dòng)的一個(gè)二進(jìn)制文件編譯到當前文件中,使用方法如下:

.incbin file[,skip[,count]]

skip表明是從文件開(kāi)始跳過(guò)skip個(gè)字節開(kāi)始讀取文件,count是讀取的字數.

4. .if偽操作

根據一個(gè)表達式的值來(lái)決定是否要編譯下面的代碼, 用.endif偽操作來(lái)表示條件判斷的結束, 中間可以使用.else來(lái)決定.if的條件不滿(mǎn)足的情況下應該編譯哪一部分代碼。

.if有多個(gè)變種:

.ifdef symbol @判斷symbol是否定義

.ifc string1,string2 @字符串string1和string2是否相等,字符串可以用單引號括起來(lái)

.ifeq expression @判斷expression的值是否為0

.ifeqs string1,string2 @判斷string1和string2是否相等,字符 串必須用雙引號括起來(lái)

.ifge expression @判斷expression的值是否大于等于0

.ifgt absolute expression @判斷expression的值是否大于0

.ifle expression @判斷expression的值是否小于等于0

.iflt absolute expression @判斷expression的值是否小于0

.ifnc string1,string2 @判斷string1和string2是否不相等, 其用法跟.ifc恰好相反。

.ifndef symbol, .ifnotdef symbol @判斷是否沒(méi)有定義symbol, 跟.ifdef恰好相反

.ifne expression @如果expression的值不是0, 那么編譯器將編譯下面的代碼

.ifnes string1,string2 @如果字符串string1和string2不相 等, 那么編譯器將編譯下面的代碼.

5. .global .type .title .list

(1).global/ .globl :用來(lái)定義一個(gè)全局的符號,格式如下:

.global symbol 或者 .globl symbol

(2).type:用來(lái)指定一個(gè)符號的類(lèi)型是函數類(lèi)型或者是對象類(lèi)型, 對象類(lèi)型一般是數據, 格式如下:

.type 符號, 類(lèi)型描述



.globl a

.data

.align 4

.type a, @object

.size a, 4

a:

.long 10



.section .text

.type asmfunc, @function

.globl asmfunc

asmfunc:

mov pc, lr

(3)列表控制語(yǔ)句:

.title:用來(lái)指定匯編列表的標題,例如:

.title “my program”

.list:用來(lái)輸出列表文件.

6. ARM特有的偽操作

(1) .reg: 用來(lái)給寄存器賦予別名,格式如下:

別名 .req 寄存器名

(2) .unreq: 用來(lái)取消一個(gè)寄存器的別名,格式如下:

       .unreq 寄存器別名

  注意被取消的別名必須事先定義過(guò),否則編譯器就會(huì )報錯,這個(gè)偽操作也可以用來(lái)取消系統預制的別名, 例如r0, 但如果沒(méi)有必要的話(huà)不推薦那樣做。

(3) .code偽操作用來(lái)選擇ARM或者Thumb指令集,格式如下:

           .code 表達式

  如果表達式的值為16則表明下面的指令為T(mén)humb指令,如果表達式的值為32則表明下面的指令為ARM指令.

(4) .thumb偽操作等同于.code 16, 表明使用Thumb指令, 類(lèi)似的.arm等同于.code 32

(5) .force_thumb偽操作用來(lái)強制目標處理器選擇thumb的指令集而不管處理器是否支持

(6) .thumb_func偽操作用來(lái)指明一個(gè)函數是thumb指令集的函數

(7) .thumb_set偽操作的作用類(lèi)似于.set, 可以用來(lái)給一個(gè)標志起一個(gè)別名, 比.set功能增加的一點(diǎn)是可以把一個(gè)標志標記為thumb函數的入口, 這點(diǎn)功能等同于.thumb_func

(8) .ltorg用于聲明一個(gè)數據緩沖池(literal pool)的開(kāi)始,它可以分配很大的空間。

(9) .pool的作用等同.ltorg

(9).space {,}

分配number_of_bytes字節的數據空間,并填充其值為fill_byte,若未指定該值,缺省填充0。(與armasm中的SPACE功能相同)

(10).word {,} …

插入一個(gè)32-bit的數據隊列。(與armasm中的DCD功能相同)

可以使用.word把標識符作為常量使用

例如:

Start:

valueOfStart:

.word Start

這樣程序的開(kāi)頭Start便被存入了內存變量valueOfStart中。

(11).hword {,} …

插入一個(gè)16-bit的數據隊列。(與armasm中的DCW相同)

八. GNU ARM匯編特殊字符和語(yǔ)法

代碼行中的注釋符號:‘@’

整行注釋符號: ‘#’

語(yǔ)句分離符號: ‘;’

直接操作數前綴: ‘#’ 或 ‘$’

第二部分 GNU的編譯器和調試工具

一. 編譯工具

1.編輯工具介紹

GNU提供的編譯工具包括匯編器as、C編譯器gcc、C++編譯器g++、連接器ld和二進(jìn)制轉換工具objcopy?;贏(yíng)RM平臺的工具分別為arm-linux-as、arm-linux-gcc、arm-linux-g++、arm-linux-ld和arm-linux- objcopy。GNU的編譯器功能非常強大,共有上百個(gè)操作選項,這也是這類(lèi)工具讓初學(xué)者頭痛的原因。不過(guò),實(shí)際開(kāi)發(fā)中只需要用到有限的幾個(gè),大部分可以采用缺省選項。GNU工具的開(kāi)發(fā)流程如下:編寫(xiě)C、C++語(yǔ)言或匯編源程序,用gcc或g++生成目標文件,編寫(xiě)連接腳本文件,用連接器生成最終目標文件(elf格式),用二進(jìn)制轉換工具生成可下載的二進(jìn)制代碼。

(1)編寫(xiě)C、C++語(yǔ)言或匯編源程序

通常匯編源程序用于系統最基本的初始化,如初始化堆棧指針、設置頁(yè)表、操作ARM的協(xié)處理器等。初始化完成后就可以跳轉到C代碼執行。需要注意的是,GNU的匯編器遵循AT&T的匯編語(yǔ)法,讀者可以從GNU的站點(diǎn)(www.gnu.org)上下載有關(guān)規范。匯編程序的缺省入口是 start標號,用戶(hù)也可以在連接腳本文件中用ENTRY標志指明其它入口點(diǎn)(見(jiàn)下文關(guān)于連接腳本的說(shuō)明)。

(2)用gcc或g++生成目標文件

如果應用程序包括多個(gè)文件,就需要進(jìn)行分別編譯,最后用連接器連接起來(lái)。如筆者的引導程序包括3個(gè)文件:init.s(匯編代碼、初始化硬件)xmrecever.c(通信模塊,采用Xmode協(xié)議)和flash.c(Flash擦寫(xiě)模塊)。

分別用如下命令生成目標文件: arm-linux-gcc-c-O2-oinit.oinit.s arm-linux-gcc-c-O2-oxmrecever.oxmrecever.c arm-linux-gcc-c-O2-oflash.oflash.c 其中-c命令表示只生成目標代碼,不進(jìn)行連接;-o命令指明目標文件的名稱(chēng);-O2表示采用二級優(yōu)化,采用優(yōu)化后可使生成的代碼更短,運行速度更快。如果項目包含很多文件,則需要編寫(xiě)makefile文件。關(guān)于makefile的內容,請感興趣的讀者參考相關(guān)資料。

(3)編寫(xiě)連接腳本文件

gcc等編譯器內置有缺省的連接腳本。如果采用缺省腳本,則生成的目標代碼需要操作系統才能加載運行。為了能在嵌入式系統上直接運行,需要編寫(xiě)自己的連接腳本文件。編寫(xiě)連接腳本,首先要對目標文件的格式有一定了解。GNU編譯器生成的目標文件缺省為elf格式。elf文件由若干段(section)組成,如不特殊指明,由C源程序生成的目標代碼中包含如下段:.text(正文段)包含程序的指令代碼;.data(數據段)包含固定的數據,如常量、字符串;.bss(未初始化數據段)包含未初始化的變量、數組等。C++源程序生成的目標代碼中還包括.fini(析構函數代碼)和. init(構造函數代碼)等。連接器的任務(wù)就是將多個(gè)目標文件的.text、.data和.bss等段連接在一起,而連接腳本文件是告訴連接器從什么地址開(kāi)始放置這些段。例如連接文件link.lds為:

ENTRY(begin)

SECTION

{

.=0x30000000;

.text:{*(.text)}

.data:{*(.data)}

.bss:{*(.bss)}

}

其中,ENTRY(begin)指明程序的入口點(diǎn)為begin標號;.=0x00300000指明目標代碼的起始地址為0x30000000,這一段地址為MX1的片內RAM;.text:{*(.text)}表示從0x30000000開(kāi)始放置所有目標文件的代碼段,隨后的.data:{* (.data)}表示數據段從代碼段的末尾開(kāi)始,再后是.bss段。

(4)用連接器生成最終目標文件

有了連接腳本文件,如下命令可生成最終的目標文件:

arm-linux-ld –no stadlib –o bootstrap.elf -Tlink.lds init.o xmrecever.o flash.o

其中,ostadlib表示不連接系統的運行庫,而是直接從begin入口;-o指明目標文件的名稱(chēng);-T指明采用的連接腳本文件(也可以使用-Ttext address,address表示執行區地址);最后是需要連接的目標文件列表。

(5)生成二進(jìn)制代碼

連接生成的elf文件還不能直接下載執行,通過(guò)objcopy工具可生成最終的二進(jìn)制文件:

arm-linux-objcopy –O binary bootstrap.elf bootstrap.bin

其中-O binary指定生成為二進(jìn)制格式文件。Objcopy還可以生成S格式的文件,只需將參數換成-O srec。還可以使用-S選項,移除所有的符號信息及重定位信息。如果想將生成的目標代碼反匯編,還可以用objdump工具:

arm-linux-objdump -D bootstrap.elf

至此,所生成的目標文件就可以直接寫(xiě)入Flash中運行了。

2.Makefile實(shí)例

example: head.s main.c

arm-linux-gcc -c -o head.o head.s

arm-linux-gcc -c -o main.o main.c

arm-linux-ld -Tlink.lds head.o ain.o -o example.elf

arm-linux-objcopy -O binary -S example_tmp.o example

arm-linux-objdump -D -b binary -m arm example >ttt.s

二. 調試工具

Linux下的GNU調試工具主要是gdb、gdbserver和kgdb。其中g(shù)db和gdbserver可完成對目標板上Linux下應用程序的遠程調試。gdbserver是一個(gè)很小的應用程序,運行于目標板上,可監控被調試進(jìn)程的運行,并通過(guò)串口與上位機上的gdb通信。開(kāi)發(fā)者可以通過(guò)上位機的gdb輸入命令,控制目標板上進(jìn)程的運行,查看內存和寄存器的內容。gdb5.1.1以后的版本加入了對ARM處理器的支持,在初始化時(shí)加入- target==arm參數可直接生成基于A(yíng)RM平臺的gdbserver。gdb工具可以從ftp: //ftp.gnu.org/pub/gnu/gdb/上下載。

對于Linux內核的調試,可以采用kgdb工具,同樣需要通過(guò)串口與上位機上的gdb通信,對目標板的Linux內核進(jìn)行調試??梢詮膆ttp://oss.sgi.com/projects/kgdb/上了解具體的使用方法。

本文引用地址:http://dyxdggzs.com/article/201611/318761.htm


關(guān)鍵詞: armgnu匯編語(yǔ)

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>