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

📌往期推文全新看点(文中附带最新·鸿蒙全栈学习笔记)

📃 鸿蒙(HarmonyOS)北向开发知识点记录~

📃 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~

📃 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?

📃 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~

📃 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?

📃 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?

📃 记录一场鸿蒙开发岗位面试经历~

📃 持续更新中……


汇编如何传复杂的参数?

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

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

先看一段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函数栈中保存大部分参数,同时又必须确保数据的连续性,好像也只能用这种办法了,一部分通过寄存器传,一部分通过拷贝的方式倒是挺有意思的.

<think>我们已经在之前的回答中介绍了如何获取OpenHarmony源码并在DevEco Studio中导入。但针对用户本次明确的需求:导入OpenHarmony5.0的完整源码,我们需要调整步骤以适应5.0版本。根据引用[4]可知,OpenHarmony5.0版本已经发布,其源码获取方式与之前版本类似,但需要注意版本分支和开发环境要求。同时,引用[3]提到了SDK离线包的安装方式,这对于源码导入后的环境配置有参考价值。以下是详细步骤:###第一步:获取OpenHarmony5.0源码```bash#1.安装repo工具(若已安装可跳过) curlhttps://gitee.com/oschina/repo/raw/fork_flow/repo-py3> /usr/local/bin/repochmoda+x/usr/local/bin/repo#2.初始化仓库(指定5.0Release分支)repo init-uhttps://gitee.com/openharmony/manifest-b OpenHarmony-5.0-Release#3.同步代码(约需50GB+磁盘空间) reposync -c``` ###第二步:配置DevEco Studio环境1.**安装DevEco Studio4.0+**根据引用[3],需使用支持OpenHarmony5.0的IDE版本(建议4.0.0.600或更高)[^3]。2.**导入源码项目**-打开DevEco Studio→ `File`→ `Open`-选择OpenHarmony5.0源码根目录-等待Gradle和索引构建完成(首次可能需30分钟以上) ###第三步:配置项目结构1. **标记源码目录**右键点击以下关键目录 →`MarkDirectory as` →`Sources Root`:-`foundation/`-`applications/`- `kernel/`2.**启用C++支持**-打开 `File`→ `Settings`→ `Build,Execution,Deployment`→ `CMake`-添加新的CMake配置(使用默认的`CMakeLists.txt`) ###第四步:解决常见问题1.**索引不全**若部分C++代码未索引,手动添加包含路径:-在`CMakeLists.txt`中添加:`include_directories([源码中的头文件路径])`-例如:`include_directories(kernel/linux/patches/linux-5.10/include)` 2. **JS/TS框架层查看**-进入`foundation/arkui/ace_engine`目录-运行`npm install`安装依赖(需提前配置Node.js)>提示:OpenHarmony5.0的子系统结构见引用[4][^4],重点模块:> -应用框架:`foundation/arkui`> -分布式能力:`foundation/distributeddatamgr` >-内核:`kernel/linux` ---###相关问题§§相关问题§§1.OpenHarmony5.0源码中如何查看分布式调度模块的实现?2.如何调试OpenHarmony5.0内核的Linux内核模块? 3.在DevEcoStudio中如何正确索引OpenHarmony的TypeScript代码?</think>### 在 DevEco Studio 中导入 OpenHarmony 5.0 完整源码指南 #### 第一步:获取 OpenHarmony 5.0 源码 ```bash # 安装 repo 工具 curl https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > /usr/local/bin/repo chmod a+x /usr/local/bin/repo # 初始化 5.0 Release 分支 repo init -u https://gitee.com/openharmony/manifest -b OpenHarmony-5.0-Release # 同步源码(约需 50GB+ 空间) repo sync -c -j8 ``` #### 第二步:配置 DevEco Studio 环境 1. **安装要求**: - 使用 DevEco Studio 4.0.3.800 或更高版本[^3] - 配置 OpenHarmony SDK:`Settings > OpenHarmony SDK > Add Local SDK`[^3] - 安装 Native 开发套件(NDK)[^1] 2. **环境验证**: ```bash # 检查工具链 hdc --version # OpenHarmony 调试工具 cmake --version # 构建工具 ``` #### 第三步:导入源码到 DevEco Studio 1. **打开项目**: - `File > Open` → 选择源码根目录 - 等待 Gradle 同步完成(首次需 10-30 分钟) 2. **关键配置**: ```groovy // build.gradle 添加 Native 支持 externalNativeBuild { cmake { path "CMakeLists.txt" version "3.22.1" } } ``` 3. **目录标记**(右键操作): | 目录路径 | 标记类型 | 作用 | |----------|----------|------| | `foundation/` | Sources Root | 核心框架源码 | | `kernel/` | Sources Root | 内核代码 | | `drivers/` | Headers Root | 驱动头文件 | | `build/` | Excluded | 避免索引构建脚本 | #### 第四步:源码浏览技巧 1. **核心模块入口**: - 应用框架:`foundation/arkui/ace_engine` - 分布式能力:`foundation/distributeddatamgr/datamgr_service` - JS 运行时:`arkcompiler/ets_runtime`[^1] - 内核:`kernel/linux/patches/linux-5.10` 2. **混合开发调试**: - 设置断点:同时可在 ArkTS 和 C++ 文件中设置断点 - 调试配置:选择 `Native Debugging` 模式[^2] ```xml <!-- launch.json 示例 --> <configuration type="native"> <target name="module_name.so"/> </configuration> ``` #### 第五步:解决常见问题 1. **索引失败处理**: - 执行 `File > Invalidate Caches` - 手动添加包含路径: ```cmake # CMakeLists.txt 添加 include_directories(kernel/linux/include) ``` 2. **多语言支持**: | 语言 | 路径示例 | 开发方式 | |------|----------|----------| | ArkTS | `applications/` | 应用层开发 | | C/C++ | `foundation/` | 系统服务 | | Rust | `third_party/rust/` | 安全模块 | 3. **跨平台适配原理**: - 硬件抽象层(HAL):`drivers/peripheral` - 编译宏控制:`build/config/` 中的板级配置 - 最小系统裁剪:通过 `subsystem_config.json` 配置 > **性能提示**:首次导入时禁用 `Settings > Tools > Shared Indexes` 可加速索引 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值