arm堆栈操作.docx
- 文档编号:12917506
- 上传时间:2023-04-22
- 格式:DOCX
- 页数:9
- 大小:18.76KB
arm堆栈操作.docx
《arm堆栈操作.docx》由会员分享,可在线阅读,更多相关《arm堆栈操作.docx(9页珍藏版)》请在冰豆网上搜索。
arm堆栈操作
arm堆栈操作
arm堆栈的组织结构是满栈降的形式,满栈即sp是要停留在最后一个进栈元素,降:
就是堆栈的增长方向是从高地址向低地址发展。
arm对于堆栈的操作一般采用LDMFD(pop)和STMFD(push)两个命令。
以前困惑的就是STMFD命令对于操作数是按照什么顺序压栈的
比如:
STMFDsp!
{R0-R5,LR}进栈顺序是:
高地址(1方式)
LR
R5
R4
```````
R0 <-sp
低地址
还是:
高地址(2方式)
R0
R1
```
R5
LR<-sp
低地址
现在通过下表,可以轻松的解决这个问题:
寻址方式
说明
pop
=LDM
push
=STM
FA
递增满
LDMFA
LDMDA
STMFA
STMIB
FD
递减满
LDMFD
LDMIA
STMFD
STMDB
EA
递增空
LDMEA
LDMDB
STMEA
STMIA
ED
递减空
LDMED
LDMIB
STMED
STMDA
按照图表,可知STMFD对应的是STMDB,根据arm指令手册,可知STMDB入栈顺序是(1方式)
而LDMFD对应的是LDMIA,这样这两个操作就可以成功配对:
以下是我在学习ARM指令中记录的关于堆栈方面的知识
1、寄存器R13在ARM指令中常用作堆栈指针
2、对于R13寄存器来说,它对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外5个物理寄存器对应于其他5种不同的运行模式。
采用以下的记号来区分不同的物理寄存器:
R13_
usr、fiq、irq、svc、abt、und。
3、寄存器R13在ARM指令中常用作堆栈指针,但这只是一种习惯用法,用户也可使用其他的寄存器作为堆栈指针。
而在Thumb指令集中,某些指令强制性的要求使用R13作为堆栈指针。
由于处理器的每种运行模式均有自己独立的物理寄存器R13,在用户应用程序的初始化部分,一般都要初始化每种模式下的R13,使其指向该运行模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入R13所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。
4、有四种类型的堆栈:
堆栈是一种数据结构,按先进后出(FirstInLastOut,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(FullStack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(EmptyStack)。
同时,根据堆栈的生成方式,又可以分为递增堆栈(AscendingStack)和递减堆栈(DecendingStack),当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。
这样就有四种类型的堆栈工作方式,ARM微处理器支持这四种类型的堆栈工作方式,即:
◎Fulldescending满递减堆栈堆栈首部是高地址,堆栈向低地址增长。
栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。
ARM-Thumb过程调用标准和ARM、ThumbC/C++编译器总是使用Fulldescending类型堆栈。
<这是什么原因呢?
>
◎Fullascending满递增堆栈堆栈首部是低地址,堆栈向高地址增长。
栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。
◎Emptydescending空递减堆栈堆栈首部是低(这里是不是错了,应该是高地址吧)地址,堆栈向高地址增长。
栈指针总是指向下一个将要放入数据的空位置。
◎Emptyascending空递增堆栈堆栈首部是高地址,堆栈向低地址增长。
栈指针总是指向下一个将要放入数据的空位置。
5、操作堆栈的汇编指令堆栈类型入栈指令出栈指令FulldescendingSTMFD(STMDB)LDMFD(LDMIA)FullascendingSTMFA(STMIB)LDMFA(LDMDA)EmptydescendingSTMED(STMDA)LDMED(LDMIB)EmptyascendingSTMEA(STMIA)LDMEA(LDMDB)
例子:
STMFDr13!
{r0-r5};PushontoaFullDescendingStackLDMFDr13!
{r0-r5};PopfromaFullDescendingStack.
例子
1)保护现场参数,不影响PC,嵌汇编的时候对之前的存参数的寄存器R0~R12保存
STMFDr13!
{r0-r7,LR}
LDMFDr13!
{r0-r7,PC}
2)ARM汇编中lr(r14)寄存器的作用
lr(r14)的作用问题,这个lr一般来说有两个作用:
1.当使用bl或者blx跳转到子过程的时候,r14保存了返回地址,可以在调用过程结尾恢复。
2.异常中断发生时,这个异常模式特定的物理R14被设置成该异常模式将要返回的地址。
另外注意pc,在调试的时候显示的是当前指令地址,而用movlr,pc的时候lr保存的是此指令向后数两条指令的地址,大家可以试一下用movpc,pc,结果得到的是跳转两条指令,这个原因是由于arm的流水线造成的,预取两条指令的结果.
3.我们看到的LR值是上一个子程序调用保存的子程序返回地址,这个LR是要赋给PC的。
嵌入式汇编要手动保存返回地址,进行现场保护。
PC记录当前运行的地址。
下一条回自己+4
进入子程序,LR才自动更新为返回地址值,PC为程序运行地址
ARM汇编嵌套子程序
几个星期前阅读了(加)CarlHamacher、ZvonkoVranesic、SafwatZaky编写的《计算机组成》第五版中的ARM子程序调用的一些知识,启发很大,顺便将它整理了一下并加入了自己的理解。
子程序
1通过寄存器传递参数
BL指令通常用于调用一个子程序。
它和B指令的区别在于它将返回地址装载到R14中。
由于子程序可能是嵌套的,因此LR的内容必须保存在子程序所使用的堆栈中。
下面的例子使用寄存器传递参数。
调用者通过寄存器R1和R2分别将数组的大小和数组的首地址传递给子程序;子程序利用寄存器R0将和传递给调用者。
该子程序使用了寄存器R3,必须将它和LR推入堆栈。
调用程序
LDRR1,N
LDRR2,POINTER
BLLISTADD
STRR0,SUM
…
子程序
LISTADDSTMFDR13!
{R3,R14}
MOVR0,#0
LOOPLDRR3,[R2],#4
ADDR0,R0,R3
SUBSR1,R1,#1
BGTLOOP
LDMFDR13!
{R3,R15}
注:
这里并没有遵守APCS(ARM过程调用标准),一般由调用者负责保存R0~R3,被调用者负责保存其他的寄存器以使调用返回后程序的状态不被破坏。
2通过堆栈传递参数
调用程序
LDRR0,POINTER
STRR0,[R13,#-4]!
;将数组首地址推入堆栈
LDRR0,N
STRR0,[R13,#-4]!
;将元素个数N推入堆栈
BLLISTADD
LDRR0,[R13,#4];将元素和装载到寄存器R0中
STRR0,SUM
ADDR13,R13,#8;恢复堆栈
…
子程序
LISTADDSTMFDR13!
{R0-R3,R14}
LDRR1,[R13,#20]
LDRR2,[R13,#24]
MOVR0,#0
LOOPLDRR3,[R2],#4
ADDR0,R0,R3
SUBSR1,R1,#1
BGTLOOP
STRR0,[R13,#24];把和推入堆栈的最深处
LDMFDR13!
{R0-R3,R15}
[R0]
[R1]
[R2]
[R3]
返回地址
N
POINTER/SUM
3嵌套子程序
当子程序嵌套时,堆栈是用于处理返回地址的最合适的数据结构。
当调用子程序时在堆栈上建立了完整的堆栈结构。
应当注意当前子程序的堆栈帧指针所指向的空间中存储的是调用当前子程序的子程序的堆栈帧指针。
调用者将子程序所需要的参数按照顺序推入堆栈。
子程序首先保存工作寄存器、调用者的堆栈帧指针以及返回地址,然后它计算自己的堆栈帧指针的值(ADDFP,SP,#16),并利用这个堆栈帧指针从堆栈帧中获取调用者传递给它的参数。
在子程序完成它的任务之后,它也将返回值保存在堆栈中,此例保存在参数所在的内存单元。
调用者和被调用者必须约定好参数的传递顺序和返回值保存位置。
如果返回值比较多的话,调用者要为返回值预先在堆栈中保留合适的空间。
调用程序
2000LDRR0,PARAM2
STRR0,[SP,#-4]!
;将参数推入堆栈
LDRR0,PARAM1
STRR0,[SP,#-4]!
BLSUB1
2020LDRR0,[SP];保存SUB1的结果
STRR0,RESULT
ADDSP,SP,#8;恢复堆栈
…
子程序
2100SUB1STMFDSP!
{R0-R3,FP,LR}
ADDFP,SP,#16;计算帧指针
LDRR0,[FP,#8];载入参数1
LDRR1,[FP,#12];载入参数2
…
LDRR2,PARAM3;载入参数3
STRR2,[SP,#-4]!
;将参数3推入堆栈
BLSUB2
2164LDRR2,[SP],#4;将SUB2的结果弹出并存储在R2中,并递增SP
…
STRR3,[FP,#8];将结果推入堆栈
LDMFDSP!
{R0-R3,FP,PC};恢复寄存器并返回
3000SUB2STMFDSP!
{R0,R1,FP,LR}
ADDFP,SP,#8;载入结构指针
LDRR0,[FP,#8];载入参数
…
STRR1,[FP,#8];将结果推入堆栈
LDMFDSP!
{R0,R1,FP,PC}
[R0]fromSUB1
[R1]fromSUB1
[FP]fromSUB1
2164/返回地址
param3/SUB2的结果最后保存在这里
[R0]frommain
[R1]frommain
[R2]frommain
[R3]frommain
[FP]frommain
2020/返回地址
param1/SUB1的结果最后保存在这里
param2
原栈顶
3子程序编译后都放在哪
嵌汇编子程序:
会在编译后统一放到一个地方系统调用__main()库函数,
后再进入main.c前的初始化C下面的堆栈命令前(这个堆栈是用来放置C里参数的)
嵌汇编子程序:
按定义顺序放在堆栈前,而C语言子程序,放在初始化堆栈堆栈命令后。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- arm 堆栈 操作