ARM-栈帧(一)

ARM 栈帧

本系列均以 corter-A7(armv7-a) 为例

在 ARM 中,通常为满减栈(Full Descending FD), 也就是说,堆栈指针指向堆栈内存中最后一个填充的位置,并且随着每个新数据项被压入堆栈而递减。

栈的本质

要理解栈的本质,首先得明白,程序执行是怎么执行的?

程序执行其实就是对应一个又一个状态机,在 A 状态有对应的内核寄存器,在 B 状态也有对应的内核寄存器值。

A 状态切换到 B 状态,就需要把 A 状态的内核寄存器保存下来,不然无法从 B 状态再回到 A 状态。此时就需要将 A 状态下内核寄存器的值放到内存中,即入栈

B 状态切换到 A 状态,需要恢复 A 状态的现场,即内核寄存器的值,所以需要将存到内存中的数据写回到内核寄存器,即出栈

其实 linux/rtos 的任务也是一个又一个状态机,状态切换的时候,先将当前状态(A)的内核寄存器保存到内存中,再把下一个状态(B)的内核寄存器从内存中读回来,程序跳转到 B 状态执行,其实这就是上下文切换的过程。

入栈/出栈的本质其实就是内存中的一块区域进行操作:

  • 需要入栈的时候,就把内核寄存器的值写入到内存中
  • 需要出栈的时候,就把相应内存中的值写回到内核寄存器中

入栈

在这里插入图片描述
在调用入栈指令 PUSH/STMFD 之前,堆栈指针(SP)指向堆栈中最后一个被占用的字节,入栈指令完成后,堆栈指针被递减8(两个字),并且这两个字的内容被写入内存。

编号低的寄存器写入内存低地址处

出栈

假设要将:R0-R3 和 LR 寄存器入栈,当前的 SP 指针指向 0x80000000,由于 ARM 处理器的栈是向下增长的,使用的汇编代码如下:PUSH {R0-R3,LR}

入栈之后,内存分布如下
在这里插入图片描述

假设这个时候又对 LR 进行入栈操作,汇编代码如下:PUSH LR

入栈之后,内存分布如下

在这里插入图片描述
上面两个图就是分步对 R0-R3,R12,及 LR 进行压栈以后的编程模型,如果要出栈的话,就使用如下代码:

POP LR
POP {
    
    R0-R3,R12}

出栈的时候从栈顶开始,也就是 SP 当前执行的位置,地址依次增大,提取堆栈中的数据到要恢复的寄存器列表中。
PUSH 和 POP 的另一种写法是 STMFD SP! 和 LDMFD SP!
因此上面的代码可以写为

入栈
STMFD SP!,{
    
    R0~R3, R12}
STMFD SP! ,{
    
    LR}
出栈
LDMFD SP! ,{
    
    LR}
LDMFD SP! ,{
    
    R0~R3, R12}

示例测试

gdb 小技巧:

  • 窗口缩小:winheight src -5
  • 窗口放大:winheight src +5

入栈

入栈之前先查看当前寄存器
在这里插入图片描述

  • Info reg:从内核寄存器中可以看到,当前 r0 r1 r2 r3 lr 寄存器的值依次为:1 2 3 4 5
    sp 指针当前为0x801fffec

  • x/20 0x801fffec 查看 SP 栈指针地址数据
    在这里插入图片描述

可以看到 SP 地址数据从高到低依次为:5 4 3 2 1

从这里可以看到,编号低的寄存器写入内存低地址处

寄存器清零

在这里插入图片描述

出栈

在这里插入图片描述

源码

.global _start

_start:
    /* 进入 SVC 模式(其实复位之后,系统就处于 SVC 模式,这里也可以不切换,设置 SP 的值) */
    mrs r0, cpsr
    bic r0, r0, #0x1f /* 将  r0 寄存器中的低 5 位清零,也就是 cpsr 的 M0~M4 */
    orr r0, r0, #0x13 /* r0 或上 0x13,表示使用 SVC 模式 */
    msr cpsr, r0      /* 将 r0 的数据写入到 cpsr 中 */

    ldr sp, =0X80200000	/* 设置栈指针 */

    ldr r0, =0x01
    ldr r1, =0x02
    ldr r2, =0x03
    ldr r3, =0x04
    ldr lr, =0x05

    push {
    
    r0-r3, lr}
    /* nop for debug */
    nop
    nop
    nop

    mov r0, #0
    mov r1, #0
    mov r2, #0
    mov r3, #0
    mov lr, #0
    /* nop for debug */
    nop
    nop
    nop

    pop {
    
    r0-r3, lr}
    /* nop for debug */
    nop
    nop
    nop

    stmfd sp!, {
    
    r0-r3, lr}
    /* nop for debug */
    nop
    nop
    nop

    mov r0, #0
    mov r1, #0
    mov r2, #0
    mov r3, #0
    mov lr, #0
    /* nop for debug */
    nop
    nop
    nop

    ldmfd sp!, {
    
    r0-r3, lr}
    /* nop for debug */
    nop
    nop
    nop

loop:
    b loop

猜你喜欢

转载自blog.csdn.net/tyustli/article/details/130650561
今日推荐