新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > GNU風(fēng)格 ARM匯編語(yǔ)法指南

GNU風(fēng)格 ARM匯編語(yǔ)法指南

作者: 時(shí)間:2016-11-20 來(lái)源:網(wǎng)絡(luò) 收藏
匯編源程序一般用于系統(tǒng)最基本的初始化:初始化堆棧指針、設(shè)置頁(yè)表、操作 ARM的協(xié)處理器等。這些初始化工作完成后就可以跳轉(zhuǎn)到C代碼main函數(shù)中執(zhí)行。

1、GNU匯編語(yǔ)言語(yǔ)句格式

本文引用地址:http://m.butianyuan.cn/article/201611/318762.htm

任何Linux匯編行都是如下結(jié)構(gòu):[

linstruction為指令

ldirective為偽操作

lpseudo-instruction為偽指令

l

lcomment為語(yǔ)句的注釋

下面定義一個(gè)"add"的函數(shù),最終返回兩個(gè)參數(shù)的和:

.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

注意:

lARM指令,偽指令,偽操作,寄存器名可以全部為大寫(xiě)字母,也可全部為小寫(xiě)字母,但不可大小寫(xiě)混用。

l如果語(yǔ)句太長(zhǎng),可以將一條語(yǔ)句分幾行來(lái)書(shū)寫(xiě),在行末用“”表示換行(即下一行與本行為同一語(yǔ)句)。“”后不能有任何字符,包含空格和制表符(Tab)。

2、GNU匯編程序中的標(biāo)號(hào)symbol(或label)

標(biāo)號(hào)只能由a~z,A~Z,0~9,“.”,_等(由點(diǎn)、字母、數(shù)字、下劃線(xiàn)等組成,除局部標(biāo)號(hào)外,不能以數(shù)字開(kāi)頭)字符組成。

Symbol的本質(zhì):代表它所在的地址,因此也可以當(dāng)作變量或者函數(shù)來(lái)使用。

l段內(nèi)標(biāo)號(hào)的地址值在匯編時(shí)確定;

l段外標(biāo)號(hào)的地址值在連接時(shí)確定。

Symbol的分類(lèi):3類(lèi)(依據(jù)標(biāo)號(hào)的生成方式)。

<1>基于PC的標(biāo)號(hào)?;赑C的標(biāo)號(hào)是位于目標(biāo)指令前的標(biāo)號(hào)或者程序中數(shù)據(jù)定義偽操作前的標(biāo)號(hào)。這種標(biāo)號(hào)在匯編時(shí)將被處理成PC值加上(或減去)一個(gè)數(shù)字常量,常用于表示跳轉(zhuǎn)指令”b”等的目標(biāo)地址,或者代碼段中所嵌入的少量數(shù)據(jù)。

<2>基于寄存器的標(biāo)號(hào)?;诩拇嫫鞯臉?biāo)號(hào)常用MAP和FIELD來(lái)定義,也可以用EQU來(lái)定義。這種標(biāo)號(hào)在匯編時(shí)將被處理成寄存器的值加上(或減去)一個(gè)數(shù)字常量,常用于訪問(wèn)數(shù)據(jù)段中的數(shù)據(jù)。

<3>絕對(duì)地址。絕對(duì)地址是一個(gè)32位數(shù)據(jù)。它可以尋址的范圍為[0,232-1]即可以直接尋址整個(gè)內(nèi)存空間。

特別說(shuō)明:局部標(biāo)號(hào)Symbol

局部標(biāo)號(hào)主要在局部范圍內(nèi)使用,而且局部標(biāo)號(hào)可以重復(fù)出現(xiàn)。它由兩部組成:開(kāi)頭是一個(gè)0-99直接的數(shù)字,后面緊接一個(gè)通常表示該局部變量作用范圍的符號(hào)。局部變量的作用范圍通常為當(dāng)前段,也可以用ROUT來(lái)定義局部變量的作用范圍。

局部變量定義的語(yǔ)法格式:N{routname}

lN:為0~99之間的數(shù)字。

lroutname:當(dāng)前局部范圍的名稱(chēng)(為符號(hào)),通常為該變量作用范圍的名稱(chēng)(用ROUT偽操作定義的)。

局部變量引用的語(yǔ)法格式:%{F|B}{A|T}N{routname}

l%:表示引用操作

lN:為局部變量的數(shù)字號(hào)

lroutname:為當(dāng)前作用范圍的名稱(chēng)(用ROUT偽操作定義的)

lF:指示編譯器只向前搜索

lB:指示編譯器只向后搜索

lA:指示編譯器搜索宏的所有嵌套層次

lT:指示編譯器搜索宏的當(dāng)前層次

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

1:

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

bne 1F @跳轉(zhuǎn)到1標(biāo)號(hào)去執(zhí)行

注意:

l如果F和B都沒(méi)有指定,編譯器先向前搜索,再向后搜索

l如果A和T都沒(méi)有指定,編譯器搜索所有從當(dāng)前層次到宏的最高層次,比當(dāng)前層次低的層次不再搜索。

l如果指定了routname,編譯器向前搜索最近的ROUT偽操作,若routname與該ROUT偽操作定義的名稱(chēng)不匹配,編譯器報(bào)告錯(cuò)誤,匯編失敗。

3、GNU匯編程序中的分段

<1>.section偽操作

.section {,””}

Starts a new code or data section. Sections in GNU are called .text, a code section, .data, an initialized data section, and .bss, an uninitialized data section.

These sections have default flags, and the linker understands the default names(similar directive to the armasm directive AREA).The following are allowable.section flagsfor ELF format files:

Meaning

a allowable section

w writable section

x executable section

中文解釋?zhuān)?/p>

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

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

