首页 > 编程语言 >ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR详解

ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR详解

时间:2024-01-26 15:01:59浏览次数:33  
标签:SP PC 指令 lr 寄存器 堆栈 指针

堆栈的实现方法

    

 

 

 

 

 

 

  在随机存储器区划出一块区域作为堆栈区,数据可以一个个顺序地存入(压入)到这个区域之中,这个过程称为‘压栈’(push )。通常用一个指针(堆栈指针 SP—StackPointer)实现做一次调整,SP总指向最后一个压入堆栈的数据所在的数据单元(栈顶)。从堆栈中读取数据时,按照堆栈 指针指向的堆栈单元读取堆栈数据,这个过程叫做 ‘弹出’(pop ),每弹出一个数据,SP 即向相反方向做一次调整,如此就实现了后进先出的原则。

  堆栈是计算机中广泛应用的技术,基于堆栈具有的数据进出LIFO特性,常应用于保存中断断点、保存子程序调用返回点、保存CPU现场数据等,也用于程序间传递参数。

  ARM处理器中通常将寄存器R13作为堆栈指针(SP)。ARM处理器针对不同的模式,共有 6 个堆栈指针(SP),其中用户模式和系统模式共用一个SP,每种异常模式都有各自专用的R13寄存器(SP)。它们通常指向各模式所对应的专用堆栈,也就是ARM处理器允许用户程序有六个不同的堆栈空间。这些堆栈指针分别为R13、R13_svc、R13_abt、R13_und、R13_irq、R13_fiq,如表2-3堆栈指针寄存器所示。

   为了更准确地描述堆栈,根据“压栈”操作时堆栈指针的增减方向,将堆栈区分为‘递增堆栈’(SP 向大数值方向变化)和‘递减堆栈’(SP 向小数值方向变化);又根据SP 指针指向的存储单元是否含有堆栈数据,又将堆栈区分为‘满堆栈’(SP 指向单元含有堆栈有效数据)和‘空堆栈’(SP 指向单元不含有堆栈有效数据)。

  这样两两组合共有四种堆栈方式——满递增、空递增、满递减和空递减。
  ARM处理器的堆栈操作具有非常大的灵活性,对这四种类型的堆栈都支持。
  ARM处理器中的R13被用作SP。当不使用堆栈时,R13 也可以用做通用数据寄存器。

深入理解ARM三个寄存器

深入理解ARM的这三个寄存器,对编程以及操作系统的移植都有很大的裨益。

PC 代表程序计数器,流水线使用三个阶段,因此指令分为三个阶段执行:

  1.取指(从存储器装载一条指令);

  2.译码(识别将要被执行的指令);
  3.执行(处理 指令并将结果写回寄存器)。

R15(PC)总是指向“正在取指”的指令  

  ARM指令是三级流水线,取指,译指,执行时同时执行的,现在PC指向的是正在取指的地址,那么cpu正在译指的指令地址是PC-4(假设在ARM状态 下,一个指令占4个字节),cpu正在执行的指令地址是PC-8,也就是说PC所指向的地址和现在所执行的指令地址相差8。当突然发生中断的时候,保存的是PC的地址这样你就知道了,如果返回的时候返回PC,那么中间就有一个指令没有执行,所以用SUB pc lr-irq #4。

三个寄存器

1、堆栈指针r13(SP):每一种异常模式都有其自己独立的r13,它通常指向异常模式所专用的堆栈,也就是说五种异常模式、非异常模式(用户模式和系统模式),都有各自独立的堆栈,用不同的堆栈指针来索引。这样当ARM进入异常模式的时候,程序就可以把一般通用寄存器压入堆栈,返回时再出栈,保证了各种模式下程序的状态的完整性。

2、连接寄存器r14(LR):每种模式下r14都有自身版组,它有两个特殊功能。

(1)保存子程序返回地址。使用BL或BLX时,跳转指令自动把返回地址放入r14中;子程序通过把r14复制到PC来实现返回,通常用下列指令之一:
  MOV PC, LR
  BX LR

通常子程序这样写,保证了子程序中还可以调用子程序。
  stmfd sp!, {lr}
  ……
  ldmfd sp!, {pc}

(2)当异常发生时,异常模式的r14用来保存异常返回地址,将r14如栈可以处理嵌套中断。

3、程序计数器r15(PC):PC是有读写限制的。当没有超过读取限制的时候,读取的值是指令的地址加上8个字节,由于ARM指令总是以字对齐的,故bit[1:0]总是00。当用str或stm存储PC的时候,偏移量有可能是8或12等其它值。在V3及以下版本中,写入bit[1:0]的值将被忽略,而在V4及以上版本写入r15的bit[1:0]必须为00,否则后果不可预测。

