[HarmonyOS之旅] Chapter5 - 有趣的init

codinglab 2020-12-23 09:56:49 5248
大家晚上好。不知道大家还记不记得上一篇的最后我们留下了一个问题呢?

前情回顾:

参考HarmonyOS的文档,我们写了第一个应用程序。

void Init_Trace(void)
{
    printf("[codinglab] >>>> Hello HarmonyOS\n");
};
SYS_RUN(Init_Trace);

并添加了两句打印

void HOS_SystemInit(void)
{
    printf("[codinglab] >>>> start\n");
    MODULE_INIT(bsp);
    MODULE_INIT(device);
    MODULE_INIT(core);
    SYS_INIT(service);
    SYS_INIT(feature);
    MODULE_INIT(run);
    printf("[codinglab] >>>> end\n");
    SAMGR_Bootstrap();
}

得到的结果是这样的。

image-20201222190954363

why?

这里遗留的问题就是我们在新增了测试函数之后,并没有在任何地方显示的调用它,但是实际运行的时候,它却被运行到了,这是为什么呢?答案就在本篇来揭晓,下面就和小A一起来看看这个有趣的机制吧。

首先我们在函数定义之后唯一做的就是做了个SYS_RUN(Init_Trace);它是调用吗?我们来看一看。

#define SYS_RUN(func) LAYER_INITCALL_DEF(func, run, "run")

#define LAYER_INITCALL_DEF(func, layer, clayer) \
    LAYER_INITCALL(func, layer, clayer, 2)