每一個(gè)段以段名為開(kāi)始, 以下一個(gè)段名或者文件結(jié)尾為結(jié)束。這些段都有缺省的標(biāo)志(flags),連接器可以識(shí)別這些標(biāo)志。(與arm asm中的AREA相同)。下面是ELF格式允許的段標(biāo)志flags:

<標(biāo)志> 含義

a 允許段

w 可寫(xiě)段

x 執(zhí)行段

例:定義一個(gè)“段”

.section .mysection @自定義數(shù)據(jù)段,段名為 “.mysection”

.align 2

strtemp:

.ascii "Temp string n" @對(duì)這一句的理解,我覺(jué)得應(yīng)該是:將"Temp string n"這個(gè)字符串存儲(chǔ)在以標(biāo)號(hào)strtemp:

@為起始地址的一段內(nèi)存空間里

<2>匯編系統(tǒng)預(yù)定義的段名

l.text @代碼段

l.data @初始化數(shù)據(jù)段.data Read-write initialized long data.

l.bss @未初始化數(shù)據(jù)段

l.sdata @ .sdata Read-write initialized short data.

l.sbss @

注意:源程序中.bss段應(yīng)該在.text段之前。

4、GNU匯編語(yǔ)言定義入口點(diǎn)

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

例:定義入口點(diǎn)

.section .data

< initialized data here>

.section .bss

< uninitialized data here>

.section .text

.globl _start

_start:

5、GNU匯編程序中的宏定義

格式如下:

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

宏體

.endm @.endm表示宏結(jié)束

如果宏使用參數(shù),那么在宏體中使用該參數(shù)時(shí)添加前綴“”。宏定義時(shí)的參數(shù)還可以使用默認(rèn)值??梢允褂?exitm偽指令來(lái)退出宏。

例:宏定義

.macro SHIFTLEFT a, b

.if b < 0

MOV a, a, ASR #-b

.exitm

.endif

MOV a, a, LSL #b

.endm

6、GNU匯編程序中的常數(shù)

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

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

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

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

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

<6>當(dāng)前地址以“.”表示,在GNU匯編程序中可以使用這個(gè)符號(hào)代表當(dāng)前指令的地址;

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

7、GNU ARM匯編的常用偽操作

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

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

l函數(shù)的定義;

l對(duì)齊方式偽操作 .align;

l源文件結(jié)束偽操作.end;

l.include偽操作;

lif偽操作;

l.global/ .globl 偽操作;

l.type偽操作;

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

別于GNUAS匯編的通用偽操作,下面是ARM特有的偽操作:

.reg ,.unreq ,.code ,.thumb ,.thumb_func ,.thumb_set, .ltorg ,.pool

<1>數(shù)據(jù)定義偽操作

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

l.short:定義雙字節(jié)數(shù)據(jù),如:.short 0x1234,60000 ;

l.long:定義4字節(jié)數(shù)據(jù),如:.long 0x12345678,23876565

l.quad:定義8字節(jié),如:.quad 0x1234567890abcd

l.float:定義浮點(diǎn)數(shù),如:.float0f-314159265358979323846264338327

95028841971.693993751E-40 @- pi

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

.string "abcd", "efgh", "hello!"

.asciz "qwer", "sun", "world!"

.ascii "welcome"

注意:ascii偽操作定義的字符串需要自行添加結(jié)尾字符。

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

.rept 重復(fù)次數(shù)

數(shù)據(jù)定義

.endr @結(jié)束重復(fù)定義

例:

.rept 3

.byte 0x23

.endr

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

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

例:

.equ abc, 3 @讓abc=3

<2>函數(shù)的定義偽操作

l函數(shù)的定義,格式如下:

函數(shù)名:

函數(shù)體

返回語(yǔ)句

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

l函數(shù)的編寫(xiě)應(yīng)當(dāng)遵循如下規(guī)則:

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

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

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

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

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

l.align:用來(lái)指定數(shù)據(jù)的對(duì)齊方式,格式如下:

.align [absexpr1, absexpr2]

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

l.end:表明源文件的結(jié)束。

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

.include “myarmasm.h”

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

.incbin "file"[,skip[,count]]

skip表明是從文件開(kāi)始跳過(guò)skip個(gè)字節(jié)開(kāi)始讀取文件,count是讀取的字?jǐn)?shù).

<4>..if偽操作

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

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

.ifdef symbol @判斷symbol是否定義

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

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

.ifeqs string1,string2 @判斷string1和string2是否相等,字符串必須用雙引號(hào)括起來(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

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

.global symbol 或者 .globl symbol

l.type:用來(lái)指定一個(gè)符號(hào)的類(lèi)型是函數(shù)類(lèi)型或者是對(duì)象類(lèi)型, 對(duì)象類(lèi)型一般是數(shù)據(jù), 格式如下:

.type 符號(hào), 類(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

<6>列表控制語(yǔ)句:

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

.title “my program”

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

<7>ARM特有的偽操作

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

別名 .req 寄存器名

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

.unreq 寄存器別名

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

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

.code 表達(dá)式

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

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

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

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

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

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

l.pool的作用等同.ltorg

l.space {,}

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

l.word {,} …

插入一個(gè)32-bit的數(shù)據(jù)隊(duì)列。(與armasm中的DCD功能相同)??梢允褂?word把標(biāo)識(shí)符作為常量使用。

例:

Start:

valueOfStart:

.word Start

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

l.hword {,} …

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

8、GNU ARM匯編特殊字符和語(yǔ)法

<1>代碼行中的注釋符號(hào):‘@’

<2>整行注釋符號(hào): ‘#’

<3>語(yǔ)句分離符號(hào): ‘;’

<4>立即數(shù)前綴: ‘#’ 或 ‘$’



評(píng)論


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

關(guān)閉