對于嵌入式開發者來說,了解匯編語言和內核寄存器是對內核深入理解的基礎
..增加 2.2 匯編偽指令 章節 2021/12/12
..完善 2.3 ARM匯編指令集 2021/12/12
..增加 3.1 不同編譯器的反匯編 2021/12/14
..增加 3.2 C和匯編 比較分析 2021/12/15
從開始寫起也沒想到內容有這么多,其中有很多干貨的東西,希望自己能夠說明到了,
其中有很多推薦的博文和網站,在此要特別感謝韋東山老師的視頻,絕對干貨滿滿
- 一、ARM內核寄存器
- 1.1 M3/M4內核寄存器
- 1.2 A7內核寄存器
- 1.3 ARM中的PC指針的值
- 二、ARM匯編語言
- 2.1 ARM匯編基礎
- 2.2 匯編偽指令
- 2.3 ARM匯編指令集
- 三、代碼反匯編簡析
- 3.1 不同編譯器的反匯編
- 3.2 C 和 匯編 比較分析
開頭直接來看幾個簡單的匯編指令:
MOV R0,R1``MOV PC,R14
上面的指令中使用了匯編 MOV
指令,但是其中的 R0,R1,R14,PC分別是什么?哪來的?怎么用?
要講 ARM 匯編語言,必須得先了解ARM的內核寄存器,內核處理所有的指令計算,都需要用到內核寄存器,所以ARM匯編里面指令大都是基于寄存器的操作。
文章前推薦韋東山老師的單片機核心視頻,視頻可以在韋東山老師官網里面找到:百問網
ARM版本簡單介紹:
內核(架構)版本 | 處理器版本 |
---|---|
ARMv1 | ARM1 |
ARMv2 | ARM2、ARM3 |
ARMv3 | ARM6、 |
ARMv4 | ARM7、StrongARM |
ARMv5 | ARM9、ARM10E |
ARMv6 | ARM11 |
ARMv7 | ARM Cortex-A、ARM Cortex-M、ARM Cortex-R |
ARMv8 | ARM Cortex-A30、ARM Cortex-A50、ARM Cortex-A70 |
一、ARM內核寄存器
內核寄存器與外設寄存器:
內核寄存器與外設寄存器是完全不同的概念。內核寄存器是指 CPU 內部的寄存器,CPU處理所有指令數據需要用到這些寄存器保存處理數據;外設寄存器是指的 串口,SPI,GPIO口這些設備有關的寄存器。
在我的另一篇博文:FreeRTOS記錄(三、FreeRTOS任務調度原理解析_Systick、PendSV、SVC)內核中斷管理 章節講到過Cortex-M的寄存器
的相關內容,這里我們再簡單說明一下:
1.1 M3/M4內核寄存器
對于M3/M4而言:R13,棧指針(Stack Pointer)
- R13寄存器中存放的是棧頂指針,M3/M4 的棧是向下生長的,入棧的時候地址是往下減少的。
- 裸機程序不會用到PSP,只用到MSP,需要運行RTOS的時候才會用到PSP。
- 堆棧主要是通過POP,PUSH指令來進行操作。在執行 PUSH 和 POP 操作時, SP 的地址寄存器,會自動調整。
R14 ,連接寄存器(Link Register)
- LR 用于在調用子程序時存儲返回地址。例如,在使用 BL(分支并連接, Branch and Link)指令時,就自動填充 LR 的值(執行函數調用的下一指令),進而在函數退出時,正確返回并執行下一指令。如果函數中又調用了其他函數,那么LR將會被覆蓋,所以需要先將LR寄存器入棧。
- 保存子程序返回地址。使用BL或BLX時,跳轉指令自動把返回地址放入r14中;子程序通過把r14復制到PC來實現返回
- 當異常發生時,異常模式的r14用來保存異常返回地址,將r14如棧可以處理嵌套中斷
R15,程序計數器(Program Count)
- 在Cortex-M3中指令是3級流水線,出于對Thumb代碼的兼容的考慮,讀取pc時,會返回當前指令地址+4的值。
- 讀 PC 時返回的值是當前指令的地址+4,關于M3、M4 和 A7的 PC值的問題需要單獨來解釋一下
其中程序狀態寄存器 XPSR:
程序狀態寄存器,該寄存器由三個程序狀態寄存器組成 應用PSR(APSR) :包含前一條指令執行后的條件標志,比較結果:大于等于,小于,進位等等; 中斷PSR(IPSR ) :包含當前ISR的異常編號 執行PSR(EPSR) :包含Thumb狀態位
1.2 A7內核寄存器
對于 A7 而言:(上圖取自原子教材,此圖在官方文檔《ARM Cortex-A(armV7)編程手冊V4.0》中第3章.ARM Processor Modes and Registers 部分有英文原版,這里用中文版本更容易理解)
A7的 R13、R14、R15 的作用和 M3/4類似。
需要注意的一點就是,對于A7而言 R15,程序計數器(Program Count) :
- 讀 PC 時返回的值是當前指令的地址+8, PC 指向當前指令的下兩條指令地址。
- 由于ARM指令總是以字對齊的,故PC寄存器 bit[1:0] 總是00。
A7內核的程序狀態寄存器 CPSR:
1.3 ARM中的PC指針的值
因為ARM指令采用三級流水線機制,所以PC指針的值并不是當前執行的指令的地址值:
- 當前執行地址A的指令,
- 同時已經在對下一條指令進行譯碼,
- 同時已經在讀取下下一條指令:PC = A +4 (Thumb/Thumb2指令集)、PC = A + 8 (ARM指令集)
在文檔《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》中對于 PC 的值有明確的說明:
M3/M4/M0:
PC的值 = 當前地址 + 4;
下面是一個 STM32F103 反匯編程序,找了一段有[pc,#0]的代碼,方便判斷:
A7:
PC的值 = 當前地址 + 8;
二、ARM匯編語言
ARM芯片屬于精簡指令集計算機(RISC:Reduced Instruction Set Computing),具體說明在下面這篇博文5.4小結有過說明:
STM32的內存管理相關(內存架構,內存管理,map文件分析)
2.1 ARM匯編基礎
2.1.1 ARM指令集說明
最初,ARM公司發布了兩類指令集:
- ARM指令集,32位的ARM指令,每條指令占據32位,高效,但是太占空間;
- Thumb指令集,16位的Thumb指令,每條指令占據16位,節省空間;
比如:MOV R0,R1
這條指令,可能是16位的,也可能是32位的
那么在匯編中是如何在 ARM 指令 和 Thumb 指令之間切換呢:
/*ARM指令 與 Thumb 指令 的切換*/
CODE16 ;(表示下面是 Thumb 指令)
...
...
;(調用下面的B函數)
bx B_addr;(B的地址B_addr的bit0 = 0,表示跳轉過去執行 ARM 指令)
;A 函數
...
CODE32 ;(表示下面是 ARM 指令)
...
...
;B 函數
;(回到上面的A函數)
bx A_addr + 1 ;(A的地址A_addr的bit0 = 1,表示跳轉過去執行 Thumb 指令)
...
/**********************/
對于A7、ARM7、ARM9 內核而言它們支持 16位的Thumb 指令集 和 32位的 ARM 指令集
對于M3、M4 內核而言它們支持的是 Thumb2 指令集,它支持16位、32位指令混合編程
對于內核來說使用的是 ARM指令集 還是 Thumb指令集,就是在 XPSR 和 CPSR
在M3/M4中, XPSR 寄存器的 T(bit24):1表示 Thumb指令集根據上面所述,M3是使用的 Thumb2 指令集,所以會有 T 總是 1.
在A7中 CPSR中的:T(bit5) :控制指令執行狀態,表明本指令是 ARM 指令還是 Thumb 指令,通常和 J(bit24)一起表明指令類型
J(bit24) | T(bit5) | 指令集 |
---|---|---|
0 | 0 | ARM |
0 | 1 | Thumb |
1 | 1 | ThumbEE -- 提供從Thumb-2而來的一些擴充性,在所處的運行環境下,使得指令集能特別適用于運行階段的編碼產生(例如實時編譯)。Thumb-2EE是專為一些語言如Limbo、Java、C#、Perl和Python,并能讓實時編譯器能夠輸出更小的編譯碼卻不會影響到性能。 |
1 | 0 | Jazelle |
回到開始的指令 MOV R0,R1
code 16 ;(表示下面指令是16位的 Thumb 指令)
MOV R0,R1
code 32 ;(表示下面指令是32位的 ARM 指令)
MOV R0,R1
Thumb ;(編譯器會根據指令自動識別是32位還是16位的 Thumb2)
MOV R0,R1
2.1.2 ARM匯編格式
編碼格式:
不同指令集的編碼格式(以 LDR 為例),摘自《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》:以“數據處理”(其他的還有內存訪問,分支跳轉等)指令為例,UAL匯編格式為:Operation
表示各類匯編指令,比如 ADD、MOV;cond表示conditon,即該指令執行的條件,如 EQ,NE 等;S表示該指令執行后,是否會影響CPSR寄存器的值, 是否影響CPSR 寄存器的值,書寫時影響CPSR,否則不影響;Rd
為目的寄存器,用來存儲運算的結果;Rn 第一個操作數的寄存器Operand2第二個操作數 ,其可以有3種操作源:1-- 立即數 2-- 寄存器 3-- 寄存器移位
其指令編碼格式如下(32位):|bit 31-28 |27-25 |24-21 |20 |19-16 | 15-12 |11-0 | |--|--|--|--|--|--|--|--|--| |cond | 001 |Operation |S |Rn |Rd | Operand2 |
舉個例子:
...
CMP R0,R2 ;比較R0和R2的值
MOV EQ R0,R1 ;加上EQ,如果上面R0的值和R2的值相等的話,才執行此語句
...
對于“數據處理”處理指令中的Operation ,指令集如下:對于其中的條件cond ,如下:
2.1.3 立即數
在一條ARM數據處理指令中,除了要包含處理的數據值外,還要標識ARM命令名稱,控制位,寄存器等其他信息。這樣在一條ARM數據處理指令中,能用于表示要處理的數據值的位數只能小于32位;
在上面的ARM匯編格式中我們介紹過,ARM在指令格式中設定,只能用指令機器碼32位中的低12位來表示要操作的常數。
那么對于指令MOV R0, #value
(把value的值存入R0寄存器)而言,value 的值也不能是任意的值,其值只能是符合某些規定的數,在官方文檔中 value 的值需要滿足如下條件:什么是立即數?
滿足上圖中條件的數我們稱之為 立即數,立即數就是符合一定規矩的數。
立即數表示方式:每個立即數由一個8位的常數循環右移偶數位得到。其中循環右移的位數由一個4位二進制的兩倍表示。
立即數 = 一個8位的常數 循環位移 偶數位
一個8bit常數循環右移(Y*2 = {0,2,4,6,8, ...,26, 28, 30})就得到一個立即數了;(為什么是0到30的偶數下面解釋)
如果需要深入理解立即數,推薦一篇博文:深刻認識 -->> 立即數
ARM處理器是按32位來處理數據的,ARM處理器處理的數據是32位,為了擴展到32位,因此使用了構造的方法,在12位中用8位表示基本數據值,用4位表示位移值,通過用8位基本數據值往右循環移動4位位移值*2次,來表示要操作的常數。
這里要強調最終的循環次數是4位位移值乘以2得到的,所以得到的最終循環次數肯定是一個偶數,為什么要乘以2呢,實質還是因為范圍不夠,4位表示位移次數,最大才15次(移位0,等于沒有循環),加上8位數據還是不夠32位,這樣只能通過ALU的內部結構設計將4位位移次數乘以2,這樣就能用12位表示32位常數了。
所以 12bit 數據存放格式如下:|bit 11-8 |7-0 | |--|--|--|--|--|--|--|--|--| |移位 1111b (0~15) | 8bit常數 |
但是我們去判斷一個數是否立即數,實在是太麻煩了,但是我們想把任意數值賦給 R0 寄存器,怎么辦? 這就需要用到偽指令了,下面說一說什么是偽指令。
-
ARM
+關注
關注
134文章
9111瀏覽量
368081 -
寄存器
+關注
關注
31文章
5357瀏覽量
120715 -
PC
+關注
關注
9文章
2093瀏覽量
154364 -
匯編語言
+關注
關注
14文章
410瀏覽量
35862
發布評論請先 登錄
相關推薦
評論