ARM GNU匯編基礎
0 前言
全文補充提醒:
筆者在閱讀ARM官方檔案及查閱實際的u-boot原始碼中的匯編代碼后,發現了一些不同于ARM官方檔案中的匯編語法,查閱相關資料后,才發現主要由于匯編器的不同,有兩種不同的匯編語法:
ARM標準匯編
- 匯編程式:armasm
GNU ARM匯編
- 匯編程式:as
兩者在語法上主要的區別在于偽操作的不同,其他相關的指令基本上是一致的,所以這一區別并不會對我們下文的學習造成太大的影響,為了方便,筆者通篇的示例均以GNU ARM匯編語法為標準,使用的匯編程式為arm-linux-gnueabihf-as.讀者如需學習ARM標準匯編相關知識,需自行參考ARM官方檔案 ARM Software Development Toolkit User Guide,
第二次補充提醒:
全文沒有過多的去關注和介紹所有的ARM指令,更多的關注點均在基礎語法和相關知識
全文內容大部分參考ARM官方檔案 ARM Software Development Toolkit User Guide第五章以及GNU檔案Using as,主要介紹了撰寫ARM和Thumb匯編程式的通用準則,主要包含了以下章節:
- ARM架構概述
- 匯編語言的模塊結構
- 資料處理指令
- 記憶體訪問指令
- 條件執行
- 宏的使用
? 在開始之前,希望讀者能夠搭建好相應的開發環境(編譯工具鏈和運行環境),在此,筆者使用的是arm-linux-gnueabihf-交叉編譯工具鏈,為了能夠運行ARM程式,筆者使用的是QEMU,相關工具的下載和安裝在此不再詳述,
? 由于計算機最終只能識別機器碼,也就是二進制序列,所以匯編語言同其他語言一樣,需要相應的工具將其轉換成機器語言,這就需要前面提的編譯工具鏈,由于筆者的平臺是X86架構,而撰寫的是ARM架構上的程式,因此需要一個能夠執行ARM指令的設備,在這里筆者使用的即前文提及的QEMU工具,由于筆者編譯的平臺是X86架構,而目標機器是ARM架構,所以需要的是交叉編譯工具鏈,不清楚這一知識的讀者可自行補充交叉編譯相關知識,
? 當你有一個寫好的匯編代碼檔案( .s 后綴)后,你需要使用as工具進行匯編,生成機器語言,再使用ld工具進行鏈接,
$ arm-linux-gnueabihf-as sum10.s -o sum10.o -g
$ arm-linux-gnueabihf-ld sum10.o -o sum10
/*
-g:帶有debug資訊
-o:指定輸出檔案名
*/
Note:
? c語言編譯程序中,中途會先進行編譯生成匯編代碼,然后再進行匯編和鏈接,最終生成可執行檔案
1 ARM架構概述
本章主要簡單介紹下后文中所需要的一些ARM架構相關知識,讀者如遇見不清楚的地方,可自行去閱讀ARM Architectural Reference Manual,
ARM是一個典型的RISC(精簡指令集)處理器,只有加載和存盤指令可以訪問記憶體,資料操作相關指令只能操作暫存器,這也就意味著程式更新一次記憶體中資料,至少需要三步:
- 從記憶體中將資料讀取到暫存器中
- 對暫存器中的資料進行更新
- 將更新后的資料放回記憶體中
1.1 ARM 架構版本
| ARM family | ARM架構版本 |
|---|---|
| ARM7 | ARM v4 |
| ARM9 | ARM v5 |
| ARM11 | ARM v6 |
| Cortex-A | ARM v7-A |
| Cortex-R | ARM v7-R |
| Cortex-M | ARM v7-M |
1.2 ARM & Thumb state
在ARM v4T和v4TxM架構中,定義了一種長度為16bits的指令集,并稱之為Thumb指令集,這一指令集是ARM指令集的一個子集,這一指令集同ARM指令集主要有以下區別:
- 在暫存器的訪問上受一定的限制
- 只能通過分支指令實作條件執行
- 不允許訪問桶式移位器(barrel shifter)
ARM處理器執行ARM指令時被稱為處于ARM狀態,執行Thumb指令時,被稱為Thumb狀態,
Note:后面會詳細急介紹以上三點區別,讀者不清楚,不用著急
ARM處理器最開始總是處于ARM狀態,可通過BX指令轉換至Thumb狀態,
1.3 地址空間
在ARM v3架構之后,所有的的處理器均有32bit的尋址范圍
1.4 處理器模式
ARM擁有以下7種基本操作模式:
- User
- FIQ
- IRQ
- Supervisor(svc)
- Abort
- Undefined
- System
以上其中模式中,大部分的程式都運行與User模式下,其他的六種均為特權模式
1.5 暫存器
ARM處理器一共提供了37個暫存器,暫存器排布在部分重疊的存盤區域,每種模式下均有不同的暫存器組,
| User mode | IRQ | FIQ | Undef | Abort | SVC |
|---|---|---|---|---|---|
| r0-r7 | |||||
| r8 | r8 | ||||
| r9 | r9 | ||||
| r10 | r10 | ||||
| r11 | r11 | ||||
| r12 | r12 | ||||
| r13 (sp) | r13 (sp) | r13 (sp) | r13 (sp) | r13 (sp) | r13 (sp) |
| r14 (lr) | r14 (lr) | r14 (lr) | r14 (lr) | r14 (lr) | r14 (lr) |
| r15 (pc) | |||||
| cpsr | |||||
| spsr | spsr | spsr | spsr | spsr |
30個32位通用暫存器,根據當前處理器的模式,可以隨時看到其中的15個暫存器,分別是:r0,r1...r14,
通常情況下,r13會被用作堆疊指標(stack pointer),r14會被用作鏈接暫存器(link register),用于存盤呼叫子程式時存盤回傳地址,r15是程式計數器,用于存放下一條需要執行的指令的位置,所以可以通過將程式需要跳轉的地址放入PC來實作程式跳轉,
CPSR暫存器(Current Program Status Register)主要持有了以下資訊:
- ALU的狀態標志資訊(C/V/N/Z)
- 當前處理器的模式
- 中斷Disable標志
- ARM State or Thumb State (如果該處理器支持Thumb)
SPSR暫存器(Saved Program Status Register)用于例外發生時存盤CPSR,
CPSR暫存器:
| N | Z | C | V | J | GE | E | A | I | F | T | M | |||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 31 | 30 | 29 | 28 | 24 | 19-16 | 9 | 8 | 7 | 6 | 5 | 4-0 |
M:處理器模式
T:ARM || Thumb 狀態
N:負數標志
Z:結果為0
C:進位標志
V:溢位標志
1.6 ARM指令集概述
所有的ARM指令長度均為32bits,因為指令在記憶體中按字對齊存盤,故地址最低2個bits應該為0,因此除了分支交換BX指令外,所有具有地址運算元的ARM指令都將忽略這兩個bit,而BX正是通過這個bit來確定進入Thumb狀態還是ARM狀態,
ARM指令可大致分為以下積累幾類:
- 分支指令
- 資料處理指令
- 狀態暫存器訪問指令
- 單一暫存器加載/存盤指令
- 多暫存器加載/存盤指令
- 信號量指令
- 協處理器指令
指令功能:
- 條件執行
- 暫存器訪問
- 訪問桶式移位器
1.7 Thumb指令集概述
所有的Thumb指令長度為16bits,且存盤為半字對齊,故地址最低bit應該為0,因此除了分支交換BX指令外,所有具有地址運算元的指令都將忽略這個bit,而BX正是通過這個bit來確定進入Thumb狀態還是ARM狀態,
下面是Thumb指令與ARM指令的不同之處:
-
分支指令
- 相對于ARM指令,在范圍上擁有更多的限制,而且僅支持無條件跳轉
-
資料處理指令
- 受一定限制訪的問r8-r15
- 一直會更新CPSR中的CVNZ位(ARM指令需添加S后綴),除了訪問r8-r15
-
狀態暫存器訪問指令
- 沒有相關的訪問指令
-
單一暫存器加載/存盤指令
- 不可訪問r8-r15
-
多暫存器加載/存盤指令
- 記憶體到暫存器的訪問范圍被限制在了r0-r7
- 此外,PUSH 和 POP可以分別使用r13和r14
-
信號量指令(無)
-
協處理器指令(無)
Thumb指令的功能
- 條件執行
- 只能通過分支指令來實作條件執行
- 訪問暫存器
- 大部分情況下只能呢個訪問r0-r7
- r8-r15訪問受限,但也可以使用,比如作為快速臨時存盤
- 訪問桶式移位器
- 只能通過特定的指令去訪問,LSL、LSR、ASR、ROR
2 匯編語言結構
匯編語言在這里指的是允許通過ARM匯編程式分析和匯編生成目標代碼的語言,它們可以是:
- ARM匯編語言
- Thumb匯編語言
- 以上兩者的混合
2.1 匯編語言源檔案的布局
匯編源檔案中的的代碼行的通常的形式如下:
GNU ARM 匯編格式:
{label:}{instruction | directive | pseudo-instruction} {@comment}
ARM 標準匯編格式:
{label} {instruction | directive | pseudo-instruction} {;comment}
{標簽} {指令|偽操作|偽指令} {注釋}
在這里需要注意的是,instruction | directive | pseudo-instruction 前面必須要有空格或者TAB
以上的三個部分均是可選的,也就是你完全可以用空行去分割你的代碼,使其更具可讀性
-
大小寫規則
- 所有的指令助記符可以是大寫或者小寫,但是不能混合
- Directive必須大寫
- 暫存器符號可以大寫或者小寫,但是不能混合
-
單行代碼長度
- 為了使代碼具有可讀性,可允許使用‘\’字符來分割換行
-
label 標簽
- 在匯編語言中,標簽是代表地址的一個符號,這個地址將在匯編期間被計算出來
- 在GNU匯編中,任何一個以冒號結尾的識別符號都會被認為是一個標簽,而不一定要在行首
-
區域標簽 local labels
-
注釋
- ARM 標準匯編的注釋以 ; 開始
- GNU標準匯編的注釋以@ 開始,同時也可以使用C語言中的 /* */
-
常量
- 數字
- 字串
- 字串常量用雙引號“ ”括起來
- 布爾型
- {TRUE}
- {FALSE}
- 字符
- 使用單引號‘ ’
-
布爾型
2.2 ARM匯編的示例
2.2.1 example的匯編和鏈接
AREA ARMexample, CODE, READONLE
ENTRY
start mov r0, #10
mov r1, #3
add r0, r0, r1
stop mov r0, #0x18
ldr r1, =0x20026
swi 0x123456
end
;ARM 標準匯編語法,不做詳細解釋,讀者可自行參考相關資料
GNU 匯編語法
.section .text
.global _start
_start:
MOV r0, #10
MOV r1, #3
ADD r0, r0, r1
stop:
MOV r0, #0x18
LDR r1, =0x20026
SWI 0x123456
將該檔案保存為gnuAssembly.S,并使用as進行匯編,然后用ld進行鏈接生成可執行檔案
arm-linux-gnueabihf-as -g gnuAsExample.S -o gnuAsExample.o
arm-linux-gnueabihf-ld gnuAsExample.o -o gnuAsExample
說明:
-g: 帶有debug資訊
-o:指定輸出檔案名
2.2.2 GDB和QEMU 除錯
匯編鏈接生成gnuAsExample可執行檔案之后,我們可以使用qemu-arm仿真工具執行該檔案,
/*首先使用qemu-arm 加載并執行該可執行檔案*/
qemu-arm -g 1234 gnuAsExample
/*另外開一個shell視窗,使用GDB工具進行聯調*/
arm-linux-gnueabihf-gdb ./gnuAsExample
/*進入GDB模式后,執行如下命令*/
(gdb) target remote 127.0.0.1:1234
/*成功后,可看見如下資訊*/
(gdb) target remote 127.0.0.1:1234
Remote debugging using 127.0.0.1:1234
_start () at gnuAsExample.S:5
5 MOV r0, #10
說明:GDB工具連接的埠號需和使用qemu-arm中設定的埠號一致,在這里均為1234,詳細的GDB命令在此不再詳述,讀者可自行查閱相關資料
2.2.3 示例詳解
.section .text @宣告text段
.global _start @宣告全域變數
_start: @定義_start標簽
MOV r0, #10 @立即數10賦值給r0
MOV r1, #3 @將立即數3賦值給r0
ADD r0, r0, r1 @將r0暫存器和r1暫存器的值相加,并將結果存放至r0
stop:
MOV r0, #0x18 @將0x18放入r0
LDR r1, =0x20026 @將0x20026放入r1
SWI 0x123456 @執行軟中斷命令
說明:stop標簽后的三條指令是一個退出程式的指令序列,首先是設定r0和r1的值,然后再觸發軟體中斷,這里面會用到者兩個暫存器的值,具體這些值的含義讀者可自行查閱相關資料,在此不進行說明,讀者在這里也可以完全忽略具體的值,只需要對匯編的寫法有個整體上的認知
在GNU ARM匯編中_start標簽是默認的程式起始地址,且由于程式是通過加載器來加載的,因此必須將 _start符號宣告為全域的,這樣加載器才能找到,
2.3 Thumb 匯編示例
.section .text
.global _start
_start:
.code 32
adr r2, thumb+1
/*在前面我們有說到,由于ARM指令按字對齊,而Thumb指令按半字對齊,
因此有最低的一個bit或兩個bit為0,而在這里我們通過加1,使其不為0,
下一條bx指令正是通過這一位判斷是否需要進行ARM到Thumb狀態的轉換*/
bx r2
thumb:
.code 16
mov r0, #10
mov r1, #3
add r0, r0, r1
bkpt
下面是匯編鏈接除錯程序,共讀者參考
視窗 1
log@log:~$ arm-linux-gnueabihf-as -g gnuAsThumbEx.S -o gnuAsThumbEx.o
log@log:~$ arm-linux-gnueabihf-ld gnuAsThumbEx.o -o gnuAsThumbEx
log@log:~$ qemu-arm -g 1234 gnuAsThumbEx
視窗 2
log@log:~$ arm-linux-gnueabihf-gdb gnuAsThumbEx
GNU gdb (Linaro_GDB-2017.08) 8.0.0.20170823-git
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-unknown-linux-gnu --target=arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gnuAsThumbEx...done.
(gdb) target remote 127.0.0.1:1234
Remote debugging using 127.0.0.1:1234
_start () at gnuAsThumbEx.S:7
7 adr r2, thumb+1
(gdb) info registers r2
r2 0x0 0
(gdb) n
8 bx r2
(gdb) info registers r2
r2 0x1005d 65629
(gdb) l
3 .global _start
4
5 _start:
6 .code 32
7 adr r2, thumb+1
8 bx r2
9
10 thumb:
11 .code 16
12 mov r0, #10
(gdb) n
thumb () at gnuAsThumbEx.S:12
12 mov r0, #10
(gdb) n
13 mov r1, #3
(gdb) info registers r2
r2 0x1005d 65629
(gdb) info registers r0
r0 0xa 10
(gdb) info registers r1
r1 0xf6fff66b -150997397
(gdb) n
14 add r0, r0, r1
(gdb) info registers r0 r1
r0 0xa 10
r1 0x3 3
(gdb) n
15 bkpt
(gdb) info registers r0 r1
r0 0xd 13
r1 0x3 3
(gdb) n
3 資料處理指令
ARM指令通常有一個或兩個運算元,形如:
助記符{s}{條件} {rd} , 運算元1,運算元2
說明:
- {s}:指令加了s后綴后,意味著將會更新CPSR中的CVZN標志
- {條件}:可以決定該指令是否執行
- {rd}: 通常用于存盤結果
- 運算元1:第一個運算元,可以是暫存器或立即數
- 運算元2:第二個運算元,可以是立即數或者是一個對暫存器進行移位操作
常見的運算式:
-
1 立即數
-
rx 暫存器x,r1、r2...
-
rx, asr n 算術右移n位
-
rx,lsl n 邏輯左移n位
-
rx,lsr n 邏輯右移n位
-
rx,ror n 回圈右移n位
-
rx,rrx 回圈右移1位
3.1 資料傳輸指令
-
mov:用于兩個暫存器之間或者立即數和暫存器之間傳遞資料
-
mvn:和mov用法一致,區別在于會將第二個運算元進行按位取反后再進行傳遞
說明:
在這里特別需要說明的是使用mov和mvn在暫存器中和立即數中傳遞資料,這里的立即數是范圍是有一定的限制的,
因為ARM指令編碼格式長度微32位,拋去條件碼、操作碼、目標暫存器等,留給立即數的位數只剩下12位,因此它是沒有辦法去表示所有的32位的數的,如果按常規操作,它只能表示0x000-0xfff,但是在ARM中,并沒有按常規操作去表示這些資料,而是將其中的低8位作為基數,高4位作為右移位數(x2):
例如:0x101代表的是:0x01 回圈右移1×2位,變成:0x40000000
通過這種方法,使得12位的數可以表示比較大的資料,但是它依舊不肯呢個表示所有的資料,因此立即數是有一定的限制的,在編程時,如使用了非法立即數,匯編程序中會報告一個錯誤,
3.2 算數運算指令
- add:加法運算
- sub:減法運算
- rsb:反減運算
- adc:帶進位的加法
- sbc:帶進位的減法
- rsc:帶進位的反減
3.3 邏輯運算指令
- and:與
- orr:或
- eor:異或
- bic:位清除操作
3.4 比較指令
- cmp:比較大小
- cmn:取反運算
- tst:按位與運算
- teq:按位異或運算
3.5 乘法指令
mvl、mla、umull、umlal、smull、smlal
3.6 CPSR訪問指令
用于訪問CPSR暫存器
- mrc:用于讀出cpsr和spsr暫存器
- msr:用于寫入cpsr和spsr暫存器
說明:CPSR是程式狀態暫存器,只有一個,而SPSR暫存器在五種例外模式下各有一個,用于保存普通模式下的CPSR暫存器的值
4 記憶體訪問指令
在ARM指令集中,只有特定的加載和存盤指令允許訪問記憶體,所以在對一個資料進行操作之前,都必須將該數加載到暫存器當中去,然后進行操作,操作完成后,再更新回記憶體中,
4.1 單一暫存器加載和存盤
- ldr:可用于加載一個地址中的內容到暫存器中,也可以加載一個32bit長度的常量存盤在暫存器中
- str:主要用與將暫存器中的值寫入記憶體中
ldr是ARM中的一個指令,也同時是一個偽指令,其主要有兩種用法:
-
加載一個常量到暫存器中
例如:ldr r1,=42
在這里,ldr是一個偽指令,匯編程式會根據需加載常量的不同,而翻譯成不同的指令,具體規則是:
如果該常量能夠使用mvn或mov指令進行操作,則直接將該指令翻譯成mvn或mov
如果該常量并不能夠直接被mvn或mov指令進行操作,則匯編器首先會在附近的文字池(literal pool)中存放這么一個常量,然后在使用ldr指令進行讀取
literal pool: a portion of memory embedded in the code to hold constant value
在這里需要注意的是,編程者必須確保在LDR能夠訪問的范圍記憶體在文字池,如果不存在,在進行匯編的程序中,會報告一個錯誤,這時編程者可通過 LTORG 指令放置一個文字池,詳細資訊見下文
放置文字池
通常來說,匯編程式會在每個area的末尾放置一個文字池,但是如果該area過大的話,可能會超出部分LDR指令能夠訪問的范圍,
在ARM狀態,從PC到常量的偏移量不能超過4KB
在Thumb狀態,該偏移量不能超過1KB
.section .text .global _start _start: ldr r0, =3 ldr r1, =0x55555555 largeTable: .space 4200 .endarm-linux-gnueabihf-as -g gnuAsLiteralPool.S -o gnuAsLiteralPool.o gnuAsLiteralPool.S: Assembler messages: gnuAsLiteralPool.S:6: 錯誤: invalid literal constant: pool needs to be closer如上代碼,在用as匯編時,會產生一個error,因為在ldr r1 , =0x55555555陳述句中,不能直接用mov或mvn加載,只能通過在文字池中存放該常量,然后使用ldr指令進行加載,然而在該示例中,因為后買你有一個4200位元組的空間,使得文字池的范圍超出了ldr指令可以訪問的范圍,因此需要我們手動在適合的位置放置一個文字池,如下:
.section .text .global _start _start: ldr r0, =3 ldr r1, =0x55555555 .ltorg largeTable: .space 4200 .end在這里我們對匯編生成后的檔案進行反匯編,結果如下:
log@log:~$ arm-linux-gnueabihf-objdump gnuAsLiteralPool.o -S gnuAsLiteralPool.o: 檔案格式 elf32-littlearm Disassembly of section .text: 00000000 <_start>: .section .text .global _start _start: ldr r0, =3 0: e3a00003 mov r0, #3 ldr r1, =0x55555555 4: e51f1004 ldr r1, [pc, #-4] ; 8 <_start+0x8> 8: 55555555 .word 0x55555555 0000000c <largeTable>: ...在這里可以看見,ldr r0,=3指令被翻譯成了mov r0, #3;而ldr r1, =0x55555555,則被翻譯成了兩條指令,一個是在.word 0x55555555,一個是ldr r1, [pc, #-4] ; 8 <_start+0x8>,
-
加載一個地址
除了上一種用法,ldr更多的功能則是將某個特定地址中的內容加載進暫存器中,主要有以下用法
ldr r0,[r1] @將地址r1中的字資料加載進r0中 ldr r0,[r1,r2] @將存盤器地址為r1+r2的字資料加載進r0中 ldr r0,[r1,#8] @將地址為r1+8的字資料加載進r0中 ldr r0,[r1],r2 @將r1地址中的字資料加載進r0中,并把r1+r2的值賦值給r1 ldr r0,[r1],#8 @將r1地址中的字資料加載進r0中,并把r1+8的值賦值給r1 ldr r0,[r1,r2]! @將存盤器地址為r1+r2的字資料加載進r0中,同時把r1+r2賦值給r1 ldr r0,label @將label作為地址加載 ldr r0,=label @將label作為一個常量數進行加載
說明
除了上述ldr指令能夠加載一個地址,同樣也可以使用adr偽指令進行地址加載,這一偽指令是小范圍的地址地址加載,主要通過相對于暫存器或當前PC的偏移量進行加載,且偏移量的范圍是255位元組(如果沒有按字對齊)或者1020位元組(如果地址按字對齊)
4.2 多暫存器加載和存盤
ldm:用于從記憶體中讀取連續的多個字存放進暫存器中
stm:用于將多個暫存器中的值存盤到記憶體中去
ldm r0,{r4-r5} @將r0地址中的兩個字內容分別加載到r4,r5
stm r1,{r4-r5} @將r4-r5兩個暫存器的值寫入r1地址中
后綴說明:
ia:每次傳送后地址加4,其中的暫存器從左到右執行
ib:每次傳送前地址加4,同上
da:每次傳送后地址減4,其中的暫存器從右到左執行
db:每次傳動前地址減4,同上
!:用最后的地址更新基地址
5 條件執行
在ARM狀態,每個資料操作指令都可以通過添加‘s’后綴,根據操作結果去設定CPSR的ALU狀態標志,也就是CPSR的N、C、V、Z位
在Thumb狀態,沒有s后綴,因為所有的指令都會自動去設定這一標志,除了通過mov和mvn指令去操作高位暫存器(r8-r15)
注意:在Thumb狀態下,只能通過分支指令實作條件執行,所以下述通過添加指令后綴實作條件執行,都是處于ARM狀態
5.1 ALU狀態標志
CPSR暫存器包含了ALU以下狀態標志:
- N:當操作結果為負數時,被置1
- Z:當操作結果為0時,被置1
- C:當操作結果出現進完位時,被置1
- V:當操作導致溢位時,被置1
當加減結果大于或等于2的32次方,或者由于一定而導致僅為,則C置1
當加減結果大于或等于2的31次方,或者少于-2的31次方,則發生溢位
不要將s后綴與cmp\cmn\tst\teq一起使用,因為這些指令默認都是會去更新這一標志的,
5.2 執行條件
| 后綴 | 標志 | 意義 |
|---|---|---|
| eq | Z = 1 | 相等 |
| ne | Z = 0 | 不想等 |
| cs/hs | C = 1 | unsigned >= |
| cc/lo | C = 0 | unsigned < |
| mi | N = 1 | 負數 |
| pl | N = 0 | 0 或 正數 |
| vs | V = 1 | 溢位 |
| vc | V = 0 | 沒有溢位 |
| hi | c=1&&z=0 | unsigned > |
| ls | c =0&&z=1 | unsigned <= |
| ge | n = v | signed >= |
| lt | n != v | signed < |
| gt | z=0,n=v | signed > |
| le | z=1,n!=v | signed <= |
6 宏的使用
在GNU匯編中的宏定義格式如下:
.macro 宏名 引數串列
宏體
.endm
example:
.macro FUN_ADD a,b
add \a, \a, \b
.endm
7 GNU ARM匯編常見偽操作
7.1 .section 偽操作
.section 偽操作格式如下:
.section section_name [,"flags" [, %type [, flag_specific_arguments]]]
在匯編中預置的一些段:
.text @代碼段
.data@初始化資料段
.bss@未初始化資料段
.sdata
.sbss
.bss段應該在.text段之前
8 參考鏈接
ARM匯編基數:https://azeria-labs.com/writing-arm-assembly-part-1/
ARM官方檔案:http://infocenter.arm.com/help/index.jsp
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/45309.html
標籤:其他
