【ARM汇编的堆栈问题】压栈运用|堆栈类型测试

举报
IM汤姆凯特 发表于 2022/04/19 17:23:51 2022/04/19
【摘要】 【ARM汇编的堆栈问题】压栈运用|堆栈类型测试 如何用ARM汇编语言输出3个以上数据 | ARM汇编的堆栈有什么作用 | 如何运用 | 堆栈类型又如何检验呢?

【ARM汇编的堆栈问题】


今天来带大家了解ARM汇编的堆栈问题

  1. ARM汇编中压栈能解决什么问题?

  2. ARM微处理器有哪几种类型的堆栈工作方式呢?

  3. 堆栈类型又如何检验呢?

写在前面:为什么必须要讲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, #0
    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,#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

运行结果:

在这里插入图片描述

: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, #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输出,目的是为了看现在的地址和上面没有压栈时的地址变化情况,从而判断是递增还是递减

查看运行结果:

在这里插入图片描述

得出结论:

  1. 压栈后栈顶的内容和原本r4的内容一直,说明r4到了栈顶,所以是满堆栈。
  2. 查看两次栈顶地址的值,发现减小了,所以是递减堆栈。
  3. 综上所述:ARM的默认堆栈类型就是(FD)满递减堆栈

:gift:总结:

  • 可以通过入栈和出栈的方式输入或输入3个以上的数据

  • ARM支持4中堆栈类型,默认是(FD)满递减堆栈,可以通过其他方式来改变你所需的堆栈类型

  • 要学会通过动手实践来验证你的猜想是否正确,这里我们学会了验证ARM堆栈类型的方法


本期就结束了,如果对您有帮助,点赞+评论支持一下博主再走吧
还没有关注汤姆的朋友,点个关注每天学一点汇编
下期预告:ARM汇编数据的批量加载

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。