ARM匯編 MOV PC,LR
終于明白這個LR寄存器了
看下面這個ARM匯編吧
本文引用地址:http://butianyuan.cn/article/201611/317869.htmBL NEXT ;跳轉(zhuǎn)到子程序
......... ;NEXT處執(zhí)行
NEXT
..........
MOV PC,LR ;從子程序返回
這里的BL是跳轉(zhuǎn)的意思,LR(R14)保存了返回地址
PC(R15)是當前地址,把LR給PC就是從子程序返回
這里有一下總結(jié)
首先
1.SP(R13) LR(R14)PC(R15)
2.lr(r14)的作用問題,這個lr一般來說有兩個作用:
1》.當使用bl或者blx跳轉(zhuǎn)到子過程的時候,r14保存了返回地址,可以在調(diào)用過程結(jié)尾恢復。
2》.異常中斷發(fā)生時,這個異常模式特定的物理R14被設置成該異常模式將要返回的地址。
另外注意pc,在調(diào)試的時候顯示的是當前指令地址,而用mov lr,pc的時候lr保存的是此指令向后數(shù)兩條指令的地址,大家可以試一下用mov pc,pc,結(jié)果得到的是跳轉(zhuǎn)兩條指令,這個原因是由于arm的流水線造成的,預取兩條指令的結(jié)果.
3.》我以前看書不懂的地方
子程序返回的三種方法
現(xiàn)在總結(jié)如下
1.MOV PC,LR
2.BL LR
3.在子程序入口處使用以下指令將R14存入堆棧
STMFD SP!,{
對應的,使用以下指令可以完成子程序的返回
LDMFD SP!, {
轉(zhuǎn)載自:http://blog.csdn.net/xgx198831/article/details/8333446
匯編學習總結(jié)記錄
1.1. 匯編學習總結(jié)記錄
對于我們之前分析的start.S中,涉及到很多的匯編的語句,其中,可以看出,很多包含了很多種不同的語法,使用慣例等,下面,就對此進行一些總結(jié),借 以實現(xiàn)一定的舉一反三或者說觸類旁通,這樣,可以起到一定的借鑒功能,方便以后看其他類似匯編代碼, 容易看懂匯編代碼所要表達的含義。
1.1.1. 匯編中的標號=C中的標號
像前面匯編代碼中,有很多的,以點開頭,加上一個名字的形式的標號,比如:
- reset:
- /*
- * set the cpu to SVC32 mode
- */
- mrs r0,cpsr
比如,C語言中定義一個標號ERR_NODEV:
- ERR_NODEV: /* no device error */
- ... /* c code here */
- if (something)
- goto ERR_NODEV ;
匯編中的標號 = C語言中的標號Label
1.1.2. 匯編中的跳轉(zhuǎn)指令=C中的goto
對應地,和上面的例子中的C語言中的編號和掉轉(zhuǎn)到標號的goto類似,匯編中,對于定義了標號,那么也會有對應的指令,去跳轉(zhuǎn)到對應的匯編中的標號。
這些跳轉(zhuǎn)的指令,就是b指令,b是branch的縮寫。
b指令的格式是:
b{cond} label
簡單說就是跳轉(zhuǎn)到label處。
用和上面的例子相關的代碼來舉例:
- .globl _start
- _start: b reset
匯編中的b跳轉(zhuǎn)指令 = C語言中的goto
1.1.3. 匯編中的.globl=C語言中的extern
對于上面例子中:
.globl _start
中的.global,就是聲明_start為全局變量/標號,可以供其他源文件所訪問。
即匯編器,在編譯此匯編代碼的時候,會將此變量記下來,知道其是個全局變量,遇到其他文件是用到此變量的的時候,知道是訪問這個全局變量的。
因此,從功能上來說,就相當于C語言用extern去生命一個變量,以實現(xiàn)本文件外部訪問此變量。
匯編中的.globl或.global = C語言中的extern
1.1.4. 匯編中用bl指令和mov pc,lr來實現(xiàn)子函數(shù)調(diào)用和返回
和b指令類似的,另外還有一個bl指令,語法是:
BL{cond} label
其作用是,除了b指令跳轉(zhuǎn)到label之外,在跳轉(zhuǎn)之前,先把下一條指令地址存到lr寄存器中,以方便跳轉(zhuǎn)到那邊執(zhí)行完畢后,將lr再賦值給pc,以實現(xiàn)函數(shù)返回,繼續(xù)執(zhí)行下面的指令的效果。
用下面這個start.S中的例子來說明:
- bl cpu_init_crit
- 。。。
- cpu_init_crit:
- 。。。
- mov pc, lr
然后在cpu_init_crit部分,執(zhí)行完畢后,最后調(diào)用 mov pc, lr,將lr中的值,賦給pc,即實現(xiàn)函數(shù)的返回原先 bl cpu_init_crit下面那條代碼,繼續(xù)執(zhí)行函數(shù)。
上面的整個過程,用C語言表示的話,就相當于
- 。。。
- cpu_init_crit();
- 。。。
- void cpu_init_crit(void)
- {
- 。。。
- }
而關于C語言中,函數(shù)的跳轉(zhuǎn)前后所要做的事情,都是C語言編譯器幫我們實現(xiàn)好了,會將此C語言中的函數(shù)調(diào)用,轉(zhuǎn)化為對應的匯編代碼的。
其中,此處所說的,函數(shù)掉轉(zhuǎn)前后所要做的事情,就是:
函數(shù)跳轉(zhuǎn)前:要將當前指令的下一條指令的地址,保存到lr寄存器中。
函數(shù)調(diào)用完畢后:將之前保存的lr的值給pc,實現(xiàn)函數(shù)跳轉(zhuǎn)回來。繼續(xù)執(zhí)行下一條指令。
而如果你本身自己寫匯編語言的話,那么這些函數(shù)跳轉(zhuǎn)前后要做的事情,都是你程序員自己要關心,要實現(xiàn)的事情。
匯編中bl + mov pc,lr = C語言中的子函數(shù)調(diào)用和返回
1.1.5. 匯編中的對應位置有存儲值的標號 = C語言中的指針變量
像前文所解析的代碼中類似于這樣的:
- LABEL1:.word Value2
- _TEXT_BASE:
- .word TEXT_BASE
而該標號中對應的位置,所存放的是一個word的值,具體的數(shù)值是TEXT_BASE,此處的TEXT_BASE是在別處定義的一個宏,值是0x33D00000。
所以,即為:
有一個標號_TEXT_BASE,其對應的位置中,所存放的是一個word的值,值為TEXT_BASE=0x33D00000。
總的來說,此種用法的含義,如果用C語言來表示,其實更加容易理解:
int *_TEXT_BASE = TEXT_BASE = 0x33D00000
即:
int *_TEXT_BASE = 0x33D00000
不過,對于這樣的類似于C語言中的指針的匯編中的標號,在C語言中調(diào)用到的話,卻是這樣引用的:
- /* for the following variables, see start.S */
- extern ulong _armboot_start; /* code start */
- extern ulong _bss_start; /* code + data end == BSS start */
- 。。。
- IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
- 。。。
- *IRQ_STACK_START = *_armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
其中,對應的匯編中的代碼為:
- .globl _armboot_start
- _armboot_start:
- .word _start
匯編中類似這樣的代碼:
label1: .word value2
就相當于C語言中的:
int *label1 = value2
但是在C語言中引用該標號/變量的時候,卻是直接拿來用的,就像這樣:
label1 = other_value
其中l(wèi)abel1就是個int型的變量。
1.1.6. 匯編中的ldr+標號,來實現(xiàn)C中的函數(shù)調(diào)用
接著上面的內(nèi)容,繼續(xù)解釋,對于匯編中這樣的代碼:
第一種:
ldr pc, 標號1
。。。
標號1:.word 標號2
。。。
標號2:
。。。(具體要執(zhí)行的代碼)
或者是,
第二種:
ldr pc, 標號1
。。。
標號1:.word XXX(C語言中某個函數(shù)的函數(shù)名)
的意思就是,將地址為標號1中內(nèi)容載入到pc中。
而地址為標號1中的內(nèi)容,就是標號2。
所以上面第一種的意思:
就很容易看出來,就是把標號2這個地址值,給pc,即實現(xiàn)了跳轉(zhuǎn)到標號2的位置執(zhí)行代碼,就相當于調(diào)用一個函數(shù),該函數(shù)名為標號2.
第二種的意思,和上面類似,是將C語言中某個函數(shù)的函數(shù)名,即某個地址值,給pc,實現(xiàn)調(diào)用C中對應的那個函數(shù)。
兩種做法,其含義用C語言表達,其實很簡單:
PC = *(標號1) = 標號2
舉個例子就是:
第一種:
- 。。。
- ldr pc, _software_interrupt
- 。。。
- _software_interrupt: .word software_interrupt
- 。。。
- software_interrupt:
- get_bad_stack
- bad_save_user_regs
- bldo_software_interrupt
就是實現(xiàn)了將標號1,_software_interrupt,對應的位置中的值,標號2,software_interrupt,給pc,即實現(xiàn)了將pc掉轉(zhuǎn)到software_interrupt的位置,即實現(xiàn)了調(diào)用函數(shù)software_interrupt的效果。
第二種:
- ldr pc, _start_armboot
- _start_armboot: .word start_armboot
其中,start_armboot是C語言文件中某個C語言的函數(shù)。
匯編中,實現(xiàn)函數(shù)調(diào)用的效果,有如下兩種方法:
方法1:
ldr pc, 標號1
。。。
標號1:.word 標號2
。。。
標號2:
。。。(具體要執(zhí)行的代碼)
方法2:
ldr pc, 標號1
。。。
標號1:.word XXX(C語言中某個函數(shù)的函數(shù)名)
1.1.7. 匯編中設置某個寄存器的值或給某個地址賦值
在匯編代碼start.S中,看到不止一處, 類似于這樣的代碼:
形式1:
- # define pWTCON0x53000000
- 。。。
- ldr r0, =pWTCON
- mov r1, #0x0
- str r1, [r0]
形式2:
- # define INTSUBMSK 0x4A00001C
- 。。。
- ldr r1, =0x7fff
- ldr r0, =INTSUBMSK
- str r1, [r0]
其中,形式1是直接通過mov指令來將0這個值賦給r1寄存器,和形式2中的通過ldr偽指令來將0x3ff賦給r1寄存器,兩者區(qū)別是,前者是因為已經(jīng)確定所要賦的值0x0是mov的有效操作數(shù),而后者對于0x3ff不確定是否是mov的有效操作數(shù)
(如果不是,則該指令無效,編譯的時候,也無法通過編譯,會出現(xiàn)類似于這樣的錯誤:
- start.S: Assembler messages:
- start.S:149: Error: invalid constant -- `mov r1,#0xFFEFDFFF
- make[1]: * [start.o] 錯誤 1
- make: * [cpu/arm920t/start.o] 錯誤 2
所以才用ldr偽指令,讓編譯器來幫你自動判斷:
(1)如果該操作數(shù)是mov的有效操作數(shù),那么ldr偽指令就會被翻譯成對應的mov指令。
舉例說明:
匯編代碼:
- # define pWTCON0x53000000
- 。。。
- ldr r0, =pWTCON
- 33d00068: e3a00453mov r0, #1392508928 ; 0x53000000
舉例說明:
匯編代碼:
- ldr r1, =0x7fff
- 33d00080: e59f13f8ldr r1, [pc, #1016] ; 33d00480
- 。。。
- 33d00480: 00007fff.word 0x00007fff
匯編中,一個常用的,用來給某個地址賦值的方法,類似如下形式:
- #define 宏的名字寄存器地址
- 。。。
- ldr r1, =要賦的值
- ldr r0, =宏的名字
- str r1, [r0]
評論