[HarmonyOS之旅] Chapter5 - 有趣的init
大家晚上好。不知道大家还记不记得上一篇的最后我们留下了一个问题呢?
前情回顾:
参考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();
}
得到的结果是这样的。
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也不怎么懂,但是不妨碍我们找到我们想要的。
红框中就是我们想要的,根据源码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
知道了这个地址,那它是什么呢,我们再深入一点
看到这里就很明显了,是不是差不多可以证明实际上就是HOS_SystemInit函数体里“调用”到了我们的测试函数。真的是这样吗?我们继续看。这里去码揭晓真相。
看到了吗,HOS_SystemInit真的去了4aeb5c这个地址,然后把保存的函数指针(4a07e0)取了出来,并执行。结题,撒花。
总结:
这种初始化方式是不是很有趣呢。其实熟悉linux驱动的话,大家就会发现这个机制和linux的initcall机制是一样的。这种通过构建初始化函数表的方式来完成初始化,简化了代码的耦合性并降低了代码的污染度,真的是一种很巧妙的设计。
另外这种机制也是分优先级等级的,具体的优先级顺序,留给大家去探索吧。😀
下回见~
- 分享
- 举报
-
浏览量:6358次2020-12-19 11:23:50
-
浏览量:5435次2021-01-07 17:33:46
-
浏览量:8526次2020-12-14 12:42:14
-
浏览量:9142次2020-12-15 21:43:52
-
浏览量:9129次2020-12-12 09:47:20
-
浏览量:9433次2020-12-08 19:45:14
-
浏览量:4620次2021-04-15 14:56:16
-
浏览量:5070次2021-04-15 15:05:49
-
浏览量:1910次2020-02-20 10:58:43
-
浏览量:2211次2020-02-26 10:38:33
-
浏览量:2916次2020-11-18 14:34:59
-
浏览量:6303次2021-09-14 13:51:27
-
浏览量:5022次2021-07-30 15:32:45
-
浏览量:5601次2021-09-18 13:38:23
-
浏览量:2229次2019-06-04 09:34:31
-
浏览量:4023次2021-08-25 13:39:25
-
浏览量:2269次2019-12-07 09:57:57
-
浏览量:4423次2021-06-25 14:01:34
-
浏览量:4372次2021-09-28 13:48:16
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
codinglab
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明
A哥会发就多发点
补充一下:这里的优先级是指的section之间的优先级关系,但是在每个section保存的函数指针间是没有强制优先级的,取决于链接顺序。