ARM处理器使用流水线来增加处理器指令流的速度,这样可使几个操作同时进行,并使处理与存储器系统之间的操作更加流畅,连续,能提供0.9MIPS/MHZ的指令执行速度。

栈的整体作用

1. 保护现场

  现场/上下文相当于案发现场,总有一些案发现场,要记录下来,否则被别人破坏,便无法恢复。而此处说的现场,是指CPU运行时,用到的一些寄存器,比如r0,r1等,对于这些寄存器的值,如果不保存而直接跳转到子函数中执行,其很可能被破坏,因为其函数执行也要用到这些寄存器。因此,在函数调用之前,应该将这些寄存器等现场暂时保存(入栈push),等调用函数执行完毕后出栈(pop)再恢复现场。这样CPU就可以正确的继续执行了。

  保存寄存器的值,一般用push指令,将对应的某些寄存器的值,一个个放到栈中,即所谓的压栈。然后待被调用的子函数执行完毕后再调用pop,把栈中的一个个的值,赋值给对应的那些你刚开始压栈时用到的寄存器,把对应的值从栈中弹出去,即所谓的出栈。

  其中保存的寄存器中,也包括lr的值(因为用bl指令进行跳转的话,之前的pc值存在lr中),在子程序执行完毕后,再把栈中的lr值pop出来,赋值给pc,这样就实现了子函数的正确的返回。

2. 传递参数

  C语言函数调用时,会传给被调用函数一些参数,对于这些C语言级别参数,被编译器翻译成汇编语言时,要找个地方存放下来,并且让被调用函数能访问,否则没法传递。找个地方存放下来分2种情况。一是,本身传递的参数不多于4个,可以通过寄存器传送。因为在前面的保存现场动作中,已经保存好对应的寄存器的值,此时这些寄存器是空闲的,可以供我们使用存放参数。二是,参数多于4个,寄存器不够用,就得用栈。

3. 临时变量保存在栈中

  这些临时变量包括函数的非静态局部变量以及编译器自动生成的其他临时变量,举例分析C语言函数调用如何使用栈,上面的解释有些抽象,此处再用例子简单说明一下,就容易明白了:

用arm-inux-objdump –d u-boot dump_u-boot.txt得到dump_u-boot.txt文件。该文件是包含了u-boot可执行汇编代码,从中我们可以看到相应C程序对应的汇编代码。

下面贴出两个函数的汇编代码,一个是clock_init,另一个是与clock_init在同一C源文件中的函数CopyCode2Ram:

33d0091c: CopyCode2Ram:
33d0091c: e92d4070  push   {r4, r5, r6, lr}
33d00920: e1a06000  mov r6, r0
33d00924: e1a05001  mov r5, r1
33d00928: e1a04002  mov r4, r2
33d0092c: ebffffef  bl  33d008f0 b BootFrmNORFlash
......
33d00984: ebffff14  bl  33d005dc nand_read_ll
......
33d009a8: e3a00000  mov r0, #0 ; 0x0
33d009ac: e8bd8070  pop {r4, r5, r6, pc}
33d009b0:clock_init:
33d009b0: e3a02313  mov r2, #1275068416   ;0x4c000000
33d009b4: e3a03005  mov r3, #5 ; 0x5
33d009b8: e5823014  str r3,
......
33d009f8: e1a0f00e  mov pc, lr

(1) 先分析clock_init对应的汇编代码,可以看到该函数第一行
:33d009b0: e3a02313 mov r2, #1275068416 ;0x4c000000
  没有我们期望的push指令,即没有将一些寄存器的值放入栈。这是因为,clock_init用到的r2,r3等寄存器,和前面调用clock_init前用到的寄存器r0,没有冲突,故此处不用push保存,有个寄存器要注意,r14,即lr,前面调用clock_init时,用的bl指令,所以会自动把跳转时的pc值赋值给lr,所以也不需要push将PC值保存到栈。而clock_init对应的汇编代码最后一行: 33d009f8: e1a0f00e mov pc, lr 就是我们常见的mov pc,lr,把lr值,即之前保存的函数调用时的PC值,赋值给现在的PC,这样便实现了函数的正确返回,即返回到了函数调用时下一个指令的位置。CPU可以继续执行原先函数内剩下的代码。

(2) CopyCode2Ram对应汇编代码第一行:33d0091c: e92d4070 push {r4, r5, r6, lr}
  就是我们所期望的,用push保存r4,r5,r6,lr,是因为此函数还包括其他函数调用
