本篇关键词:、、、

下载 >> 离线文档.鸿蒙内核源码分析(百篇博客分析.挖透鸿蒙内核).pdf

内核汇编相关篇为:

链接器

查看 >> LD官方资料

board.ld

#include "los_vm_zone.h"
#define TEXT_BASE  KERNEL_VADDR_BASE //代码区为内核起始地址

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)

MEMORY //链接器的默认配置允许分配所有可用内存,描述链接器可以使用哪些内存区域
{ //ram,sram为存储区域的名字,可以随意取
    ram : ORIGIN = KERNEL_VADDR_BASE, LENGTH = KERNEL_VADDR_SIZE //内核运行空间范围
    sram : ORIGIN = 0x40000000, LENGTH = 0x1000 
    user_ram : ORIGIN = 0x1000000, LENGTH = 0x100000 //用户运行空间范围 USER_ASPACE_BASE ,此大小不是真正最后映射到用户空间的大小
}
SECTIONS
{
    /DISCARD/ : { *(.comment .note) } //过滤掉所有输入文件的 .comment .note 段

    .ram_vectors TEXT_BASE : { //内核中断向量区开始位置,此处用 TEXT_BASE 宏有误导嫌疑, 因为真正的(.text)并不在此 @notethinking 
        __ram_vectors_vma = .; //定位到当前位置,即TEXT_BASE处
        KEEP (*(.vectors)) //告诉链接器 强制保留所有输入文件中的 .vectors 节
    } > ram //中断向量是开机代码的位置 , 可翻看 鸿蒙内核源码分析(开机启动篇) 和 (中断管理篇)
    __ram_vectors_lma = LOADADDR(.ram_vectors);//启动时对向量的初始化,加载地址和链接地址一致,说明内核设计者希望从加载地址处运行指令
}
//LMA:加载存储地址,指加载到存储器的地址,即加载或烧写到哪里
//VMA:虚拟存储地址,也就是链接地址,即代码和数据运行的时候应在哪里
USER_INIT_VM_START = 0x1000000; //用户空间初始地址 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

liteos.ld

