欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
ARM內核寄存器&基本匯編語(yǔ)言講解

對于嵌入式開(kāi)發(fā)者來(lái)說(shuō),了解匯編語(yǔ)言和內核寄存器是對內核深入理解的基礎。從開(kāi)始寫(xiě)起也沒(méi)想到內容有這么多,其中有很多干貨的東西,希望自己能夠說(shuō)明到了。其中有很多推薦的博文和網(wǎng)站,在此要特別感謝韋東山老師的視頻,絕對干貨滿(mǎn)滿(mǎn)!

本文目錄:

  • 一、ARM內核寄存器

    • 1.1 M3/M4內核寄存器

    • 1.2 A7內核寄存器

    • 1.3 ARM中的PC指針的值

  • 二、ARM匯編語(yǔ)言

    • 2.1 ARM匯編基礎

    • 2.2 匯編偽指令

    • 2.3 ARM匯編指令集

  • 三、代碼反匯編簡(jiǎn)析

    • 3.1 不同編譯器的反匯編

    • 3.2 C和匯編比較分析

我們先來(lái)看幾個(gè)簡(jiǎn)單的匯編指令:

MOV R0,R1MOV PC,R14

上面的指令中使用了匯編 MOV指令,但是其中的 R0,R1,R14,PC分別是什么?哪來(lái)的?怎么用?

要講 ARM 匯編語(yǔ)言,必須得先了解ARM的內核寄存器,內核處理所有的指令計算,都需要用到內核寄存器,所以ARM匯編里面指令大都是基于寄存器的操作。

ARM版本簡(jiǎn)單介紹:

內核(架構)版本處理器版本
ARMv1ARM1
ARMv2ARM2、ARM3
ARMv3ARM6、
ARMv4ARM7、StrongARM
ARMv5ARM9、ARM10E
ARMv6ARM11
ARMv7ARM Cortex-A、ARM Cortex-M、ARM Cortex-R
ARMv8ARM Cortex-A30、ARM Cortex-A50、ARM Cortex-A70

一、ARM內核寄存器

內核寄存器與外設寄存器:

內核寄存器與外設寄存器是完全不同的概念。內核寄存器是指 CPU 內部的寄存器,CPU處理所有指令數據需要用到這些寄存器保存處理數據;外設寄存器是指的 串口,SPI,GPIO口這些設備有關(guān)的寄存器。

在我的另一篇博文:FreeRTOS記錄(三、FreeRTOS任務(wù)調度原理解析_Systick、PendSV、SVC)內核中斷管理章節講到過(guò)Cortex-M的寄存器的相關(guān)內容,這里我們再簡(jiǎn)單說(shuō)明一下:

1.1 M3/M4內核寄存器

對于M3/M4而言:

R13,棧指針(Stack Pointer)
  • R13寄存器中存放的是棧頂指針,M3/M4 的棧是向下生長(cháng)的,入棧的時(shí)候地址是往下減少的。
  • 裸機程序不會(huì )用到PSP,只用到MSP,需要運行RTOS的時(shí)候才會(huì )用到PSP。
  • 堆棧主要是通過(guò)POP,PUSH指令來(lái)進(jìn)行操作。在執行 PUSH 和 POP 操作時(shí), SP 的地址寄存器,會(huì )自動(dòng)調整。

R14 ,連接寄存器(Link Register)

  • LR 用于在調用子程序時(shí)存儲返回地址。例如,在使用 BL(分支并連接, Branch and Link)指令時(shí),就自動(dòng)填充 LR 的值(執行函數調用的下一指令),進(jìn)而在函數退出時(shí),正確返回并執行下一指令。如果函數中又調用了其他函數,那么LR將會(huì )被覆蓋,所以需要先將LR寄存器入棧。

  • 保存子程序返回地址。使用BL或BLX時(shí),跳轉指令自動(dòng)把返回地址放入r14中;子程序通過(guò)把r14復制到PC來(lái)實(shí)現返回

  • 當異常發(fā)生時(shí),異常模式的r14用來(lái)保存異常返回地址,將r14如??梢蕴幚砬短字袛?/p>

R15,程序計數器(Program Count)

  • 在Cortex-M3中指令是3級流水線(xiàn),出于對Thumb代碼的兼容的考慮,讀取pc時(shí),會(huì )返回當前指令地址+4的值。
  • 讀 PC 時(shí)返回的值是當前指令的地址+4,關(guān)于M3、M4 和 A7的 PC值的問(wèn)題需要單獨來(lái)解釋一下。
