【ARM汇编的堆栈问题】
今天来带大家了解ARM汇编的堆栈问题
-
ARM汇编中压栈能解决什么问题?
-
ARM微处理器有哪几种类型的堆栈工作方式呢?
-
堆栈类型又如何检验呢?
写在前面:为什么必须要讲ARM的堆栈问题,因为如果没有堆栈的运用很多问题是没有办法解决的,之前有文章讲到,只能用寄存器R0~R3来传递参数,因此当你需要传递多个参数的时候就会收到限制,该怎么解决?这里就引入了压栈和出栈概念的运用。
看完前面写的文章,大家肯定学会了如何从内存获取地址、如何读取数据、如何调用scanf和printf,但只能输入和输出三个数据,今就带大家学习一下如何运用栈输入输出4个数据甚至更多。
怎么一次性输出多个数据呢?
其实很简单,只需要我们在ARM汇编中栈的运用。用于传递参数的寄存器只有四个R0~R3,也称为易失性寄存器,当一次需要传递的参数较多时,我们可以先把数据压到栈中,这样我们把R0~R3的数据读取完毕后,就会优先读取栈顶中的数据依次类推,如果不压入栈中,自然就没有办法读取到。 |
:dizzy:没有接触栈概念时的思路(从内存中读取地址——将内容传给寄存器——调用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,
ldmfd sp!,{lr}
mov pc,lr
.end
运行结果:
如果这一部分代码还是看不明白的话,那就请小可爱自行看前面写的文章了哦。【ARM嵌入式基础】
我们发现第四个数据并不是我们想要的数据,这里为什么会出错呢?就是因为我们调用printf也好scanf也好只能用R0~R3中的数据,R4中的数据取不到的。
如何压入栈中,输出正确结果呢?
:star:我们知道了为什么出错,下面就来看看怎么不出错
从上面例子中来看,想输出内存中“d”的值,就要把获取到“d”数据的寄存器压到栈中,让调用printf的时候能找到它。我们看,r4其实已经通过取地址和间接寻址获取到了“d”的数据,那么只需要把r4压到栈中就可以。 压栈的指令在这篇文章中其实已经提到【ARM汇编模板】 //压栈
push {r4}
//出栈
pop {r4}
:sparkling_heart:这里由于我们调用了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,
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,
ldmfd sp!,{lr}
mov pc,lr
.end
运行结果: :thumbsup:没错,就是这么简单!大家一定要动手试一下哦
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,
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)满递减堆栈
:gift:总结:
本期就结束了,如果对您有帮助,点赞+评论支持一下博主再走吧
还没有关注汤姆的朋友,点个关注每天学一点汇编
下期预告:ARM汇编数据的批量加载
|
|
|
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
评论(0)