第一部分 Linux下ARM匯編語法
盡管在Linux下使用C或C++編寫程序很方便,但匯編源程序用於系統最基本的初始化,如初始化堆棧指針、設置頁表、操作ARM的協處理器等。初始化完成後就可以跳轉到C代碼執行。需要注意的是,GNU的匯編器遵循AT&T的匯編語法,可以從GNU的站點(www.gnu.org)上下載有關規范。
一. Linux匯編行結構
任何匯編行都是如下結構:
[:] [} @ comment
[:] [} @ 注釋
Linux ARM 匯編中,任何以冒號結尾的標識符都被認為是一個標號,而不一定非要在一行的開始。
【例1】定義一個"add"的函數,返回兩個參數的和。
.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的數字時為局部標號,局部標號可以重復出現,使用方法如下:
標號f: 在引用的地方向前的標號
標號b: 在引用的地方向後的標號
【例2】使用局部符號的例子,一段循環程序
1:
subs r0,r0,#1 @每次循環使r0=r0-1
bne 1f @跳轉到1標號去執行
局部標號代表它所在的地址,因此也可以當作變量或者函數來使用。
三. Linux匯編程序中的分段
(1).section偽操作
用戶可以通過.section偽操作來自定義一個段,格式如下:
.section section_name [, "flags"[, %type[,flag_specific_arguments]]]
每一個段以段名為開始, 以下一個段名或者文件結尾為結束。這些段都有缺省的標志(flags),連接器可以識別這些標志。(與armasm中的AREA相同)。
下面是ELF格式允許的段標志
<標志> 含義
a 允許段
w 可寫段
x 執行段
【例3】定義段
.section .mysection @自定義數據段,段名為 “.mysection”
.align 2
strtemp:
.ascii "Temp string \n\0"
(2)匯編系統預定義的段名
.text @代碼段
.data @初始化數據段
.bss @未初始化數據段
.sdata @
.sbss @
需要注意的是,源程序中.bss段應該在.text之前。
四. 定義入口點
匯編程序的缺省入口是 start標號,用戶也可以在連接腳本文件中用ENTRY標志指明其它入口點。
【例4】定義入口點
.section.data
< initialized data here>
.section .bss
< uninitialized data here>
.section .text
.globl _start
_start:
<instruction code goes here>
五. Linux匯編程序中的宏定義
格式如下:
.macro 宏名 參數名列表 @偽指令.macro定義一個宏
宏體
.endm @.endm表示宏結束
如果宏使用參數,那麼在宏體中使用該參數時添加前綴“\”。宏定義時的參數還可以使用默認值。
可以使用.exitm偽指令來退出宏。
【例5】宏定義
.macro SHIFTLEFT a, b
.if \b < 0
MOV \a, \a, ASR #-\b
.exitm
.endif
MOV \a, \a, LSL #\b
.endm
六. Linux匯編程序中的常數
(1)十進制數以非0數字開頭,如:123和9876;
(2)二進制數以0b開頭,其中字母也可以為大寫;
(3)八進制數以0開始,如:0456,0123;
(4)十六進制數以0x開頭,如:0xabcd,0X123f;
(5)字符串常量需要用引號括起來,中間也可以使用轉義字符,如: “You are welcome!\n”;
(6)當前地址以“.”表示,在匯編程序中可以使用這個符號代表當前指令的地址;
(7)表達式:在匯編程序中的表達式可以使用常數或者數值, “-”表示取負數, “~”表示取補,“<>”表示不相等,其他的符號如:+、-、*、 /、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、|| 跟C語言中的用法相似。
七. Linux下ARM匯編的常用偽操作
在前面已經提到過了一些為操作,還有下面一些為操作:
>> 數據定義偽操作: .byte,.short,.long,.quad,.float,.string/.asciz/.ascii,重復定義偽操作.rept,賦值語句.equ/.set ;
>> 函數的定義 ;
>> 對齊方式偽操作 .align;
>> 源文件結束偽操作.end;
>> .include偽操作;
>> if偽操作;
>> .global/ .globl 偽操作 ;
>> .type偽操作 ;
>> 列表控制語句 ;
>> 區別於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:定義浮點數,如:
.float 0f-314159265358979323846264338327\
95028841971.693993751E-40 @ - pi
(6) .string/.asciz/.ascii:定義多個字符串,如:
.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: 賦值語句, 格式如下:
.equ(.set) 變量名,表達式
例如:
.equ abc 3 @讓abc=3
2.函數的定義偽操作
(1)函數的定義,格式如下:
函數名:
函數體
返回語句
一般的,函數如果需要在其他文件中調用, 需要用到.global偽操作將函數聲明為全局函數。為了不至於在其他程序在調用某個C函數時發生混亂,對寄存器的使用我們需要遵循APCS准則。函數編譯器將處理為函數代碼為一段.global的匯編碼。
(2)函數的編寫應當遵循如下規則:
>> a1-a4寄存器(參數、結果或暫存寄存器,r0到r3 的同義字)以及浮點寄存器f0-f3(如果存在浮點協處理器)在函數中是不必保存的;
>> 如果函數返回一個不大於一個字大小的值,則在函數結束時應該把這個值送到 r0 中;
>> 如果函數返回一個浮點數,則在函數結束時把它放入浮點寄存器f0中;
>> 如果函數的過程改動了sp(堆棧指針,r13)、fp(框架指針,r11)、sl(堆棧限制,r10)、lr(連接寄存器,r14)、v1-v8(變量寄存器,r4 到 r11)和 f4-f7,那麼函數結束時這些寄存器應當被恢復為包含在進入函數時它所持有的值。
3. .align .end .include .incbin偽操作
(1).align:用來指定數據的對齊方式,格式如下:
.align [absexpr1, absexpr2]
以某種對齊方式,在未使用的存儲區域填充值. 第一個值表示對齊方式,4, 8,16或 32. 第二個表達式值表示填充的值。
(2).end:表明源文件的結束。
(3).include:可以將指定的文件在使用.include 的地方展開,一般是頭文件,例如:
.include “myarmasm.h”
(4).incbin偽操作可以將原封不動的一個二進制文件編譯到當前文件中,使用方法如下:
.incbin "file"[,skip[,count]]
skip表明是從文件開始跳過skip個字節開始讀取文件,count是讀取的字數.
4. .if偽操作
根據一個表達式的值來決定是否要編譯下面的代碼, 用.endif偽操作來表示條件判斷的結束, 中間可以使用.else來決定.if的條件不滿足的情況下應該編譯哪一部分代碼。
.if有多個變種:
.ifdef symbol @判斷symbol是否定義
.ifc string1,string2 @字符串string1和string2是否相等,字符串可以用單引號括起來
.ifeq expression @判斷expression的值是否為0
.ifeqs string1,string2 @判斷string1和string2是否相等,字符 串必須用雙引號括起來
.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 @判斷是否沒有定義symbol, 跟.ifdef恰好相反
.ifne expression @如果expression的值不是0, 那麼編譯器將編譯下面的代碼
.ifnes string1,string2 @如果字符串string1和string2不相 等, 那麼編譯器將編譯下面的代碼.
5. .global .type .title .list
(1).global/ .globl :用來定義一個全局的符號,格式如下:
.global symbol 或者 .globl symbol
(2).type:用來指定一個符號的類型是函數類型或者是對象類型, 對象類型一般是數據, 格式如下:
.type 符號, 類型描述
【例6】
.globl a
.data
.align 4
.type a, @object
.size a, 4
a:
.long 10
【例7】
.section .text
.type asmfunc, @function
.globl asmfunc
asmfunc:
mov pc, lr
(3)列表控制語句:
.title:用來指定匯編列表的標題,例如:
.title “my program”
.list:用來輸出列表文件.
6. ARM特有的偽操作
(1) .reg: 用來給寄存器賦予別名,格式如下:
別名 .req 寄存���名
(2) .unreq: 用來取消一個寄存器的別名,格式如下:
.unreq 寄存器別名
注意被取消的別名必須事先定義過,否則編譯器就會報錯,這個偽操作也可以用來取消系統預制的別名, 例如r0, 但如果沒有必要的話不推薦那樣做。
(3) .code偽操作用來選擇ARM或者Thumb指令集,格式如下:
.code 表達式
如果表達式的值為16則表明下面的指令為Thumb指令,如果表達式的值為32則表明下面的指令為ARM指令.
(4) .thumb偽操作等同於.code 16, 表明使用Thumb指令, 類似的.arm等同於.code 32
(5) .force_thumb偽操作用來強制目標處理器選擇thumb的指令集而不管處理器是否支持
(6) .thumb_func偽操作用來指明一個函數是thumb指令集的函數
(7) .thumb_set偽操作的作用類似於.set, 可以用來給一個標志起一個別名, 比.set功能增加的一點是可以把一個標志標記為thumb函數的入口, 這點功能等同於.thumb_func
(8) .ltorg用於聲明一個數據緩沖池(literal pool)的開始,它可以分配很大的空間。
(9) .pool的作用等同.ltorg
(9).space <number_of_bytes> {,<fill_byte>}
分配number_of_bytes字節的數據空間,並填充其值為fill_byte,若未指定該值,缺省填充0。(與armasm中的SPACE功能相同)
(10).word <word1> {,<word2>} …
插入一個32-bit的數據隊列。(與armasm中的DCD功能相同)
可以使用.word把標識符作為常量使用
例如:
Start:
valueOfStart:
.word Start
這樣程序的開頭Start便被存入了內存變量valueOfStart中。
(11).hword <short1> {,<short2>} …
插入一個16-bit的數據隊列。(與armasm中的DCW相同)
八. GNU ARM匯編特殊字符和語法
代碼行中的注釋符號: ‘@’
整行注釋符號: ‘#’
語句分離符號: ‘;’
直接操作數前綴: ‘#’ 或 ‘$’
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-11/109204p2.htm