【ARM汇编的堆栈问题】
☀️作者简介:大家好我是IM汤姆凯特,大家可以叫我汤姆
🐋个人主页:IM汤姆凯特的CSDN博客
🎁系列专栏:【ARM嵌入式基础】
🌱每日一句:
“假如你花7美元买了一张电影票,你怀疑这个电影是否值7美元。看了半个小时后,你最担心的事被证实了:影片糟透了。你应该离开影院吗?在做这个决定时,你应当忽视那7美元。它是沉没成本,无论你离开影院与否,钱都不会再收回。” —— ——斯蒂格利茨(2001年诺贝尔经济学奖获得者)
今天来带大家了解ARM汇编的堆栈问题
-
ARM汇编中压栈能解决什么问题?
-
ARM微处理器有哪几种类型的堆栈工作方式呢?
-
堆栈类型又如何检验呢?
写在前面:为什么必须要讲ARM的堆栈问题,因为如果没有堆栈的运用很多问题是没有办法解决的,之前有文章讲到,只能用寄存器R0~R3来传递参数,因此当你需要传递多个参数的时候就会收到限制,该怎么解决?这里就引入了压栈和出栈概念的运用。
看完前面写的文章,大家肯定学会了如何从内存获取地址、如何读取数据、如何调用scanf和printf,但只能输入和输出三个数据,今就带大家学习一下如何运用栈输入输出4个数据甚至更多。
怎么一次性输出多个数据呢?
其实很简单,只需要我们在ARM汇编中栈的运用。用于传递参数的寄存器只有四个R0~R3,也称为易失性寄存器,当一次需要传递的参数较多时,我们可以先把数据压到栈中,这样我们把R0~R3的数据读取完毕后,就会优先读取栈顶中的数据依次类推,如果不压入栈中,自然就没有办法读取到。 |
💫没有接触栈概念时的思路(从内存中读取地址——将内容传给寄存器——调用printf显示)
源代码:
.data
str:.asciz " %5d\n %5d\n %5d\n %5d\n"
a:.word 1
b:.word 2
c:.word 3
d:.word 4
.text
.globl main
main:
stmfd sp!,{
lr}
ldr r0,=a
ldr r1,[r0]
ldr r0,=b
ldr r2,[r0]
ldr r0,=c
ldr r3,[r0]
ldr r0,=d
ldr r4,[r0]
ldr r0,=str
bl printf
mov r0, #0
ldmfd sp!,{
lr}
mov pc,lr
.end
运行结果:
如果这一部分代码还是看不明白的话,那就请小可爱自行看前面写的文章了哦。【ARM嵌入式基础】
我们发现第四个数据并不是我们想要的数据,这里为什么会出错呢?就是因为我们调用printf也好scanf也好只能用R0~R3中的数据,R4中的数据取不到的。
如何压入栈中,输出正确结果呢?
⭐️我们知道了为什么出错,下面就来看看怎么不出错
从上面例子中来看,想输出内存中“d”的值,就要把获取到“d”数据的寄存器压到栈中,让调用printf的时候能找到它。我们看,r4其实已经通过取地址和间接寻址获取到了“d”的数据,那么只需要把r4压到栈中就可以。 |
压栈的指令在这篇文章中其实已经提到【ARM汇编模板】
//压栈
push {
r4}
//出栈
pop {
r4}
💖这里由于我们调用了printf,调用之后实际上已经把r4出栈,所以不用加pop {r4}
但是当没有运用函数让他出栈时要手动出栈,因为我们一定要遵循堆栈平衡原则,我们来看下一
压栈后的源代码:
.data
str:.asciz " %5d\n %5d\n %5d\n %5d\n"
a:.word 1
b:.word 2
c:.word 3
d:.word 4
.text
.globl main
main:
mov r4,#0
stmfd sp!,{
lr}
ldr r0,=a
ldr r1,[r0]
ldr r0,=b
ldr r2,[r0]
ldr r0,=c
ldr r3,[r0]
ldr r0,=d
ldr r4,[r0]
push {
r4}
ldr r0,=str
bl printf
mov r0, #0
ldmfd sp!,{
lr}
mov pc,lr
.end
运行结果:
👍没错,就是这么简单!大家一定要动手试一下哦
ARM微处理器有哪几种堆栈工作方式呢?
既然提到了栈的运用,就把它给讲明白
堆栈是一种数据结构,按照先进后出的工作方式,使用一个称为堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈,而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈。同时,当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。这样就有4种类型的堆栈工作方式,ARM微处理器支持这四种类型的堆栈工作方式。
1. 满递增堆栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生成。 2. 满递减堆栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生成。 3. 空递增堆栈(EA):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。 4. 空递增堆栈(ED):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。 |
一共就这四种类型,下面我们可以验证一下ARM默认的是哪种堆栈类型
怎么来检测ARM堆栈的类型?
我们了解完堆栈的类型,发现有两个变量:满|空堆栈类型、递增|递减堆栈类型。
这样就得到了一个思路:
1. 通过测试栈顶的内容是不是空,来判断是满堆栈类型还是空堆栈类型 | 2. 通过测试栈顶的地址是增加还是减少,来判断是递增类型还是递减类型 |
直接上代码:
.data
fmt:.asciz "\n r4=%d sp=%d lr=%d\n"
.text
.globl main
main:
push {
lr}
ldr r0,=fmt
mov r1,r4
mov r2,sp
mov r3,lr
bl printf
push {
r4}
ldr r0,=fmt
ldr r1,[sp]
mov r2,sp
mov r3,lr
bl printf
mov r0, #0
ldmfd sp!,{
lr}
mov pc, lr
.end
给大家分开讲解一下:
①把r4中的内容给到r1,把栈顶的内容给到r2,把链接寄存器的内容给r3,然后调用printf输出
②这里的sp是R13寄存器(堆栈指针寄存器)的别名,sp指向栈顶
③ 这里的lr是R14寄存器(链接寄存器)的别名,想通过他是否改变来验证调用printf跳转一次后,寄存器的值是否会发生变化
ldr r0,=fmt
mov r1,r4
mov r2,sp
mov r3,lr
bl printf
①先把r4压到栈中
②把栈顶的内容给r1然后输出,目的是为了把r4压到栈中后,比较栈顶和上面r4中的内容,查看r4是不是放到了栈顶,从而来判断是不是满堆栈
③ 再把栈顶的地址给r2输出,目的是为了看现在的地址和上面没有压栈时的地址变化情况,从而判断是递增还是递减
查看运行结果:
得出结论:
- 压栈后栈顶的内容和原本r4的内容一直,说明r4到了栈顶,所以是满堆栈。
- 查看两次栈顶地址的值,发现减小了,所以是递减堆栈。
- 综上所述:ARM的默认堆栈类型就是(FD)满递减堆栈
🎁总结:
-
可以通过入栈和出栈的方式输入或输入3个以上的数据
-
ARM支持4中堆栈类型,默认是(FD)满递减堆栈,可以通过其他方式来改变你所需的堆栈类型
-
要学会通过动手实践来验证你的猜想是否正确,这里我们学会了验证ARM堆栈类型的方法
本期就结束了,如果对您有帮助,点赞+评论支持一下博主再走吧
还没有关注汤姆的朋友,点个关注每天学一点汇编
下期预告:
ARM汇编数据的批量加载
今天的文章arm堆栈操作指令_汇编语言实现冒泡排序「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/78009.html