PUSH

目录

什么是 PUSH?

PUSH 指令用于将数据保存到栈内存中。栈是一种"后进先出"(LIFO)的内存区域,主要用于函数调用时保存现场,同时也可能是要开辟空间给后面用。

作用

  • 保存上层函数现场,本层函数执行完后恢复
  • 开辟空间给后面用

基本语法

PUSH {寄存器列表}

工作原理

栈的生长方向

在ARM架构中,栈通常是向下生长的: - 栈指针(SP) 指向栈顶 - PUSH时,SP减小,数据存入栈中 - POP时,SP增加,数据从栈中恢复

执行过程

PUSH {R0, R1, LR}

实际执行步骤: 1. SP = SP - 12(为3个寄存器分配空间,每个4字节,因为ARM架构为32位) 1. 将LR存入 [SP+8] 2. 将R1存入 [SP+4] 3. 将R0存入 [SP+0]

示例分析

示例1:基本用法

0x08002F34  B503    ..    PUSH    {R0, R1, LR}

执行前:

SP = 0x20001000
栈内存:未知

执行后:

SP = 0x20000FF4  (SP减少了12字节)
栈内存:
0x20000FF4: R0的值
0x20000FF8: R1的值  
0x20000FFC: LR的值

示例2:函数开头的典型用法

0x08002F00  B570    p.    PUSH    {R4-R6, LR}
  • 保存R4、R5、R6和LR四个寄存器
  • 为每个寄存器分配4字节,共16字节栈空间
  • SP减少16

示例3:保存多个寄存器

0x08002F10  E92D4FF0  -O..    PUSH    {R4-R11, LR}
  • 保存R4到R11共8个寄存器,加上LR共9个
  • 分配36字节栈空间(9×4)
  • SP减少36

PUSH 的作用

1. 函数调用保存现场

my_function:
    PUSH    {R4-R6, LR}    ; 保存要用到的寄存器和返回地址
    ; ... 函数体 ...
    POP     {R4-R6, PC}    ; 恢复寄存器并返回

2. 临时保存寄存器值

当寄存器不够用时,把不常用的寄存器暂时保存到栈上。

3. 中断处理

发生中断时,自动保存关键寄存器到栈中。

与 POP 指令配对使用

PUSH 和 POP 必须成对使用,确保栈平衡:

; 正确的用法
PUSH    {R0, R1, LR}    ; 进入时保存
; ... 一些操作 ...
POP     {R0, R1, PC}    ; 退出时恢复,用PC替代LR实现返回

; 错误的用法会导致栈崩溃!
PUSH    {R0, R1, LR}
; ... 忘记POP ...
; 结果:栈指针错乱,程序崩溃

代码示例

完整的函数模板

my_function:
    PUSH    {R4, R5, LR}       ; 1. 保存寄存器
    SUB     SP, SP, #8         ; 2. 为局部变量分配空间

    ; 函数体代码
    MOV     R4, R0             ; 使用保存的寄存器
    MOV     R5, #100
    ADD     R0, R4, R5

    ADD     SP, SP, #8         ; 3. 释放局部变量空间
    POP     {R4, R5, PC}       ; 4. 恢复寄存器并返回

重要注意事项

  1. 栈对齐:SP必须保持4字节或8字节对齐
  2. 寄存器顺序:PUSH/POP的寄存器列表会自动按编号排序
  3. LR处理:PUSH保存LR,POP时通常用PC来同时恢复和返回
  4. 栈平衡:PUSH和POP必须数量匹配,否则栈会损坏

总结

PUSH 是ARM汇编中最重要的指令之一,它: - 保存寄存器到栈内存 - 为函数调用建立工作环境
- 保护现场不被破坏 - 必须与POP配对使用