3-2-2 ARM架构简明教程:汇编实例分析

3-2-2 ARM架构简明教程:汇编实例分析

1. 目标:深入理解 ARM 汇编

在上一节课里,我们介绍了 ARM 的硬件架构以及基础汇编指令。如果你想更深入地理解 ARM 的底层运行机制,就必须看反汇编代码,即 C 代码被编译后的机器码对应的汇编指令。

今天,我们通过一个简单的 加法函数 来分析它的汇编执行过程,并掌握:

  • C 代码如何传递参数给汇编
  • 函数调用过程中的寄存器变化
  • 栈(Stack)在函数调用中的作用
  • 函数返回值是如何传递的

2. 代码准备:创建一个简单的 C 函数

我们写一个简单的 累加函数,并让编译器生成反汇编代码:

int add(int count) {
    count = count + 1;
    return count;
}

这个函数的作用就是 count 加 1,然后返回新的值。接下来,我们通过编译器生成它的反汇编代码,看看它在 ARM 处理器上的实际运行机制。


3. 反汇编代码分析

我们让编译器生成 add 函数的反汇编代码,得到如下指令:

add:
    PUSH {LR}         ; 1. 保存 LR(链接寄存器,存放返回地址)
    ADD  R0, R0, #1   ; 2. R0 = R0 + 1 (计算 count + 1)
    POP  {PC}         ; 3. 从栈中取回 PC(返回调用处)

📌 解析:

  1. PUSH {LR}

    • 进入 add 函数时,我们需要保存返回地址 LR,防止后续跳转丢失。
    • LR(Link Register)存放的是函数调用前的地址,用于函数返回。
  2. ADD R0, R0, #1

    • R0 里面存放的是 count,这一步就是让 count + 1
  3. POP {PC}

    • POP 指令从栈中恢复 PC,即恢复 LR,让 CPU 跳回到调用 add 函数的代码处。

🔍 总结

  • ARM 处理器在函数调用时,参数存放在 R0 - R3 里(最多 4 个参数)。
  • 返回值存放在 R0,所以 add 函数返回的值也放在 R0
  • PUSH / POP 保护寄存器,确保函数返回时 PC 仍然指向正确的代码位置。

4. 在主程序中调用 add

假设我们在 main.c 里调用 add

void main() {
    int result = add(5);
}

主函数的反汇编代码

MOV  R0, #5         ; 1. 把 5 赋值给 R0
BL   add            ; 2. 跳转到 add 函数(BL = Branch with Link)
MOV  R1, R0         ; 3. 把 add 的返回值(R0)存入 R1

📌 分析

  1. MOV R0, #5

    • 把参数 5 传递给 add,它存入 R0,因为 ARM 规定 R0 - R3 用于函数参数
  2. BL add

    • BL(Branch with Link) 是一个带返回的跳转,即:
      • 先把 PC+4 存入 LR
      • 再跳转到 add 函数
  3. MOV R1, R0

    • add 的返回值存在 R0 里,主程序通过 MOV 指令将其存入 R1 变量。

5. ARM 栈(Stack)在函数调用中的作用

我们修改 add,增加一个局部变量

int add(int count) {
    int sum = count + 1;
    return sum;
}

现在 sum局部变量,那么它存放在哪里?来看新的汇编代码:

add:
    PUSH {LR}           ; 1. 保护 LR
    SUB  SP, SP, #4     ; 2. 申请 4 字节栈空间
    STR  R0, [SP]       ; 3. 把 R0 存到栈里(存储 sum)
    LDR  R0, [SP]       ; 4. 读取 sum
    ADD  R0, R0, #1     ; 5. sum = sum + 1
    ADD  SP, SP, #4     ; 6. 释放栈空间
    POP  {PC}           ; 7. 恢复 PC,返回主程序

📌 栈的作用

  • SUB SP, SP, #4 申请 4 字节的栈空间sum 的存储位置)
  • STR R0, [SP] R0 里的 count 存入栈
  • LDR R0, [SP] 从栈中读取 sum
  • ADD SP, SP, #4 释放栈空间

你会发现,所有的局部变量都存放在栈(Stack)里。这也是任务切换时为什么要保存栈指针(SP)——因为每个任务的变量都是存放在栈里的


6. ARM 函数调用的完整流程

  1. 主程序调用 add(5)

    • MOV R0, #5 传递参数
    • BL add 进入 add 函数,LR 记录返回地址
  2. 执行 add

    • PUSH {LR} 保护返回地址
    • SP-4 申请局部变量
    • ADD R0, R0, #1 计算 count + 1
    • POP {PC} 返回主程序
  3. 主程序获取返回值

    • MOV R1, R0 存储 add 返回的结果

💡 理解这套流程,RTOS 的任务切换就不难了!


7. 代码完整示例

add:
    PUSH {LR}           ; 保护返回地址
    SUB  SP, SP, #4     ; 申请 4 字节栈空间
    STR  R0, [SP]       ; 存储 sum
    LDR  R0, [SP]       ; 读取 sum
    ADD  R0, R0, #1     ; sum = sum + 1
    ADD  SP, SP, #4     ; 释放栈空间
    POP  {PC}           ; 返回主程序

8. 结论

ARM 的函数参数通过 R0 - R3 传递
局部变量存放在栈(Stack)里
PUSH / POP 用于保护 LR 和局部变量
任务切换时必须保存 SP,否则数据丢失

下节课,我们将学习 任务切换的底层实现,看看 RTOS 是如何管理多个任务的!🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值