#define LAYER_INITCALL(func, layer, clayer, priority)            \
    static const InitCall USED_ATTR __zinitcall_##layer##_##func \
        __attribute__((section(".zinitcall." clayer #priority ".init"))) = func

#define USED_ATTR __attribute__((used))

typedef void (*InitCall)(void);

what?

乍看起来是不是有点懵,就像俄罗斯套娃一样,一宏接一宏。这里的阅读顺序应该是自下而上的阅读。我们一个个来看

  • typedef void (*InitCall)(void);

    无需多说。
  • USED_ATTR。

    我们知道的是__attribute__是用来指定编译属性的,
    这是是通知编译器在目标文件中保留一个静态函数,即使它没有被引用,
    这样编译过程中,既不会因为被定义了但没有使用而报错,也不会在链接的时候被删除掉。
  • LAYER_INITCALL。

    __attribute__((section("name”))) = func,
    作用是将作用对象func指定放入到名为name的段中。(这里关于段的概念不在展开)。
    注意这个是要搭配链接脚本用的。
  • SYS_RUN

    这里简化一下就是:Initcall _zinitcall_run_func = func,
    其实就是定义了一个函数指针(_zinitcall_run_func)并把它指向了func,
    然后根据attribute的修饰,在编译的时候把它放到了.zinitcall.run2.init段当中。

说到现在好像还是没能看到怎么调用的。不要急,我们来看一下MODULE_INIT(run);

#define MODULE_INIT(name)     \
    do {                      \
        MODULE_CALL(name, 0); \
    } while (0)

#define MODULE_CALL(name, step)                                      \
    do {                                                             \
        InitCall *initcall = (InitCall *)(MODULE_BEGIN(name, step)); \
        InitCall *initend = (InitCall *)(MODULE_END(name, step));    \
        for (; initcall < initend; initcall++) {                     \
            printf("[core_main] call pointer address: %p\n", initcall); \
            printf("[core_main] func address: %p\n", *initcall);         \
            (*initcall)();                                           \
        }                                                            \
    } while (0)

#define MODULE_BEGIN(name, step)                          \
    ({        extern InitCall __zinitcall_##name##_start;       \
        InitCall *initCall = &__zinitcall_##name##_start; \
        (initCall);                                       \
    })
#define MODULE_END(name, step)                          \
    ({        extern InitCall __zinitcall_##name##_end;       \
        InitCall *initCall = &__zinitcall_##name##_end; \
        (initCall);                                     \
    })

不知道大家看明白没有,MODULE_INIT(name)的意思就是在遍历从.zinitcall_name_start到.zinitcall_name_end这一段区域中保存的函数指针 ,然后挨个调用一遍。

how?

那么我们来捋一下。真相就是:

HOS_SystemInit()执行过程中,去在.zinitcall_run_start和.zinitcall_run_start之间的.zinitcall.run2.init程序段中找到了保存的Init_Trace函数对应的函数指针,然后调用了他。 Now,show you the 证据, 老套路 out/wifiiot/Hi3861_wifiiot_app.asm与out/wifiiot/Hi3861_wifiiot_app.map。

 004a0810 <HOS_SystemInit>:
   4a0810: 81a5a2ef            jal t0,3fa82a <__riscv_save_4>
   4a0814: 00004497            auipc s1,0x4
   4a0818: 77c4a483            lw  s1,1916(s1) # 4a4f90 <_GLOBAL_OFFSET_TABLE_+0xc4>
   4a081c: 409c                  lw  a5,0(s1)
   4a081e: 1141                  addi  sp,sp,-16
   4a0820: 0000e517            auipc a0,0xe
   4a0824: da850513            addi  a0,a0,-600 # 4ae5c8 <cb.8884+0x2ac>
   4a0828: c63e                  sw  a5,12(sp)
   4a082a: 00004417            auipc s0,0x4
   4a082e: 77a42403            lw  s0,1914(s0) # 4a4fa4 <_GLOBAL_OFFSET_TABLE_+0xd8>
   4a0832: c7b590ef            jal ra,3fa4ac <printf>
   4a0836: 00004917            auipc s2,0x4
   4a083a: 6fe92903            lw  s2,1790(s2) # 4a4f34 <_GLOBAL_OFFSET_TABLE_+0x68>
   4a083e: 0000e997            auipc s3,0xe
   4a0842: da298993            addi  s3,s3,-606 # 4ae5e0 <cb.8884+0x2c4>
   4a0846: 0000ea17            auipc s4,0xe
   4a084a: dc2a0a13            addi  s4,s4,-574 # 4ae608 <cb.8884+0x2ec>
   4a084e: 0b246a63            bltu  s0,s2,4a0902 <HOS_SystemInit+0xf2>
   4a0852: 00004417            auipc s0,0x4
   4a0856: 78242403            lw  s0,1922(s0) # 4a4fd4 <_GLOBAL_OFFSET_TABLE_+0x108>
   4a085a: 00004917            auipc s2,0x4
   4a085e: 72692903            lw  s2,1830(s2) # 4a4f80 <_GLOBAL_OFFSET_TABLE_+0xb4>
   4a0862: 0000e997            auipc s3,0xe
   4a0866: d7e98993            addi  s3,s3,-642 # 4ae5e0 <cb.8884+0x2c4>
   4a086a: 0000ea17            auipc s4,0xe
   4a086e: d9ea0a13            addi  s4,s4,-610 # 4ae608 <cb.8884+0x2ec>
   4a0872: 0b246563            bltu  s0,s2,4a091c <HOS_SystemInit+0x10c>
   4a0876: 00004417            auipc s0,0x4
   4a087a: 7c242403            lw  s0,1986(s0) # 4a5038 <_GLOBAL_OFFSET_TABLE_+0x16c>
   4a087e: 00004917            auipc s2,0x4
   4a0882: 6f692903            lw  s2,1782(s2) # 4a4f74 <_GLOBAL_OFFSET_TABLE_+0xa8>
   4a0886: 0000e997            auipc s3,0xe
   4a088a: d5a98993            addi  s3,s3,-678 # 4ae5e0 <cb.8884+0x2c4>
   4a088e: 0000ea17            auipc s4,0xe
   4a0892: d7aa0a13            addi  s4,s4,-646 # 4ae608 <cb.8884+0x2ec>
   4a0896: 0b246063            bltu  s0,s2,4a0936 <HOS_SystemInit+0x126>
   4a089a: 00004417            auipc s0,0x4
   4a089e: 65642403            lw  s0,1622(s0) # 4a4ef0 <_GLOBAL_OFFSET_TABLE_+0x24>
   4a08a2: 00004917            auipc s2,0x4
   4a08a6: 7aa92903            lw  s2,1962(s2) # 4a504c <_GLOBAL_OFFSET_TABLE_+0x180>
   4a08aa: 0b246363            bltu  s0,s2,4a0950 <HOS_SystemInit+0x140>
   4a08ae: 00004417            auipc s0,0x4
   4a08b2: 71e42403            lw  s0,1822(s0) # 4a4fcc <_GLOBAL_OFFSET_TABLE_+0x100>
   4a08b6: 00004917            auipc s2,0x4
   4a08ba: 6d292903            lw  s2,1746(s2) # 4a4f88 <_GLOBAL_OFFSET_TABLE_+0xbc>
   4a08be: 09246d63            bltu  s0,s2,4a0958 <HOS_SystemInit+0x148>
   4a08c2: 00004417            auipc s0,0x4
   4a08c6: 65642403            lw  s0,1622(s0) # 4a4f18 <_GLOBAL_OFFSET_TABLE_+0x4c>
   4a08ca: 00004917            auipc s2,0x4
   4a08ce: 76a92903            lw  s2,1898(s2) # 4a5034 <_GLOBAL_OFFSET_TABLE_+0x168>
   4a08d2: 0000e997            auipc s3,0xe
   4a08d6: d0e98993            addi  s3,s3,-754 # 4ae5e0 <cb.8884+0x2c4>
   4a08da: 0000ea17            auipc s4,0xe
   4a08de: d2ea0a13            addi  s4,s4,-722 # 4ae608 <cb.8884+0x2ec>
   4a08e2: 07246f63            bltu  s0,s2,4a0960 <HOS_SystemInit+0x150>
   4a08e6: 0000e517            auipc a0,0xe
   4a08ea: d4250513            addi  a0,a0,-702 # 4ae628 <cb.8884+0x30c>
   4a08ee: bbf590ef            jal ra,3fa4ac <printf>
   4a08f2: 6a9020ef            jal ra,4a379a <SAMGR_Bootstrap>
   4a08f6: 4732                  lw  a4,12(sp)
   4a08f8: 409c                  lw  a5,0(s1)
   4a08fa: 08f70063            beq a4,a5,4a097a <HOS_SystemInit+0x16a>
   4a08fe: 8bb550ef            jal ra,3f61b8 <__stack_chk_fail>
   4a0902: 85a2                  mv  a1,s0
   4a0904: 854e                  mv  a0,s3
   4a0906: ba7590ef            jal ra,3fa4ac <printf>
   4a090a: 400c                  lw  a1,0(s0)
   4a090c: 8552                  mv  a0,s4
   4a090e: 0411                  addi  s0,s0,4
   4a0910: b9d590ef            jal ra,3fa4ac <printf>
   4a0914: ffc42783            lw  a5,-4(s0)
   4a0918: 9782                  jalr  a5
   4a091a: bf15                  j 4a084e <HOS_SystemInit+0x3e>
   4a091c: 85a2                  mv  a1,s0
   4a091e: 854e                  mv  a0,s3
   4a0920: b8d590ef            jal ra,3fa4ac <printf>
   4a0924: 400c                  lw  a1,0(s0)
   4a0926: 8552                  mv  a0,s4
   4a0928: 0411                  addi  s0,s0,4
   4a092a: b83590ef            jal ra,3fa4ac <printf>
   4a092e: ffc42783            lw  a5,-4(s0)
   4a0932: 9782                  jalr  a5
   4a0934: bf3d                  j 4a0872 <HOS_SystemInit+0x62>
   4a0936: 85a2                  mv  a1,s0
   4a0938: 854e                  mv  a0,s3
   4a093a: b73590ef            jal ra,3fa4ac <printf>
   4a093e: 400c                  lw  a1,0(s0)
   4a0940: 8552                  mv  a0,s4
   4a0942: 0411                  addi  s0,s0,4
   4a0944: b69590ef            jal ra,3fa4ac <printf>
   4a0948: ffc42783            lw  a5,-4(s0)
   4a094c: 9782                  jalr  a5
   4a094e: b7a1                  j 4a0896 <HOS_SystemInit+0x86>
   4a0950: 401c                  lw  a5,0(s0)
   4a0952: 0411                  addi  s0,s0,4
   4a0954: 9782                  jalr  a5
   4a0956: bf91                  j 4a08aa <HOS_SystemInit+0x9a>
   4a0958: 401c                  lw  a5,0(s0)
   4a095a: 0411                  addi  s0,s0,4
   4a095c: 9782                  jalr  a5
   4a095e: b785                  j 4a08be <HOS_SystemInit+0xae>
   4a0960: 85a2                  mv  a1,s0
   4a0962: 854e                  mv  a0,s3
   4a0964: b49590ef            jal ra,3fa4ac <printf>
   4a0968: 400c                  lw  a1,0(s0)
   4a096a: 8552                  mv  a0,s4
   4a096c: 0411                  addi  s0,s0,4
   4a096e: b3f590ef            jal ra,3fa4ac <printf>
   4a0972: ffc42783            lw  a5,-4(s0)
   4a0976: 9782                  jalr  a5
   4a0978: b7ad                  j 4a08e2 <HOS_SystemInit+0xd2>
   4a097a: 0141                  addi  sp,sp,16
   4a097c: ee35906f            j 3fa85e <__riscv_restore_4>

上面这一段就是HOS_SystemInit的整个汇编实现了,是不是感觉看不懂,没关系,小A也不怎么懂,但是不妨碍我们找到我们想要的。

image-20201223090923874

红框中就是我们想要的,根据源码HOS_SystemInit,两个printf之间,夹了6条语句。途中标号1-6分别就是这6条语句执行完后的汇编回跳语句。我们想确认的MODULE_INIT(run);是第六条,因此我们从5号位后面开始看起,到6号位结束。

  4a08c6: 65642403            lw  s0,1622(s0) # 4a4f18 <_GLOBAL_OFFSET_TABLE_+0x4c>
  4a08ca: 00004917            auipc s2,0x4
  4a08ce: 76a92903            lw  s2,1898(s2) # 4a5034 <_GLOBAL_OFFSET_TABLE_+0x168>
  4a08d2: 0000e997            auipc s3,0xe
  4a08d6: d0e98993            addi  s3,s3,-754 # 4ae5e0 <cb.8884+0x2c4>
  4a08da: 0000ea17            auipc s4,0xe
  4a08de: d2ea0a13            addi  s4,s4,-722 # 4ae608 <cb.8884+0x2ec>
  4a08e2: 07246f63            bltu  s0,s2,4a0960 <HOS_SystemInit+0x150>

这里的第一条是访址寻址的意思,至于这个地址是什么,很明显

_GLOBAL_OFFSET_TABLE_+0x4c

image-20201223092458864

知道了这个地址,那它是什么呢,我们再深入一点

image-20201223092819099

看到这里就很明显了,是不是差不多可以证明实际上就是HOS_SystemInit函数体里“调用”到了我们的测试函数。真的是这样吗?我们继续看。这里去码揭晓真相。

image-20201223093827342

image-20201223094132428

看到了吗,HOS_SystemInit真的去了4aeb5c这个地址,然后把保存的函数指针(4a07e0)取了出来,并执行。结题,撒花。

总结:

​ 这种初始化方式是不是很有趣呢。其实熟悉linux驱动的话,大家就会发现这个机制和linux的initcall机制是一样的。这种通过构建初始化函数表的方式来完成初始化,简化了代码的耦合性并降低了代码的污染度,真的是一种很巧妙的设计。

​ 另外这种机制也是分优先级等级的,具体的优先级顺序,留给大家去探索吧。😀

​ 下回见~

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 10 7 评论 打赏
评论
2个
内容存在敏感词
手气红包
  • 绿波电龙 2021-04-26 15:37:39
    回复

    A哥会发就多发点

  • codinglab 2020-12-24 08:45:57
    回复

    补充一下:这里的优先级是指的section之间的优先级关系,但是在每个section保存的函数指针间是没有强制优先级的,取决于链接顺序。

相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
codinglab
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区