今天在搞arm汇编,终于明白它的堆栈到底是个怎么回事
先贴上代码:
USR_STACK_LENGTH EQU 64
SVC_STACK_LENGTH EQU 0
FIQ_STACK_LENGTH EQU 16
IRQ_STACK_LENGTH EQU 64
ABT_STACK_LENGTH EQU 0
UND_STACK_LENGTH EQU 0
AREA Example5,CODE,READONLY
ENTRY
CODE32
START MOV R0,#0
MOV R1,#1
MOV R2,#2
MOV R3,#3
MOV R4,#4
MOV R5,#5
MOV R6,#6
MOV R7,#7
MOV R8,#8
MOV R9,#9
MOV R10,#10
MOV R11,#11
MOV R12,#12
BL InitStack
;打开IRQ中断(cpsr寄存器的I位零位)
MRS R0,CPSR
BIC R0,R0,#0X80
MSR CPSR_cxsf,R0
;切换到用户模式
MSR CPSR_c,#0XD0
MRS R0,CPSR
;切换到管理模式
MSR CPSR_c,#0XDF
MRS R0,CPSR
HALT B HALT
InitStack
MOV R0,LR
;设置管理模式堆栈
MSR CPSR_c,#0xd3
LDR SP,StackSvc
;设置中断模式堆栈
MSR CPSR_c,#0xd2
LDR SP,STackIrq
;设置快速中断模式堆栈
MSR CPSR_c,#0XD1
LDR SP,STackFiq
;设置中止模式堆栈
MSR CPSR_c,#0XD7
LDR SP,STackAbt
;设置未定义模式堆栈
MSR CPSR_c,#0XDB
LDR SP,STackUnd
;设置系统模式
MSR CPSR_c,#0XDF
LDR SP,StackUsr
MOV PC,R0
StackUsr DCD UsrStackSpace+(USR_STACK_LENGTH-1)*4
StackSvc DCD SvcStackSpace+(SVC_STACK_LENGTH-1)*4
STackIrq DCD IrqStackSpace+(IRQ_STACK_LENGTH-1)*4
STackFiq DCD FiqStackSpace+(FIQ_STACK_LENGTH-1)*4
STackAbt DCD AbtStackSpace+(ABT_STACK_LENGTH-1)*4
STackUnd DCD UndStackSpace+(UND_STACK_LENGTH-1)*4
;分配堆栈空间
AREA MyStacks,DATA,NOINIT,ALIGN=2
UsrStackSpace SPACE USR_STACK_LENGTH*4
SvcStackSpace SPACE SVC_STACK_LENGTH*4
IrqStackSpace SPACE IRQ_STACK_LENGTH*4
FiqStackSpace SPACE FIQ_STACK_LENGTH*4
AbtStackSpace SPACE ABT_STACK_LENGTH*4
UndStackSpace SPACE UND_STACK_LENGTH*4
END
再贴上两幅图
注意上面红色圈住的部分。上面的图说明以下以点:
1.程序在0x0000808C处结束
2.程序后面的伪代码诸如:
StackUsr DCD UsrStackSpace+(USR_STACK_LENGTH-1)*4
StackSvc DCD SvcStackSpace+(SVC_STACK_LENGTH-1)*4
它们的地址是紧跟着程序末尾的
3.LDR SP,StackSvc是将StackSvc这个标号处的内容给sp,如图StackSvc标号的地址为0x8094但是其内容是
由下面的语句决定:
StackSvc DCD SvcStackSpace+(SVC_STACK_LENGTH-1)*4决定
SvcStackSpace标号的地址为0x81a8,所以StackSvc的内容为0x81a4
由上面三点认识,可以得出如下结论:各个模式下的堆栈指针不是一个固定的值。而是由程序
编译后根据程序大小来决定的。我们可以规定堆栈的大小,初如化的时候只需要将堆栈指针
放到合适的位置即可。在正式的程序运行时,可能我们所用的栈可能比规定的堆栈大,那么堆栈指针
可能越界,就会造成不可预知的后果。这个结论也支持了前面的“堆栈”那篇文章的推理。