其中程序狀態(tài)寄存器  XPSR:
程序狀態(tài)寄存器,該寄存器由三個(gè)程序狀態(tài)寄存器組成 應用PSR(APSR) :包含前一條指令執行后的條件標志,比較結果:大于等于,小于,進(jìn)位等等;中斷PSR(IPSR ) :包含當前ISR的異常編號 執行PSR(EPSR) :包含Thumb狀態(tài)位

1.2 A7內核寄存器

對于 A7 而言:

(上圖取自原子教材,此圖在官方文檔《ARM Cortex-A(armV7)編程手冊V4.0》中第3章.ARM Processor Modes and Registers 部分有英文原版,這里用中文版本更容易理解)

A7的 R13、R14、R15 的作用和 M3/4類(lèi)似。

需要注意的一點(diǎn)就是,對于A(yíng)7而言**R15,程序計數器(Program Count)**:

  • 讀 PC 時(shí)返回的值是當前指令的地址+8, PC 指向當前指令的下兩條指令地址。
  • 由于A(yíng)RM指令總是以字對齊的,故PC寄存器 bit[1:0] 總是00。

A7內核的程序狀態(tài)寄存器  CPSR:


1.3 ARM中的PC指針的值

因為ARM指令采用三級流水線(xiàn)機制,所以PC指針的值并不是當前執行的指令的地址值:

  1. 當前執行地址A的指令,
  2. 同時(shí)已經(jīng)在對下一條指令進(jìn)行譯碼,
  3. 同時(shí)已經(jīng)在讀取下下一條指令:PC = A +4 (Thumb/Thumb2指令集)、PC = A + 8 (ARM指令集)
在文檔《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》中對于 PC 的值有明確的說(shuō)明:


M3/M4/M0:

PC的值 = 當前地址 + 4;