ENTRY(reset_vector) /*指定程序入口地址*/
INCLUDE board.ld // > ram 指放入ram这个地址范围中, ram在board.ld中定义
/* SECTIONS 是脚本中最重要的命令,所有的LD脚本都会有这个命令,用来指定如何将输入文件映射到输出文件等等 */
SECTIONS 
{		 
	 //节地址是指该节的VMA地址。如果改地未明确指定,连接器会在考虑严格对齐情况下,首先按照region参数分配内存,其次根据当前位置计数器向下分地址。
     _start = .; //特殊符号 .表示当前位置计数器,即紧挨着中断向量结束的位置 
    .set_sysinit_set : {
        __start_set_sysinit_set = ABSOLUTE(.); //
        KEEP (*(.set_sysinit_set))//所有输入文件中的 .set_sysinit_set 链接到此
        __stop_set_sysinit_set = ABSOLUTE(.);//定位结束 .set_sysinit_set 区结束位置
    } > ram

    .got ALIGN(0x4) : { *(.got.plt) *(.got) } > ram

    .gcc_except_table ALIGN (0x8) : { . = .; } > ram  .gcc_except_table : { KEEP(*(.gcc_except_table*)) }
    .exception_ranges ALIGN (0x8) : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } > ram

    .ARM.extab ALIGN(0x4) : { *(.ARM.extab* .gnu.linkonce.armextab.*) } > ram

    /* .ARM.exidx is sorted, so has to go in its own output section.  */
    .ARM.exidx ALIGN(0x8) : { __exidx_start = .; *(.ARM.exidx* .gnu.linkonce.armexidx.*) ;__exidx_end = .;} > ram

    /* text/read-only data */
    .text ALIGN(0x1000) : { //代码区 按4K对齐
        __text_start = .; //当前位置为 __text_start 开始位置
        *(.text* .sram.text.glue_7* .gnu.linkonce.t.*) //*(.text)
    } > ram
	//重定向代码 Relocation ,包括 代码区和数据区的重定位
    .rel.text : { *(.rel.text) *(.rel.text.*) *(.rel.gnu.linkonce.t*) } > ram
    .rela.text : { *(.rela.text) *(.rela.text.*) *(.rela.gnu.linkonce.t*) } > ram
    .rel.data : { *(.rel.data) *(.rel.data.*) *(.rel.gnu.linkonce.d*) } > ram
    .rela.data : { *(.rela.data) *(.rela.data.*) *(.rela.gnu.linkonce.d*) } > ram
    .rel.rodata : { *(.rel.rodata) *(.rel.rodata.*) *(.rel.gnu.linkonce.r*) } > ram
    .rela.rodata : { *(.rela.rodata) *(.rela.rodata.*) *(.rela.gnu.linkonce.r*) } > ram
    .rel.got : { *(.rel.got) } > ram
    .rela.got : { *(.rela.got) } > ram
    .rel.ctors : { *(.rel.ctors) } > ram
    .rela.ctors : { *(.rela.ctors) } > ram
    .rel.dtors : { *(.rel.dtors) } > ram
    .rela.dtors : { *(.rela.dtors) } > ram
    .rel.init : { *(.rel.init) } > ram
    .rela.init : { *(.rela.init) } > ram
    .rel.fini : { *(.rel.fini) } > ram
    .rela.fini : { *(.rela.fini) } > ram
    .rel.bss : { *(.rel.bss) } > ram
    .rela.bss : { *(.rela.bss) } > ram
    .rel.plt : { *(.rel.plt) } > ram
    .rela.plt : { *(.rela.plt) } > ram
    .rel.dyn : { *(.rel.dyn) } > ram

    .dummy_post_text : {
        __text_end = .; //代码区结束位置
    } > ram

    .rodata ALIGN(0x1000) : {
        __rodata_start = .; // 只读数据区开始位置
        __kernel_init_level_0 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.0.*)));
        __kernel_init_level_1 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.1.*)));
        __kernel_init_level_2 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.2.*)));
        __kernel_init_level_3 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.3.*)));
        __kernel_init_level_4 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.4.*)));
        __kernel_init_level_5 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.5.*)));
        __kernel_init_level_6 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.6.*)));
        __kernel_init_level_7 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.7.*)));
        __kernel_init_level_8 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.8.*)));
        __kernel_init_level_9 = ABSOLUTE(.);
        KEEP(*( SORT (.rodata.init.kernel.9.*)));
        __kernel_init_level_10 = ABSOLUTE(.);
        *(.rodata .rodata.* .gnu.linkonce.r.*)
        __exc_table_start = .;  //异常表开始位置
        KEEP(*(__exc_table))
        __exc_table_end = .;	//异常表结束位置
    } > ram

    /*
     * extra linker scripts tend to insert sections just after .rodata,
     * so we want to make sure this symbol comes after anything inserted above,
     * but not aligned to the next section necessarily.
     */
    .dummy_post_rodata : {
        _hdf_drivers_start = .;
        KEEP(*(.hdf.driver)) // 统一驱动框架也放在了只读区
        _hdf_drivers_end = .;
        __rodata_end = .; //数据只读区结束
    } > ram

    .data ALIGN(0x1000) : {
        /* writable data  */
        __ram_data_start = .; //可写入数据开始位置
        __vdso_data_start = LOADADDR(.data); // vdso区 (virtual dynamic shared object)开始位置 
        KEEP(*(.data.vdso.datapage)) //vdso由数据区+代码区两部分组成, 可翻看 鸿蒙内核源码分析(vdso篇) 
        . = ALIGN(0x1000);//vdso 的特性是 代码在内核区, 但运行却在用户区
        KEEP(*(.data.vdso.text))//vdso 的代码区
        . = ALIGN(0x1000);//按4K对齐,因啥要按4K对齐,因为需要页表映射,而一页为4K
        __vdso_text_end = .;//vdso区结束位置
        *(.data .data.* .gnu.linkonce.d.*)
        . = ALIGN(0x4);
        KEEP(*( SORT (.liteos.table.*)));
    } > ram

    .ctors : ALIGN(0x4) {
        __ctor_list__ = .;
        KEEP (*(.ctors .init_array))
        __ctor_end__ = .;
    } > ram
    .dtors : ALIGN(0x4) {
        __dtor_list__ = .;
        KEEP (*(.dtors .fini_array))
        __dtor_end__ = .;
    } > ram
    /*
     * extra linker scripts tend to insert sections just after .data,
     * so we want to make sure this symbol comes after anything inserted above,
     * but not aligned to the next section necessarily.
     */
    .dummy_post_data : {
        __ram_data_end = .; //可写入数据区结束
    } > ram
	//这里指的是 init 应用程度的位置
    .user_init USER_INIT_VM_START : ALIGN(0x1000) {//开始地址设为 USER_INIT_VM_START = 0x1000000;
        . = ALIGN(0x4);
        __user_init_load_addr = LOADADDR(.user_init);//应用程序的加载地址
        __user_init_entry = .;//应用程序的入口地址
        KEEP(libuserinit.O (.user.entry))
        KEEP(libuserinit.O (.user.text))
        KEEP(libuserinit.O (.user.rodata))
        . = ALIGN(0X4);
        __user_init_data = .;//设置数据段开始位置 __user_init_data
        KEEP(libuserinit.O (.user.data))
        . = ALIGN(0X4);
        __user_init_bss = .;//init 进程的 bss开始位置
        KEEP(libuserinit.O (.user.bss))
        . = ALIGN(0x1000);
        __user_init_end = .; //init 进程结束位置
    } > user_ram AT > ram

    __user_init_size = __user_init_end - __user_init_entry; //计算init进程占用大小

    /* uninitialized data (in same segment as writable data) | 未初始化数据 */
    .bss : {
        . = ALIGN(0x800); //当前位置按 0x800对齐
        __int_stack_start = .; //内核栈开始位置
        *(.int_stack);
        . = ALIGN(0x4);//4字节对齐
        KEEP(*(.bss.prebss.*))
        . = ALIGN(0x8);
        __bss_start = .;	//将当前位置给__bss_start,将所有的目标*(.bss .bss.*) .. 链接到 .bss中
        *(.bss .bss.*)
        *(.gnu.linkonce.b.*)
        *(COMMON)
        . = ALIGN(0x8);
        __bss_end = .;
    } > ram

    . = ALIGN(0x1000);
    _end = .;
    /* mmu temp page table(sys aviliable mem is start with __bss_end) */
    . = ALIGN(0x4000);
    __mmu_ttlb_begin = .;//临时页表开始位置

    /* Strip unnecessary stuff */
    /DISCARD/ 0 : { *(.comment .note) } > ram //过滤不需要的块
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

