鸿蒙内核源码分析(汇编传参篇) | 如何传递复杂的参数

汇编如何传复杂的参数?

汇编基础篇 中很详细的介绍了一段具有代表性很经典的汇编代码,有循环,有判断,有运算,有多级函数调用。但有一个问题没有涉及,就是很复杂的参数如何处理?

在实际开发过程中函数参数往往是很复杂的参数,(比如结构体)汇编怎么传递呢?

先看一段C语言及汇编代码,传递一个稍微复杂的参数来说明汇编传参的过程

复制代码
#include <stdio.h>
#include <math.h>
struct reg{//参数远超寄存器数量
    int Rn[100]; 
    int pc;
};

int framePoint(reg cpu)
{
    return cpu.Rn[0] * cpu.pc;
}

int main()
{
    reg cpu;
    cpu.Rn[0] = 1;
    cpu.pc = 2;
    return framePoint(cpu);
}

复制代码
//编译器: armv7-a gcc (9.2.1)
framePoint(reg):
        sub     sp, sp, #16     @申请栈空间
        str     fp, [sp, #-4]!  @保护main函数栈帧,等同于push {fp}
        add     fp, sp, #0      @fp变成framePoint栈帧,同时也指向了栈顶
        add     ip, fp, #4      @定位到入栈口,让4个参数依次入栈 
        stm     ip, {r0, r1, r2, r3}@r0-r3入栈保存
        ldr     r3, [fp, #4]    @取值cpu.pc = 2    
        ldr     r2, [fp, #404]  @取值cpu.Rn[0] = 1
        mul     r3, r2, r3      @cpu.Rn[0] * cpu.pc
        mov     r0, r3          @返回值由r0保存
        add     sp, fp, #0      @重置sp,和add     fp, sp, #0配套出现
        ldr     fp, [sp], #4    @恢复main函数栈帧
        add     sp, sp, #16     @归还栈空间,sp回落到main函数栈顶位置
        bx      lr              @跳回main函数
main:
        push    {fp, lr}        @入栈保存调用函数现场                     
        add     fp, sp, #4      @fp指向sp+4,即main栈帧的底部
        sub     sp, sp, #800    @分配800个线性地址,即main栈帧的顶部
        mov     r3, #1          @r3 = 1
        str     r3, [fp, #-408] @将1放置 fp-408处,即:cpu.Rn[0]处
        mov     r3, #2          @r3 = 2
        str     r3, [fp, #-8]   @将2放置 fp-8处,即:cpu.pc
        mov     r0, sp          @r0 = sp
        sub     r3, fp, #392    @r3 = fp - 392
        mov     r2, #388        @只拷贝388,剩下4个由寄存器传参
        mov     r1, r3          @保存由r1保存r3,用于memcpy
        bl      memcpy          @拷贝结构体部分内容,将r1的内容拷贝r2的数量到r0
        sub     r3, fp, #408    @定位到结构体剩余未拷贝处
        ldm     r3, {r0, r1, r2, r3} @将剩余结构体内容通过寄存器传参
        bl      framePoint(reg)         @执行framePoint
        mov     r3, r0          @返回值给r3
        nop @用于程序指令的对齐
        mov     r0, r3          @再将返回值给r0
        sub     sp, fp, #4      @恢复SP值
        pop     {fp, lr}        @出栈恢复调用函数现场
        bx      lr              @跳回调用函数

两个函数对应两段汇编,干净利落,去除中间各项干扰,只有一个结构体reg,以下详细讲解如何传递它,以及它在栈中的数据变化是怎样的?

入参方式

结构体总共101个栈空间(一个栈空间单位四个字节),对应就是404个线性地址.

main上来就申请了 sub sp, sp, #800 @申请800个线性地址给main,即 200个栈空间

复制代码
int main()
{
    reg cpu;
    cpu.Rn[0] = 1;
    cpu.pc = 2;
    return framePoint(cpu);
}

但main函数只有一个变量,只需101个栈空间,其他都算上也用不了200个.为什么要这么做呢?

而且注意下里面的数字 388, 408, 392 这些都是什么意思?

看完main汇编能得到一个结论是 200个栈空间中除了存放了main函数本身的变量外 ,还存放了要传递给framePoint函数的部分参数值,存放了多少个?答案是 388/4 = 97个. 注意变量没有共用,而是拷贝了一部份出来.如何拷贝的?继续看

memcpy汇编调用

复制代码
        mov     r0, sp          @r0 = sp
        sub     r3, fp, #392    @r3 = fp - 392
        mov     r2, #388        @只拷贝388,剩下4个由寄存器传参
        mov     r1, r3          @保存由r1保存r3,用于memcpy
        bl      memcpy          @拷贝结构体部分内容,将r1的内容拷贝r2的数量到r0
        sub     r3, fp, #408    @定位到结构体剩余未拷贝处
        ldm     r3, {r0, r1, r2, r3} @将剩余结构体内容通过寄存器传参

看这段汇编拷贝,意思是从r1开始位置拷贝r2数量的数据到r0的位置,注意只拷贝了 388个,也就是 388/4 = 97个栈空间.剩余的4个通过寄存器传的参数.ldm代表从fp-408的位置将内存地址的值连续的给r0 - r3寄存器,即位置(fp-396,fp-400,fp-404,fp-408)的值.

执行下来的结果就是

复制代码
r3 = fp-408, r2 = fp-404 ,r1 = fp-400 ,r0 = fp-396 得到虚拟地址的值,这些值整好是memcpy没有拷贝到变量剩余的值

逐句分析 framePoint

复制代码
framePoint(reg):
        sub     sp, sp, #16     @申请栈空间
        str     fp, [sp, #-4]!  @保护main函数栈帧,等同于push {fp}
        add     fp, sp, #0      @fp变成framePoint栈帧,同时也指向了栈顶
        add     ip, fp, #4      @定位到入栈口,让4个参数依次入栈 
        stm     ip, {r0, r1, r2, r3}@r0-r3入栈保存
        ldr     r3, [fp, #4]    @取值cpu.pc = 2    
        ldr     r2, [fp, #404]  @取值cpu.Rn[0] = 1
        mul     r3, r2, r3      @cpu.Rn[0] * cpu.pc
        mov     r0, r3          @返回值由r0保存
        add     sp, fp, #0      @重置sp,和add     fp, sp, #0配套出现
        ldr     fp, [sp], #4    @恢复main函数栈帧
        add     sp, sp, #16     @归还栈空间,sp回落到main函数栈顶位置
        bx      lr              @跳回main函数

framePoint申请了4个栈空间目的是用来存放四个寄存器值的,以上汇编代码逐句分析.

复制代码
第一句: sub     sp, sp, #16     @申请栈空间,用来存放r0-r3四个参数

第二句: str     fp, [sp, #-4]!  @保护main的fp,等同于push {fp},为什么这里要把main函数的fp放到 [sp, #-4]! 位置,注意 !号,表示SP的位置要变动,因为这里必须要保证参数的连续性.

第三句: add     fp, sp, #0      @指定framePoint的栈帧位置,同时指向了栈顶 SP

第四句: add     ip, fp, #4      @很关键,用了ip寄存器,因为此时 fp sp 都已经确定了,但别忘了 r0 - r3 还没有入栈呢.从哪个位置入栈呢, fp+4位置,因为 main函数的栈帧已经入栈了,在已经fp的位置.中间隔了四个空位,就是给 r0-r3留的.

第五句: stm     ip, {r0, r1, r2, r3}@r0-r3入栈,填满了剩下的四个空位.

第六句: ldr     r3, [fp, #4]    @取的就是cpu.pc = 2的值,因为上一句就是从这里依次入栈的,最后一个当然就是cpu.pc了.

第七句: ldr     r2, [fp, #404]  @取值cpu.Rn[0] = 1,其实这一句已经是跳到了main函数的栈帧取值了,所以看明白了没有,并不是在传统意义上理解的在framePoint的栈帧中取值.

第八句: mul     r3, r2, r3      @cpu.Rn[0] * cpu.pc 做乘法运算

第九句: mov     r0, r3          @返回值r0保存运算结构, 目的是return

第十句: add sp, fp, #0          @重置sp,其实这一句可以优化掉,因为此时sp = fp

第十一句: ldr     fp, [sp], #4  @恢复fp,等同于pop {fp},因为函数运行完了,需要回到main函数了,所以要拿到main的栈帧

第十二句: add     sp, sp, #16   @归还栈空间,等于把四个入参抹掉了.

最后一句: bx      lr            @跳回main函数,如此 fp 和 lr 寄存器中保存的都是 main函数的信息,就可以安全着陆了.

总结

因为寄存器数量有限,所以只能通过这种方式来传递大的参数,想想也只能在main函数栈中保存大部分参数,同时又必须确保数据的连续性,好像也只能用这种办法了,一部分通过寄存器传,一部分通过拷贝的方式倒是挺有意思的.

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念

2.构建第一个ArkTS应用

3.......

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识

2.配置文件

3.应用数据管理

4.应用安全管理

5.应用隐私保护

6.三方应用调用管控机制

7.资源分类与访问

8.学习ArkTS语言

9.......

基于ArkTS 开发

1.Ability开发

2.UI开发

3.公共事件与通知

4.窗口管理

5.媒体

6.安全

7.网络与链接

8.电话服务

9.数据管理

10.后台任务(Background Task)管理

11.设备管理

12.设备使用信息统计

13.DFX

14.国际化开发

15.折叠屏系列

16.......

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH
相关推荐
zhanshuo6 小时前
在鸿蒙里优雅地处理网络错误:从 Demo 到实战案例
harmonyos
zhanshuo6 小时前
在鸿蒙中实现深色/浅色模式切换:从原理到可运行 Demo
harmonyos
whysqwhw12 小时前
鸿蒙分布式投屏
harmonyos
whysqwhw13 小时前
鸿蒙AVSession Kit
harmonyos
whysqwhw15 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw16 小时前
鸿蒙音频编码
harmonyos
whysqwhw16 小时前
鸿蒙音频解码
harmonyos
whysqwhw16 小时前
鸿蒙视频解码
harmonyos
whysqwhw16 小时前
鸿蒙视频编码
harmonyos
ajassi200016 小时前
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器
华为·开源·harmonyos