下面是一個(gè) STM32F103 反匯編程序,找了一段有[pc,#0]的代碼,方便判斷:
A7:
PC的值 = 當前地址 + 8;

二、ARM匯編語(yǔ)言

ARM芯片屬于精簡(jiǎn)指令集計算機(RISC:Reduced Instruction Set Computing),具體說(shuō)明在下面這篇博文5.4小結有過(guò)說(shuō)明:

STM32的內存管理相關(guān)(內存架構,內存管理,map文件分析)

2.1 ARM匯編基礎

2.1.1 ARM指令集說(shuō)明

最初,ARM公司發(fā)布了兩類(lèi)指令集:

  1. ARM指令集,32位的ARM指令,每條指令占據32位,高效,但是太占空間;
  2. Thumb指令集,16位的Thumb指令,每條指令占據16位,節省空間;

比如:MOV R0,R1這條指令,可能是16位的,也可能是32位的

那么,在匯編中是如何在 ARM 指令 和 Thumb 指令之間切換呢?

/*ARM指令 與 Thumb 指令 的切換*/

CODE16  ;(表示下面是 Thumb 指令)
...
...

;(調用下面的B函數)
bx  B_addr;(B的地址B_addr的bit0 = 0,表示跳轉過(guò)去執行 ARM 指令)
;A 函數
...

CODE32  ;(表示下面是 ARM 指令)
...
...
;B 函數
;(回到上面的A函數)
bx  A_addr + 1 ;(A的地址A_addr的bit0 = 1,表示跳轉過(guò)去執行 Thumb 指令)
...

/**********************/

對于A(yíng)7、ARM7、ARM9 內核而言它們支持 16位的Thumb 指令集 和 32位的 ARM 指令集。

對于M3、M4 內核而言它們支持的是 Thumb2 指令集,它支持16位、32位指令混合編程。

對于內核來(lái)說(shuō)使用的是 ARM指令集 還是 Thumb指令集,就是在 XPSR 和 CPSR。

在M3/M4中, XPSR 寄存器的 T(bit24):1表示 Thumb指令集。



根據上面所述,M3是使用的 Thumb2 指令集,所以會(huì )有 T 總是 1。

在A(yíng)7中 CPSR中的:T(bit5) :控制指令執行狀態(tài),表明本指令是 ARM 指令還是 Thumb 指令,通常和 J(bit24)一起表明指令類(lèi)型。


J(bit24)T(bit5)指令集
00ARM
01Thumb
11ThumbEE  --                                              提供從Thumb-2而來(lái)的一些擴充性,在所處的運行環(huán)境下,使得指令集能特別適用于運行階段的編碼產(chǎn)生(例如實(shí)時(shí)編譯)。Thumb-2EE是專(zhuān)為一些語(yǔ)言如Limbo、Java、C#、Perl和Python,并能讓實(shí)時(shí)編譯器能夠輸出更小的編譯碼卻不會(huì )影響到性能。
10Jazelle

回到開(kāi)始的指令 MOV R0,R1

code 16  ;(表示下面指令是16位的 Thumb 指令)
MOV R0,R1
code 32  ;(表示下面指令是32位的 ARM 指令)
MOV R0,R1
Thumb    ;(編譯器會(huì )根據指令自動(dòng)識別是32位還是16位的 Thumb2)
MOV R0,R1

2.1.2 ARM匯編格式

編碼格式:

不同指令集的編碼格式(以 LDR 為例),摘自《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》:



以“數據處理”(其他的還有內存訪(fǎng)問(wèn),分支跳轉等)指令為例,UAL匯編格式為:



Operation
表示各類(lèi)匯編指令,比如 ADD、MOV;cond表示conditon,即該指令執行的條件,如 EQ,NE 等;S表示該指令執行后,是否會(huì )影響CPSR寄存器的值, 是否影響CPSR 寄存器的值,書(shū)寫(xiě)時(shí)影響CPSR,否則不影響;Rd
為目的寄存器,用來(lái)存儲運算的結果;Rn第一個(gè)操作數的寄存器Operand2第二個(gè)操作數 ,其可以有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 |

舉個(gè)例子:

...
CMP R0,R2      ;比較R0和R2的值
MOV EQ R0,R1  ;加上EQ,如果上面R0的值和R2的值相等的話(huà),才執行此語(yǔ)句
...
對于“數據處理”處理指令中的Operation,指令集如下:


對于其中的條件cond,如下:

2.1.3 立即數

在一條ARM數據處理指令中,除了要包含處理的數據值外,還要標識ARM命令名稱(chēng),控制位,寄存器等其他信息。這樣在一條ARM數據處理指令中,能用于表示要處理的數據值的位數只能小于32位;

在上面的ARM匯編格式中我們介紹過(guò),ARM在指令格式中設定,只能用指令機器碼32位中的低12位來(lái)表示要操作的常數。


那么,對于指令MOV R0, #value(把value的值存入R0寄存器)而言,value 的值也不能是任意的值,其值只能是符合某些規定的數,在官方文檔中 value 的值需要滿(mǎn)足如下條件:


什么是立即數?

滿(mǎn)足上圖中條件的數我們稱(chēng)之為 立即數,立即數就是符合一定規矩的數。

立即數表示方式:每個(gè)立即數由一個(gè)8位的常數循環(huán)右移偶數位得到。其中循環(huán)右移的位數由一個(gè)4位二進(jìn)制的兩倍表示。

立即數 =  一個(gè)8位的常數  循環(huán)位移  偶數位

一個(gè)8bit常數循環(huán)右移(Y*2 = {0,2,4,6,8, ...,26, 28, 30})就得到一個(gè)立即數了;(為什么是0到30的偶數下面解釋?zhuān)?/p>

如果需要深入理解立即數,推薦一篇博文:深刻認識 -->> 立即數

ARM處理器是按32位來(lái)處理數據的,ARM處理器處理的數據是32位,為了擴展到32位,因此使用了構造的方法,在12位中用8位表示基本數據值,用4位表示位移值,通過(guò)用8位基本數據值往右循環(huán)移動(dòng)4位位移值*2次,來(lái)表示要操作的常數。

這里要強調最終的循環(huán)次數是4位位移值乘以2得到的,所以得到的最終循環(huán)次數肯定是一個(gè)偶數,為什么要乘以2呢,實(shí)質(zhì)還是因為范圍不夠,4位表示位移次數,最大才15次(移位0,等于沒(méi)有循環(huán)),加上8位數據還是不夠32位,這樣只能通過(guò)ALU的內部結構設計將4位位移次數乘以2,這樣就能用12位表示32位常數了。

所以 12bit 數據存放格式如下:|bit 11-8  |7-0 | |--|--|--|--|--|--|--|--|--| |移位 1111b (0~15) | 8bit常數 |

但是我們去判斷一個(gè)數是否立即數,實(shí)在是太麻煩了,但是我們想把任意數值賦給 R0 寄存器,怎么辦?  這就需要用到偽指令了,下面說(shuō)一說(shuō)什么是偽指令。

2.2 匯編偽指令

匯編語(yǔ)言分成兩塊:標準指令集和非標準指令集。偽指令屬于非標準指令集。

什么是偽指令?

類(lèi)似于宏的東西,把復雜的有好幾天指令進(jìn)行跳轉的完成的小功能級進(jìn)行新的標簽設定,這就是偽指令。

類(lèi)似于學(xué)c語(yǔ)言的時(shí)候的預處理,在預處理的時(shí)候把它定義于一堆的宏轉化為真正的c語(yǔ)言的代碼。同樣,偽指令是在定義好之后的匯編,匯編的時(shí)候會(huì )把它翻譯成標準指令,也許一條簡(jiǎn)單的偽指令可以翻譯成很多條標準的匯編指令集,所以這就是偽指令最重要的作用。

我們前面說(shuō)的 CODE16CODE32也是偽指令,用來(lái)指定其后的代碼格式。

偽指令的作用?

基本的指令可以做各類(lèi)操作了,但操作起來(lái)太麻煩了。偽指令定義了一些類(lèi)似于帶參數的宏,能夠更好的實(shí)現匯編程序邏輯。(比如我現在要設置一個(gè)值給寄存器R0,但下次我修改了寄存器R0之后又需要讀出來(lái)剛才的值,那我們就要先臨時(shí)保存值到SPSR,CPSR,然后不斷切換。)

偽指令只是在匯編器之前作用,匯編以后翻譯為標準的匯編令集。

偽指令的類(lèi)別偽指令可分為ARM匯編偽指令和GNU匯編偽指令。

ARM匯編偽指令是ARM公司的,GNU匯編偽指令是GNU平臺的。他們有自己的匯編器,不同的匯編器的解釋語(yǔ)法可以設成不同。

2.2.1 GNU匯編偽指令

這里列出部分偽指令說(shuō)明,具體的偽指令可以結合 ARM匯編偽指令分析:
bit 11-87-0
.word分配一個(gè)4字節空間
.byte定義單字節數據
.short定義雙字節數據
.long定義一個(gè)4字節數據
.equ賦值語(yǔ)句:.equ a, 0x11
.align數據字節對齊:.align 4 (4字節對齊)
.global定義全局符號:.global Default_Handler
.end源文件結束

2.2.2 ARM匯編偽指令

在我的另一篇博文:STM32的啟動(dòng)過(guò)程(startup_xxxx.s文件解析)

里面有過(guò)一些對偽指令意思的的說(shuō)明,下面也列出部分說(shuō)明:

AREA:

用于定義一個(gè)代碼段或數據段。屬性字段表示該代碼段(或數據段)的相關(guān)屬性,多個(gè)屬性用逗號分隔。

其中,段名若以數字開(kāi)頭,則該段名需用?“?|?”?括起來(lái):

ALIGN:
ALIGN?偽指令可通過(guò)添加填充字節的方式,使當前位置滿(mǎn)足一定的對其方式。其中,表達式的值用于指定對齊方式,可能的取值為2的冪,如?1?、2?、4?、8?、16?等。

EE芯視頻推薦
視頻:【艾邁斯歐司朗】3D傳感與AR/VR
若未指定表達式,則將當前位置對齊到下一個(gè)字的位置。
CODE16和CODE32:

指定其后面的指令為 ARM 指令還是?Thumb?指令,前面介紹過(guò)。

ENTRY:

用于指定匯編程序的入口點(diǎn)。在一個(gè)完整的匯編程序中至少要有一個(gè)?ENTRY?(也可以有多個(gè),當有多個(gè)?ENTRY?時(shí),程序的真正入口點(diǎn)由鏈接器指定),但在一個(gè)源文件里最多只能有一個(gè)?ENTRY。

startup_stm32f103xg.s里面就沒(méi)有。

END:

用于通知編譯器已經(jīng)到了源程序的結尾。
IMPORT 和 EXPORT:
IMPORT  定義表示這是一個(gè)外部變量的標號,不是在本程序定義的 EXPORT 表示本程序里面用到的變量提供給其他模塊調用的。

2.2.3 LDRADR

LDR偽指令:

簡(jiǎn)單介紹了偽指令基礎,回到上一小結留下的問(wèn)題,想要把任意值復制給 R0,怎么處理,我們使用偽指令:LDR R0, =value

編譯器會(huì )把“偽指令”替換成真實(shí)的指令:

LDR R0, =0x12
0x12是立即數,那么替換為:MOV R0, #0x12

LDR R0, =0x123456780x12345678不是立即數,那么替換為:LDR R0, [PC, #offset] // 2. 使用Load Register讀內存指令讀出值,offset是鏈接程序時(shí)確定的 ……Label DCD 0x12345678// 1. 編譯器在程序某個(gè)地方保存有這個(gè)值

ADR偽指令:

ADR的意思是:address,用來(lái)讀某個(gè)標號的地址:ADR{cond} Rd, labe1

ADR  R0,  Loop
...
Loop
ADD  R0, R0, #1

;(它是“偽指令”,會(huì )被轉換成某條真實(shí)的指令,比如:)
ADD R0, PC, #val   ; loop的地址等于PC值加上或者減去val的值,val的值在鏈接時(shí)確定,
...
Loop
ADD  R0, R0, #1

2.3 ARM匯編指令集

在《ARM Cortex-M3與Cortex-M4權威指南》一文中第5章節有詳細的指令集說(shuō)明:
匯編指令可以分為幾大類(lèi):數據處理、內存訪(fǎng)問(wèn)、跳轉、飽和運算、其他指令。

數據傳輸命令 MOV

MOV指令,用于將數據從一個(gè)寄存器拷貝到另外一個(gè)寄存器,或者將一個(gè)立即數傳遞到寄存器。

MOV指令的格式為:MOV{條件}{S} 目的寄存器,源操作數。

MOV R0,R1     ;@將寄存器R1中的數據傳遞給R0,即R0=R1
MOV R0, #0X12  ;@將立即數0X12傳遞給R0寄存器,即R0=0X12

狀態(tài)寄存器訪(fǎng)問(wèn) MRS 和 MSR

MRS指令,用于將特殊寄存器(如CPSR和SPSR)中的數據傳遞給通用寄存器。

MSR指令,和MRS相反,用來(lái)將普通寄存器的數據傳遞給特殊寄存器。


;M3/M4
MRS  R0, APSR  ;單獨讀APSR
MRS  R0,  PSR  ; 讀組合程序狀態(tài)

;A7
MRS  R0, CPSR  ; 讀組合程序狀態(tài)

...
MSR CPSR,R0   ;傳送R0的內容到CPSR

存儲器訪(fǎng)問(wèn) LDR 和 STR

LDR:

LDR 指令用于從存儲器中將一個(gè)32位的字數據傳送到目的寄存器中。該指令通常用于從存儲器中讀取32位的字數據到通用寄存器,然后對數據進(jìn)行處理。

指令的格式為:LDR{條件} 目的寄存器,<存儲器地址>

當程序計數器PC作為目的寄存器時(shí),指令從存儲器中讀取的字數據被當作目的地址,從而可以實(shí)現程序流程的跳轉。

LDRB: 字節操作

LDRH: 半字操作

LDR Rd, [Rn , #offset] ;從存儲器Rn+offset的位置讀取數據存放到Rd中。
...
LDR R0, =0X02077004 ;偽指令,將寄存器地址 0X02077004 加載到 R0 中,即 R0=0X02077004
LDR R1, [R0]        ;讀取地址 0X02077004 中的數據到 R1 寄存器中
...
LDR R0,[R1,R2]      ;將存儲器地址為R1+R2的字數據讀入寄存器R0。
LDR  R0,[R1,#8]     ;將存儲器地址為R1+8的字數據讀入寄存器R0。
...
LDR  R0,[R1,R2,LSL#2]! ;將存儲器地址R1+R2×4的字數據讀入寄存器R0,并將新地址R1+R2×4寫(xiě)入R1。
LDR  R0,[R1],R2,LSL#2  ;將存儲器地址R1的字數據讀入寄存器R0,并將新地址R1+R2×4寫(xiě)入R1。
...
LDRH R0,[R1]      ;將存儲器地址為R1的半字數據讀入寄存器R0,并將R0的高16位清零。
STR:

STR 指令用于從源寄存器中將一個(gè)32位的字數據傳送到存儲器中。該指令在程序設計中比較常用,且尋址方式靈活多樣,使用方式可參考指令LDR。

指令的格式為:STR{條件} 源寄存器,<存儲器地址>

STRB: 字節操作,從源寄存器中將一個(gè)8位的字節數據傳送到存儲器中。該字節數據為源寄存器中的低8位。

STRH: 半字操作,從源寄存器中將一個(gè)16位的半字數據傳送到存儲器中。該半字數據為源寄存器中的低16位。
STR Rd, [Rn, #offset] ;將Rd中的數據寫(xiě)入到存儲器中的Rn+offset位置。
...
LDR R0, =0X02077004 ;將寄存器地址 0X02077004 加載到 R0 中,即 R0=0X02077004
LDR R1, =0X2000060c ;R1 保存要寫(xiě)入到寄存器的值,即 R1=0X2000060c
STR R1, [R0]        ;將 R1 中的值寫(xiě)入到 R0 中所保存的地址中
...
STR R0,[R1],#8  ;將R0中的字數據寫(xiě)入以R1為地址的存儲器中,并將新地址R1+8寫(xiě)入R1。
STR R0,[R1,#8]  ;將R0中的字數據寫(xiě)入以R1+8為地址的存儲器中。
...

壓棧和出棧 PUSH 和 POP

PUSH :

壓棧,將寄存器中的內容,保存到堆棧指針指向的內存上面,將寄存器列表存入棧中。

PUSH < reg list >

POP :

出棧,從棧中恢復寄存器列表

POP < reg list >

push {R0, R1}   ;保存R0,R1
push {R0~R3,R12} ;保存 R0~R3 和 R12,入棧
pop {R0~R3}       ;恢復R0 到 R3 ,出棧

以M3內核來(lái)舉個(gè)例子:

假設當前 MSP 值為  0x2000 2480;寄存器 R0 的值為 0x3434 3434 寄存器 R1 的值為 0x0000 1212 寄存器 R2 的值為 0x0000 0000

執行push {R0, R1,R2}之后,

內存地址的數據為:0x2000 2474的值為: 0x3434 3434  (R0的值) 0x2000 2478的值為: 0x0000 1212  (R1的值) 0x2000 247C的值為: 0x0000 0000 (R2的值) MSP 的值變成  0x2000 2474

高位寄存器保存到高地址,先入棧,如果是POP,數據先出到低位寄存器。

跳轉指令 B 和 BL

B :

ARM 處理器將立即跳轉到指定的目標地址,不再返回原地址。

B指令的格式為:B{條件} 目標地址

注意,存儲在跳轉指令中的實(shí)際值是相對當前PC值的一個(gè)偏移量,而不是一個(gè)絕對地址,它的值由匯編器來(lái)計算。

//設置棧頂指針后跳轉到C語(yǔ)言
_start:
ldr sp,=0X80200000  ;設置棧指針
b main          ;跳到 main 函數

BL :

BL 跳轉指令,在跳轉之前會(huì )在寄存器LR(R14)中保存當前PC寄存器值,所以可以通過(guò)將LR 寄存器中的值重新加載到PC中來(lái)繼續從跳轉之前的代碼處運行,是子程序調用的常用的方法。

BL loop  ;跳轉到標號loop處執行時(shí),同時(shí)將當前的PC值保存到R14中

BLX:

該跳轉指令是當子程序使用Thumb指令集,而調用者使用ARM指令集時(shí)使用。

BLX指令從ARM指令集跳轉到指令中所指定的目標地址,并將處理器的工作狀態(tài)有ARM狀態(tài)切換到Thumb狀態(tài),該指令同時(shí)將PC的當前內容保存到寄存器R14中。

BX:

BX指令跳轉到指令中所指定的目標地址,目標地址處的指令既可以是ARM指令,也可以是Thumb指令。

算數運算指令

算數運算指令和下面的邏輯運算指令表格摘自《【正點(diǎn)原子】I.MX6U嵌入式Linux驅動(dòng)開(kāi)發(fā)指南》。

邏輯運算指令


三、代碼反匯編簡(jiǎn)析

  • 匯編 匯編文件轉換為目標文件(里面是機器碼,機器碼是給CPU使用的,燒錄保存在Flash空間的就是機器碼)。
  • 反匯編 可執行文件(目標文件,里面是機器碼),轉換為匯編文件。

3.1 不同編譯器的反匯編

3.1.1 Keil下面生成反匯編文件

fromelf –text -a -c –output=(改成你想生成的反匯編名字一般是工程名字).dis (需要的axf文件,根據你工程生成axf的路徑填寫(xiě)).axf


設置好以后編譯之后就會(huì )生成反匯編.dis文件:


打開(kāi)如下所示:


對于上圖中的紅色圈出來(lái)的語(yǔ)句,我們可以根據本文 第 二 章節的第2小節 ARM匯編格式中的介紹來(lái)分析一下:


簡(jiǎn)單分析如下(立即數就不分析了= =?。?/p>


3.1.2 gcc下生成反匯編文件

在X86架構下的電腦上生成ARM架構的匯編代碼有兩種方式:

  • 使用交叉編譯工具鏈 指定-S選項可以生成匯編中間文件。ex:gcc -S test.c
  • 使用 objdump 反匯編 arm二進(jìn)制文件。

上述兩種方法的區別為:

(1)反匯編可以生成ARM指令操作碼,-S生成的匯編沒(méi)有指令碼 (2)反匯編的代碼是經(jīng)過(guò)編譯器優(yōu)化過(guò)的。(3)反匯編代碼量很大。

對于A(yíng)RM Cortex-M,使用的是 arm-none-eabi-objdump,常用指令如下:

  • arm-none-eabi-objdump -d -S(可省) a1.o   查看a1.o反匯編可執行段代碼
  • arm-none-eabi-objdump -D -S(可省) a1.o   查看a1.o反匯編所有段代碼
  • arm-none-eabi-objdump -D -b binary -m arm ab.bin  查看ab.bin反匯編所有代碼段

對于使用 arm-none-eabi-gcc  工具鏈(以STM32CUbeMX)的內核來(lái)說(shuō),使用如下方式生成反匯編文件:

$(OBJDUMP) -D -b binary -m arm  (需要的elf文件,一般是工程名字).elf  > (改成你想生成的反匯編名字,一般是工程名字).dis       # OBJDUMP = arm-none-eabi-objdump

-D表示對全部文件進(jìn)行反匯編,-b表示二進(jìn)制,-m表示指令集架構

Makefile修改如下:

...
TARGET = D6TPir
#######################################
# paths
#######################################
# Build path
BUILD_DIR = build
...
PREFIX = arm-none-eabi-
...
OBJDUMP = $(PREFIX)objdump

dis:
$(OBJDUMP) -D -b binary -m arm $(BUILD_DIR)/$(TARGET).elf > $(BUILD_DIR)/$(TARGET).dis
# $(OBJDUMP) -D -b binary -m arm $(BUILD_DIR)/$(TARGET).bin > $(BUILD_DIR)/$(TARGET).dis

執行 make  dis 即可生成 .dis 文件:




打開(kāi)文件查看,發(fā)現怎么這個(gè)匯編語(yǔ)言有點(diǎn)不一樣:



經(jīng)過(guò)研究了一段時(shí)間,加上了-M force-thumb后稍微有點(diǎn)樣子了:



在網(wǎng)上有各種參考,但是我都測試過(guò)了,并沒(méi)有找到合適的生成完全和標準匯編一致的那種,-M后面的參數也不能亂加,需要根據自己的交叉編譯器,因為這里用的是 arm-none-eabi-gcc,所以可以通過(guò)arm-none-eabi-objdump --help查看能用的命令和參數:



gcc工具鏈下的匯編還是不太熟悉,所以我們下面反匯編文件與 C語(yǔ)言的對比,使用Keil下的反匯編進(jìn)行說(shuō)明。

3.2 C和匯編比較分析

前面介紹了那么多,最終用一個(gè)簡(jiǎn)單的程序對比一下C語(yǔ)言反匯編后的匯編語(yǔ)言,加深一下印象,當作個(gè)實(shí)戰總結。

基于STM32L051(Cortex-M0)內核,目的是為了比較C和匯編,用了個(gè)最簡(jiǎn)單的程序來(lái)分析,沒(méi)有用到任務(wù)外設,程序如下:

//前面省略...
void delay(u32 count)
{
while(count--);
}

u32 add(u16 val1,u16 val2)
{
u32 add_val;

add_val = val1 + val2;

return add_val;
}
int main(void)
{
u16 a,b;
u32 c;
a = 12345;
b = 45678;
c = add(a,b);
while(1)
{
c--;
delay(200000);
}
}

反匯編的代碼對應部分如下(因為基于硬件平臺,其他異常中斷,堆,棧,包括其他一些也有匯編代碼,這里省略):

;省略前面
delay
0x080001ae:    bf00        ..      NOP      
0x080001b0:    1e01        ..      SUBS     r1,r0,#0
0x080001b2:    f1a00001    ....    SUB      r0,r0,#1
0x080001b6:    d1fb        ..      BNE      0x80001b0 ; delay + 2
0x080001b8:    4770        pG      BX       lr
add
0x080001ba:    4602        .F      MOV      r2,r0
0x080001bc:    1850        P.      ADDS     r0,r2,r1
0x080001be:    4770        pG      BX       lr
main
0x080001c0:    f2430439    C.9.    MOV      r4,#0x3039
0x080001c4:    f24b256e    K.n%    MOV      r5,#0xb26e
0x080001c8:    4629        )F      MOV      r1,r5
0x080001ca:    4620         F      MOV      r0,r4
0x080001cc:    f7fffff5    ....    BL       add ; 0x80001ba
0x080001d0:    4606        .F      MOV      r6,r0
0x080001d2:    e003        ..      B        0x80001dc ; main + 28
0x080001d4:    1e76        v.      SUBS     r6,r6,#1
0x080001d6:    4804        .H      LDR      r0,[pc,#16] ; [0x80001e8] = 0x30d40
0x080001d8:    f7ffffe9    ....    BL       delay ; 0x80001ae
0x080001dc:    e7fa        ..      B        0x80001d4 ; main + 20
$d
0x080001de:    0000        ..      DCW    0
0x080001e0:    e000ed0c    ....    DCD    3758157068
0x080001e4:    05fa0000    ....    DCD    100270080
0x080001e8:    00030d40    @...    DCD    200000
;省略后面

3.2.1 MOV后面 立即數的疑問(wèn)

在對比分析這段代碼前,在 main 函數中的第一句:

0x080001c0:    f2430439    C.9.    MOV      r4,#0x3039
就有一個(gè)大大的疑問(wèn), MOV r4,#0x3039中 0x3039 并不是立即數(按照我們第二章 立即數的說(shuō)明) ,包括接下來(lái)的 0xb26e 也不是立即數,怎么可以直接用 mov,按理來(lái)說(shuō)需要用 LDR偽指令的??

至于這個(gè)問(wèn)題,網(wǎng)上簡(jiǎn)單查找了一下,找到一篇有關(guān)說(shuō)明的文章:ARM 匯編的mov操作立即數的疑問(wèn)  其中有說(shuō)到,在 keil 公司方網(wǎng)站里關(guān)于arm匯編的說(shuō)明里有這么一段:

Syntax MOV{cond} Rd, #imm16 where: imm16 is any value in the range 0-65535.

所以,是不是在 Keil 中的arm匯編 立即數可以使16位的?

為了驗證一下,我稍微修改了一下程序,就是把a的值賦值超過(guò)16位(當然定義函數之類(lèi)的也要跟著(zhù)改,測試代碼中a為u16的無(wú)符號整形),測試了一下。

a賦值為 65535,結果如下(65535不是立即數,也可以直接mov):

0x080001c0:    f64f75ff    O..u    MOV      r5,#0xffff 
a賦值為 65536,結果如下(65536是立即數,可以直接mov):
0x080001c0:    f44f3580    O..5    MOV      r5,#0x10000
a賦值為一個(gè)大于16位的,不是立即數的數,比如:0x1FFFF :
0x080001c0:    4d08        .M      LDR      r5,[pc,#32] ; [0x80001e4] = 0x1ffff

果然,最后當 a 大于16位,不是立即數時(shí)候,會(huì )使用偽指令 LDR,所以我們可以得出結論:

在 Keil 中的arm匯編中,16位內(包括16位)的數都直接使用 MOV 賦值,大于16位,如果是立即數,直接使用MOV,不是立即數用LDR (立即數的判斷方式還是前面講的那樣)。

3.2.2 反匯編文件解析

對于上面的示例程序的匯編碼,簡(jiǎn)單解析如下:

添加一個(gè)有意思的測試對于delay函數中的語(yǔ)句,上圖是while(count--);改成while(--count);后匯編代碼如下:

對于上面的測試程序,匯編中并沒(méi)有使用到 PUSH 和 POP 指令,因為程序太簡(jiǎn)單了,不需要使用到棧,為了能夠熟悉下單片機中必須且經(jīng)常需要用到的 棧,我們稍微修改一下add函數,在add函數中調用了delay函數:

u32 add(u16 val1,u16 val2)
{
u32 add_val;

add_val = val1 + val2;

delay(10);

return add_val;
}

對于的add函數匯編代碼如下:

add
0x080001ba:    b530        0.      PUSH     {r4,r5,lr}   ;把r4 r5 lr的值入棧
0x080001bc:    4603        .F      MOV      r3,r0
0x080001be:    460c        .F      MOV      r4,r1
0x080001c0:    191d        ..      ADDS     r5,r3,r4
0x080001c2:    200a        .       MOVS     r0,#0xa
0x080001c4:    f7fffff3    ....    BL       delay ; 0x80001ae
0x080001c8:    4628        (F      MOV      r0,r5
0x080001ca:    bd30        0.      POP      {r4,r5,pc}  ;把r4 r5 lr的值出棧,
匯編中可以看到,指令后面后面加了個(gè)S ,MOVS 、ADDS,這就是我們前面說(shuō)到的,帶了S 會(huì )影響 xPSR 寄存器中的值。

可以看到,因為存在函數的多次調用,main函數中調用add函數,add函數中調用delay函數,所以在add函數運行之前,通過(guò) push把 r4,r5,lr 寄存器的值先存入棧中,等待程序執行完(函數調用結束)再吧  r4,r5,lr 寄存器的值恢復。

上面的程序雖然簡(jiǎn)單,但是通過(guò)我們C程序 與 匯編程序的對比分析,能夠讓我們更加深入的理解匯編語(yǔ)言。


END

來(lái)源:矜辰所致

版權歸原作者所有,如有侵權,請聯(lián)系刪除。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
arm 匯編
ARM立即數講解--LDR和MOV的區別
ARM匯編 -- 嵌入式學(xué)習博客
iOS C函數ARM匯編
常用的匯編指令都有哪些?
[轉載]常用ARM匯編指令
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久