:33d0092c: ebffffef bl 33d008f0 b BootFrmNORFlash……
33d00984: ebffff14 bl 33d005dc nand_read_ll
……
  也用到bl指令,会改变我们最开始进入clock_init时的lr值,所以也要push暂时保存起来。

  而对应地,CopyCode2Ram最后一行:33d009ac: e8bd8070 pop {r4, r5, r6,pc}是把之前push的值给pop出来,还给对应的寄存器,其中最后一个是将开始push的lr的值pop出来赋给PC,实现了函数的返回。另外我们注意到,CopyCode2Ram的倒数第二行:33d009a8: e3a00000 movr0, #0 ; 0x0 是把0赋值给r0寄存器,就是我们说的返回值的传递,此处的返回值为0,也对应着C代码中的“ return 0”。

  当然也可以用其他暂时空闲没有用到的寄存器来传递返回值。对于使用哪个寄存器来传递返回值,是根据ARM的APCS寄存器的使用约定而设计的,最好按照其约定的来处理,不要随便改变它。这样程序将更加规范。

 

标签:SP,PC,指令,lr,寄存器,堆栈,指针
From: https://www.cnblogs.com/FireLife-Cheng/p/17989416

相关文章

  • ARM 中SP,LR,PC寄存器的作用
    ARM中所有寄存器都是32位的。这里以cortex-a7内核的MX6ULL处理器为例,按照功能可以分为两类:运行需要寄存器(程序正常运行所需要的,比如变量暂存,pc制作等,总共43个),系统管理控制寄存器(存在于协处理器cp15的16个寄存器,用于mmu存储管理控制,cache控制,中断控制,浮点运算单元FPU等功能)。因此co......
  • 网工内推 | 网络安全工程师专场,CISP、HCIE安全认证优先
    01八马茶业招聘岗位:网络安全工程师职责描述:1、负责公司信息安全项目实施及技术支持工作,主要包含信息安全风险检查评估、网络安全设备、服务器及网站日志审查等;2、应用、服务器和网络架构进行安全检查测试,找出安全漏洞并提出修改建议;3、组织协调服务器,网络和应用支持团队修复发现的......
  • SpringBoot中Bean的条件装配
    目录概述ProfileConditionalConditionalOnConditionalOnProperty概述众所周知,SpringBoot最腻害的地方就是容器,开发人员的日常工作就是编写bean,并由框架扫描存到容器里面,当程序跑起来的时候,各种bean协同工作完成了软件功能。那么容器是什么呢?从概念层面来讲,容器是一个池子;从物......
  • 使用Spring Data JPA实现审计功能,记录创建人、创建时间、最后修改时间和最后修改人
    目录前言近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功能开发。本项目为前后端分离开发,后端基于Java21和SpringBoot3开发,后端使用SpringSecurity、JWT、SpringDataJP......
  • SpringBoot中集成XXL-JOB分布式任务调度平台,轻量级、低侵入实现定时任务
    场景XXL-JOBhttps://www.xuxueli.com/xxl-jobXXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。特性:1、简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手;2、动态:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即时生......
  • 使用ZLMediaKit对RTSP流转MP4进行播放
    框架地址:https://github.com/ZLMediaKit/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-API 部署ZLMediaKit解码器https://blog.csdn.net/u011374856/article/details/124802856?spm=1001.2101.3001.6650.4&utm_medium=distribute.pc_relevant.none-task-blog......
  • github action 自动化部署asp.net core应用到服务器
    在自己的仓库里工作流编辑workflow贴上自己的工作流name:ASP.NETCoreDeploymenton:push:branches:-master#你可以根据需要更改分支名称(在向master分支推送的时候触发这个workflow)jobs:deploy:runs-on:ubuntu-latest#使用Ubuntu环......
  • SpringBoot整合Redis
    1、pom.xml中引入redis依赖<!--Redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>2、application.yml配置文件中配置Redis连接参数等s......
  • 修改vSphere ESXi中虚拟机的三大件CPUID,硬盘ID,MAC地址
    1、查看硬件信息 wmicdiskdrivegetserialnumber查看磁盘序列号wmicbiosgetserialnumber查询BIOS序列号wmicnicconfiggetmacaddress查询网卡MAC信息wmiccpugetprocessorid查询cpu的IDwmicbeseboardgetserialnumber查询主板序列号 2、修改CPUID......
  • Spring源码导入IDEA记录
    一、首先Spring源码是gradle搭建,gradle会和jdk版本、IDEA产生兼容问题参考【学不完@根本学不完】的文章:SpringBoot版本与Spring、java、maven、gradle版本对应汇总=>由于本地JDK是1.8对应Java8,按照尽可能看新版本查找=>对应表格里面gradle7.x二、gradle安装步骤gradle历......