百文说内核 | 抓住主脉络

  • 百文相当于摸出内核的肌肉和器官系统,让人开始丰满有立体感,因是直接从注释源码起步,在加注释过程中,每每有心得处就整理,慢慢形成了以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切。
  • 与代码需不断debug一样,文章内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。
  • 百文在 < 鸿蒙研究站 | 开源中国 | 博客园 | 51cto | csdn | 知乎 | 掘金 > 站点发布,百篇博客系列目录如下。

按功能模块:

基础知识 进程管理 任务管理 内存管理
双向链表
内核概念
源码结构
地址空间
计时单位
优雅的宏
钩子框架
位图管理
POSIX
main函数
调度故事
进程控制块
进程空间
线性区
红黑树
进程管理
Fork进程
进程回收
Shell编辑
Shell解析
任务控制块
并发并行
就绪队列
调度机制
任务管理
用栈方式
软件定时器
控制台
远程登录
协议栈
内存规则
物理内存
内存概念
虚实映射
页表管理
静态分配
TLFS算法
内存池管理
原子操作
圆整对齐
通讯机制 文件系统 硬件架构 内核汇编
通讯总览
自旋锁
互斥锁
快锁使用
快锁实现
读写锁
信号量
事件机制
信号生产
信号消费
消息队列
消息封装
消息映射
共享内存
文件概念
文件故事
索引节点
VFS
文件句柄
根文件系统
挂载机制
管道文件
文件映射
写时拷贝
芯片模式
ARM架构
指令集
协处理器
工作模式
寄存器
多核管理
中断概念
中断管理
编码方式
汇编基础
汇编传参
链接脚本
内核启动
进程切换
任务切换
中断切换
异常接管
缺页中断
编译运行 调测工具
编译过程
编译构建
GN语法
忍者无敌
ELF格式
ELF解析
静态链接
重定位
动态链接
进程映像
应用启动
系统调用
VDSO
模块监控
日志跟踪
系统安全
测试用例

百万注源码 | 处处扣细节

  • 百万汉字注解内核目的是要看清楚其毛细血管,细胞结构,等于在拿放大镜看内核。内核并不神秘,带着问题去源码中找答案是很容易上瘾的,你会发现很多文章对一些问题的解读是错误的,或者说不深刻难以自圆其说,你会慢慢形成自己新的解读,而新的解读又会碰到新的问题,如此层层递进,滚滚向前,拿着放大镜根本不愿意放手。

  • < gitee | github | coding | gitcode > 四大码仓推送 | 同步官方源码。

关注不迷路 | 代码即人生

期间不断得到小伙伴的支持,有学生,有职场新人,也有老江湖,在此一并感谢,大家的支持是前进的动力。尤其每次收到学生的赞助很感慨,后生可敬。 >> 查看捐助名单

据说喜欢 点赞 + 分享 的,后来都成了大神。😃