
v53.03 鸿蒙内核源码分析(ELF解析) | 敢忘了她姐俩你就不是银 原创 精华
季路问事鬼神。子曰:“未能事人,焉能事鬼?”敢问死。曰:“未知生,焉知死?” 《论语》:先进篇
百篇博客系列篇.本篇为:
v53.xx 鸿蒙内核源码分析(ELF解析篇) | 敢忘了她姐俩你就不是银
加载运行相关篇为:
- v51.04 鸿蒙内核源码分析(ELF格式) | 应用程序入口并非main
- v53.03 鸿蒙内核源码分析(ELF解析) | 敢忘了她姐俩你就不是银
- v54.04 鸿蒙内核源码分析(静态链接) | 一个小项目看中间过程
- v55.04 鸿蒙内核源码分析(重定位) | 与国际接轨的对外发言人
- v56.05 鸿蒙内核源码分析(进程映像) | 程序是如何被加载运行的
ELF,它实在是太重要了,内核加载的就是它,不说清楚它怎么去说清楚应用程序运行的过程呢.看到下面这一坨一坨的,除了.text
,.bss
,.data
听过见过外,其他的咱也没啥交情。
系列篇要全说清楚也不太可能,可以去看 ELF官方文档(106页) ,本篇试图与它多些交情,混个脸熟,方便后续推进.从两个命令入手。
readelf -S app
和 readelf -s app
这俩宝贝长的很像,但仔细看中间参数是大S和小s,说到大S小s又有点意思了,这姐妹俩上了点年纪的码农都应该不陌生,据说是性格完全不同.个人喜欢大的,甜美安静,小的太聒噪,受不了,码农最需要安静了。
readelf -S app
先看老大是干啥的,其实她是她们家老二,上面还有个姐姐,没啥存在感,不管她了。
显示所有区头信息 | sections’ header
解读
命令结果主要三个部分,区名称(Section Head Name),区类型 (Section Head Type) 和区标签(Section Head Flag)
Name
部分 出现了一些熟悉的内容.bss
,.text
,但更多是看不懂的.fini
,.plt
,.relname
Type
部分 就有更多看不懂的NULL
,PROGBITS
,INIT_ARRAY
等等.Flag
部分 好像也似懂非懂.
一个区只属于一个类型,具有排它性,跟男人,女人一样.
但身上可以贴多个标签.可以是码农,可以是高富帅,可以是脱发男,不对!!! 码农你还想是高富帅,想多了.脱发才是你的标配.例如:
- 代码区(
.text
)属于PROGBITS
类型被贴上了AX
(alloc
+execute
)标签.原来代码区可以被CPU取指运行是因为在ELF中被贴上了可运行标签.但注意.text
是只读不可写,因为它身上没有write
标签. - 再看熟悉两个数据区
.bss
和.data
,它们都有WA
(write
+alloc
)标签,可写+运行过程中需要占用内存,但二者区别是类型的不同,.bss
是NOBITS
类型.data
是PROGBITS
类型
区名称 | Section Head Name
简称:SHN
在ELF文件中有一些特定的区是预定义好的,其内容是指令代码或者控制信息.这些区专门为操作系统使用,对于不同的操作系统,这些区的类型和属性有所不同。
在构建可执行程序时,链接器(linker)可能需要把一些独立的目标文件和库文件链接在一起,在这个过程中,链接器要解析各个文件中的相互引用,调整某些目标文件中的绝对引用,并重定位指令码。
每种操作系统都有自己的一套链接模型,但总的来说,不外乎静态和动态两类:
-
静态链接:所有的目标文件和动态链接库被静态地绑定在一起,所有的符号都被解析出来.所创建的目标文件是完整的,运行时不依赖于任何外部的库。
-
动态链接:所有的目标文件,系统共享资源以及共享库以动态的形式链接在一起,外部库的内容没有完整地拷贝进来。如果创建的是可执行文件的话,程序在运行的时候,在构建时所依赖的那些库必须在系统中能找到,把它们一并装载之后,程序才能运行起来。运行期间如何解析那些动态链接进来的符号引用,不同的系统有各自不同的方式。
根据区功能划分:
- 有些区包含调试信息,比如.debug和.line区.
- 有些区包含程序控制信息,比如.bss,.data,.data1,.rodata和.rodata1这些区.
- 还有一些区含有程序或控制信息,这些区由系统使用,有指定的类型和属性.它们中的大多数都将用于链接过程.动态链接过程所需要的信息由.dynsym,.dynstr,.interp,.hash,.dynamic,.rel,.rela,.got,.plt等区提供.其中有些区(比如.plt和.got)的内容依处理器而不同,但它们都支持同样的链接模型.
以点号"."为前缀的区名字是为系统保留的.应用程序也可以构造自己的区,但最好不要取与上述系统已定义的区相同的名字,也不要取以点号开头的名字,以避免潜在的冲突,注意,目标文件中区的名字并不具有唯一性,可以存在多个相同名字的区.具体如下:
详细解读
.text
通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码区为可写,即允许修改程序.在代码区中,也有可能包含一些只读的常数变量,例如字符串常量等。.rodata
和.data
区类型一样但标签有别,.rodata
只有A
标,是个只读区,比如字符串常量,全局const变量和#define定义的常量,又称为常量区
但是注意,并不是所有的常量都放在rodata区的,其特殊情况如下:- 有些立即数与指令编译在一起直接放在代码区。
- 对于字符串常量,编译器会去掉重复的常量,让程序的每个字符串常量只有一份
- 有些系统中rodata区是多个进程共享的,目的是为了提高空间的利用率
.bss
和.data
是标签一样但类型有别,.bss
区属于静态内存分配.通常是指用来存放程序中未初始化的全局变量和未初始化的局部静态变量.未初始化的全局变量和未初始化的局部静态变量默认值是0,本来这些变量也可以放到data
区的,但是因为它们都是0,所以它们在data
区分配空间并且存放数据0是没有必要的.在程序运行时,才会给BSS区里面的变量分配内存空间.在目标文件(*.o)和可执行文件中,.bss
只是为未初始化的全局变量和未初始化的局部静态变量预留位置而已,它并没有内容,所以它不占据空间。.data
通常是指用来存放程序中已初始化的全局变量和已初始化的静态变量的一块内存区域,属于静态内存分配。
区类型 | Section Head Type
简称:SHT
解读
.bss
类型为NOBITS
,这一区的内容是空的,区并不占用实际的空间, 没有初值的全局变量就放在这个区.它是真没有值,由运行过程中映射到哪个地址就取哪个地址的值.鬼知道跑哪个位置的。PROGBITS
本区内容的格式和含义都由程序来决定,属于这个区的内容还挺多的.text
,.data
,.init
,.rodata
,这些区默认自带运行时数据.不需要你额外提供,区别是这些自带数据运行时可不可以被改变..data
可以被程序运行时逻辑所修改,.rodata
不可改,即常量数据。
区标签 | Section Head Flag
简称:SHF
解读
此处看下与数据相关的三个区,仔细对照看参数发现其真正的区别.
readelf -s app
说完大S再来说小S
显示所有符号表 | Symbol Table.
解读
.dynsym
,.symtab
两区的类型如下,是一个含义.
正如描述所言,.dynsym
是.symtab
的缩小版,在其中能看到亲切的printf
.具体请参考以下四个维度来理解符号表.
符号表绑定 | Symbol Table Bind
简称 STB
符号表类型 | Symbol Table Type
简称 STT
符号表可见性 | Symbol Table Visibility
简称 STV
符号表索引 | Symbol Table Ndx
简称 STN
任何一个符号表项的定义都与某一个"区"相联系,因为符号是为区而定义,在区中被引用。本数据成员即指明了相关联的区。本数据成员是一个索引值,它指向相关联的区在区头表中的索引。在重定位过程中,区的位置会改变,本数据成员的值也随之改变,继续指向区的新位置。当本数据成员指向下面三种特殊的区索引值时,本符号具有如下特别的意义:
百篇博客分析.深挖内核地基
- 给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
- 与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。
按功能模块:
- 前因后果 >> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
- 基础工具 >> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
- 加载运行 >> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
- 进程管理 >> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
- 编译构建 >> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
- 进程通讯 >> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
- 内存管理 >> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
- 任务管理 >> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
- 文件系统 >> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
- 硬件架构 >> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
百万汉字注解.精读内核源码
四大码仓中文注解 . 定期同步官方代码
鸿蒙研究站( weharmonyos ) | 每天死磕一点点,原创不易,欢迎转载,请注明出处。若能支持点赞则更佳,感谢每一份支持。
