开源鸿蒙内核源码分析系列 | ELF解析 | 敢忘了她姐俩你就不是银

开源鸿蒙内核源码分析系列 | ELF解析 | 敢忘了她姐俩你就不是银

大S和小s

ELF,它实在是太重要了,内核加载的就是它,不说清楚它怎么去说清楚应用程序运行的过程呢。看到下面这一坨一坨的,除了.text,.bss,.data听过见过外,其他的咱也没啥交情。


01     .interp
02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03     .init .plt .plt.got .plt.sec .text .fini
04     .rodata .eh_frame_hdr .eh_frame
05     .init_array .fini_array .dynamic .got .data .bss  

系列篇要全说清楚也不太可能,可以去看 ELF官方文档(106页) ,本篇试图与它多些交情,混个脸熟,方便后续推进。从两个命令入手。 readelf -S app 和 readelf -s app这俩宝贝长的很像,但仔细看中间参数是大S和小s,说到大S小s又有点意思了,这姐妹俩上了点年纪的码农都应该不陌生,据说是性格完全不同。个人喜欢大的,甜美安静,小的太聒噪,受不了,码农最需要安静了。

大S-readelf -S app

先看老大是干啥的,其实她是她们家老二,上面还有个姐姐,没啥存在感,不管她了。


root@5e3abe332c5a:/home/docker/case_code_100# readelf -h
    ...
  -S --section-headers   Display the sections' header
     --sections          An alias for --section-headers  
  -s --syms              Display the symbol table
     --symbols           An alias for --syms      

显示所有区头信息 | sections’ header


root@5e3abe332c5a:/home/docker/case_code_100# readelf -S app
There are 31 section headers, starting at offset 0x39c0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.propert NOTE             0000000000000338  00000338
       0000000000000020  0000000000000000   A       0     0     8
  [ 3] .note.gnu.build-i NOTE             0000000000000358  00000358
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000037c  0000037c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003a0  000003a0
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003c8  000003c8
       00000000000000a8  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000470  00000470
       0000000000000084  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           00000000000004f4  000004f4
       000000000000000e  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          0000000000000508  00000508
       0000000000000020  0000000000000000   A       7     1     8
  [10] .rela.dyn         RELA             0000000000000528  00000528
       00000000000000d8  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             0000000000000600  00000600
       0000000000000018  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020
       0000000000000020  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001040  00001040
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001050  00001050
       0000000000000010  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         0000000000001060  00001060
       00000000000001b5  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         0000000000001218  00001218
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       000000000000001b  0000000000000000   A       0     0     4
  [19] .eh_frame_hdr     PROGBITS         000000000000201c  0000201c
       000000000000004c  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002068  00002068
       0000000000000128  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003db8  00002db8
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003dc0  00002dc0
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003dc8  00002dc8
       00000000000001f0  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003fb8  00002fb8
       0000000000000048  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000018  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004018  00003018
       0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00003018
       000000000000002a  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00003048
       0000000000000648  0000000000000018          29    46     8
  [29] .strtab           STRTAB           0000000000000000  00003690
       0000000000000216  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  000038a6
       000000000000011a  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

解读:

  • 命令结果主要三个部分,区名称(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类型

大S-区名称 | Section Head Name (SHN)

在ELF文件中有一些特定的区是预定义好的,其内容是指令代码或者控制信息。这些区专门为操作系统使用,对于不同的操作系统,这些区的类型和属性有所不同。

在构建可执行程序时,链接器(linker)可能需要把一些独立的目标文件和库文件链接在一起,在这个过程中,链接器要解析各个文件中的相互引用,调整某些目标文件中的绝对引用,并重定位指令码。

每种操作系统都有自己的一套链接模型,但总的来说,不外乎静态和动态两类:

静态链接:所有的目标文件和动态链接库被静态地绑定在一起,所有的符号都被解析出来。所创建的目标文件是完整的,运行时不依赖于任何外部的库。

动态链接:所有的目标文件,系统共享资源以及共享库以动态的形式链接在一起,外部库的内容没有完整地拷贝进来。如果创建的是可执行文件的话,程序在运行的时候,在构建时所依赖的那些库必须在系统中能找到,把它们一并装载之后,程序才能运行起来。运行期间如何解析那些动态链接进来的符号引用,不同的系统有各自不同的方式。

根据区功能划分:

有些区包含调试信息,比如.debug和.line区。

有些区包含程序控制信息,比如.bss,.data,.data1,.rodata和.rodata1这些区。

还有一些区含有程序或控制信息,这些区由系统使用,有指定的类型和属性。它们中的大多数都将用于链接过程。动态链接过程所需要的信息由.dynsym,.dynstr,.interp,.hash,.dynamic,.rel,.rela,.got,.plt等区提供。其中有些区(比如.plt和.got)的内容依处理器而不同,但它们都支持同样的链接模型。

以点号”.”为前缀的区名字是为系统保留的。应用程序也可以构造自己的区,但最好不要取与上述系统已定义的区相同的名字,也不要取以点号开头的名字,以避免潜在的冲突,注意,目标文件中区的名字并不具有唯一性,可以存在多个相同名字的区。具体如下:


区名              描述说明
.bss            本区中包含目标文件中未初始化的全局变量。一般情况下,可执行程序在开始运行的时候,系统会把这一区内容清零。但是,
                在运行期间的bss区是由系统初始化而成的,在目标文件中.bss区并不包含任何内容,其长度为0,所以它的区类型为NOBITS。
.comment        本区包含版本控制信息。
.data           这个区不陌生,用于存放程序中被初始化过的全局变量。在目标文件中,它们是占用实际的存储空间的,与.bss区不同。
.debug          本区中含有调试信息,内容格式没有统一规定。所有以".debug"为前缀的区名字都是保留的。
.dynamic        本区包含动态链接信息,并且可能有SHF_ALLOC和SHF_WRITE等属性。是否具有SHF_WRITE属性取决于操作系统和处理器。
.dynstr         本区含有用于动态链接的字符串,一般是那些与符号表相关的名字。具有SHF_ALLOC属性
.dynsym         本区含有动态链接符号表。具有SHF_ALLOC属性,因为它需要在运行时被加载
.got            本区包含全局偏移量表(global offset table)。
.hash           本区包含一张符号哈希表。
.init           本区包含进程初始化时要执行的程序指令,当程序开始运行时,系统会在进入主函数之前执行这一区中的代码。
.fini           程序终止代码区,当程序结束运行时,系统会在最后执行这一区中的代码。
.interp         本区含有ELF程序解析器的路径名。如果本区被包含在某个可装载的区中,那么本区的属性中应置SHF_ALLOC标志位,否则不置此标志。
.line           本区也是一个用于调试的区,它包含那些调试符号的行号,为程序指令码与源文件的行号建立起联系。其内容格式没有统一规定。
.note           本区所包含的信息在第2章"注释区(note section)"部分描述。
.plt            本区包含函数链接表。动态链接时使用的过程链接表(precedure linkage table)
.relname        同下
.relaname       这两个区含有重定位信息。如果本区被包含在某个可装载的区中,那么本区的属性中应置SHF_ALLOC标志位,否则不置此标志。注意,这两个区的名字中"name"是可替换的部分,执照惯例,
                对哪一区做重定位就把"name"换成哪一区的名字。比如,.text区的重定位区的名字将是.rel.text或.rela.text。
.rodata         同下
.rodata1        本区包含程序中的只读数据,在程序装载时,它们一般会被装入进程空间中那些只读的区中去。
.shstrtab       本区是"区名字表",含有所有其它区的名字,如 `.data`,`.bss`,`.text`...
.strtab         本区用于存放字符串,主要是那些符号表项的名字。如果一个目标文件有一个可装载的区,并且其中含有符号表,存储的是变量名,函数名等。
.symtab         本区用于存放符号表。如果一个目标文件有一个可载入的区,并且其中含有符号表,那么本区的属性中应该有SHF_ALLOC。
.text           本区包含程序指令代码。
.rel.text       重定位的地方在.text段内,以offset指定具体要定位位置。在连接时候由连接器完成。注意比较。text段前后变化。
                指的是比较.o文件和最终的执行文件(或者动态库文件)。就是重定位前后比较,以上是说明了具体比较对象而已。 
                一般由编译器编译产生,存在于obj文件内。
.rel.dyn        重定位的地方在.got段内。主要是针对外部数据变量符号。例如全局数据。重定位在程序运行时定位,
                一般是在.init段内。定位过程:获得符号对应value后,根据rel.dyn表中对应的offset,修改.got表对应位置的
                value。另外,.rel.dyn 含义是指和dyn有关,一般是指在程序运行时候,动态加载。
                区别于rel.plt,rel.plt是指和plt相关,具体是指在某个函数被调用时候加载。
                一般由连接器产生,存在于可执行文件或者动态库文件。
.rel.plt        重定位的地方在.got.plt段内(注意也是。got内,具体区分而已)。 主要是针对外部函数符号。一般是函数首次
                被调用时候重定位。可看汇编,理解其首次访问是如何重定位的,实际很简单,就是初次重定位函数地址,
                然后把最终函数地址放到.got.plt内,以后读取该.got.plt就直接得到最终函数地址(参考过程说明)。  
                所有外部函数调用都是经过一个对应桩函数,这些桩函数都在.plt段内。 
                一般由连接器产生,存在于可执行文件或者动态库文件。
                借助这两个辅助段可以动态修改对应.got和.got.plt段,从而实现运行时重定位。
.rel.data      常量区重定位信息
.rel.rodata    数据段重定位信息                   

详细解读:

.text 通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码区为可写,即允许修改程序。在代码区中,也有可能包含一些只读的常数变量,例如字符串常量等。

.rodata和.data区类型一样但标签有别,.rodata只有A标,是个只读区,比如字符串常量,全局const变量和#define定义的常量,又称为常量区 但是注意,并不是所有的常量都放在rodata区的,其特殊情况如下:

有些立即数与指令编译在一起直接放在代码区。

对于字符串常量,编译器会去掉重复的常量,让程序的每个字符串常量只有一份

有些系统中rodata区是多个进程共享的,目的是为了提高空间的利用率

.bss和.data是标签一样但类型有别,.bss区属于静态内存分配。通常是指用来存放程序中未初始化的全局变量和未初始化的局部静态变量。未初始化的全局变量和未初始化的局部静态变量默认值是0,本来这些变量也可以放到data区的,但是因为它们都是0,所以它们在data区分配空间并且存放数据0是没有必要的。在程序运行时,才会给BSS区里面的变量分配内存空间。在目标文件(*.o)和可执行文件中,.bss只是为未初始化的全局变量和未初始化的局部静态变量预留位置而已,它并没有内容,所以它不占据空间。

.data 通常是指用来存放程序中已初始化的全局变量和已初始化的静态变量的一块内存区域,属于静态内存分配。

大S-区类型 | Section Head Type(SHT)


SHT_NULL        本区头是一个无效的(非活动的)区头,它也没有对应的区。本区头中的其它成员的值也都是没有意义的。
SHT_PROGBITS    本区所含有的信息是由程序定义的,本区内容的格式和含义都由程序来决定。
SHT_SYMTAB      同DYNSYM
SHT_DYNSYM      这两类区都含有符号表。目前,目标文件中最多只能各包含一个这两种区,但这种限制以后可能会取消。
                一般来说,SYMTAB提供的符号用于在创建目标文件的时候编辑链接,在运行期间也有可能会用于动态链接。
                SYMTAB包含完整的符号表,它往往会包含很多在运行期间(动态链接)用不到的符号。所以,一个目标文件
                可以再有一个DYNSYM区,它含有一个较小的符号表,专门用于动态链接。
SHT_STRTAB      本区是字符串表。目标文件中可以包含多个字符串表区。
SHT_RELA        本区是一个重定位区,含有带明确加数(addend)的重定位项,对于32位类型的目标文件来说,
                这个加数就是Elf32_Rela。一个目标文件可能含有多个重定位区。
SHT_HASH        本区包含一张哈希表。所有参与动态链接的目标文件都必须要包含一个符号哈希表。目前,一个目标文件中最多只能有一个哈希表,
                但这一限制以后可能会取消。
SHT_DYNAMIC     本区包含的是动态链接信息。目前,一个目标文件中最多只能有一个DYNAMIC区,
                但这一限制以后可能会取消。
SHT_NOTE        本区包含的信息用于以某种方式来标记本文件。
SHT_NOBITS      这一区的内容是空的,区并不占用实际的空间。只代表一个逻辑上的位置概念,并不代表实际的内容。
SHT_REL         本区是一个重定位区,含有带明确加数的重定位项,对于32位类型的目标文件来说,这个加数就是Elf32_Rel。一个目标文件可能含有多个重定位区。
SHT_SHLIB       此值是一个保留值,暂未指定语义。
SHT_LOPROC      为特殊处理器保留的区类型索引值的下边界。
SHT_HIPROC      为特殊处理器保留的区类型索引值的上边界。LOPROC ~ HIPROC区间是为特殊处理器区类型的保留值。
SHT_LOUSER      为应用程序保留区类型索引值的下边界。
SHT_HIUSER      为应用程序保留区类型索引值的下边界。LOUSER ~ HIUSER区间的区类型可由应用程序自行定义,是一区保留值。

解读:

.bss 类型为 NOBITS,这一区的内容是空的,区并不占用实际的空间, 没有初值的全局变量就放在这个区。它是真没有值,由运行过程中映射到哪个地址就取哪个地址的值。鬼知道跑哪个位置的。

PROGBITS本区内容的格式和含义都由程序来决定,属于这个区的内容还挺多的 .text,.data, .init, .rodata ,这些区默认自带运行时数据。不需要你额外提供,区别是这些自带数据运行时可不可以被改变。.data可以被程序运行时逻辑所修改,.rodata不可改,即常量数据。

大S-区标签 | Section Head Flag(SHF)


Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)


名字         值       描述
SHF_WRITE       0x01       如果此标志被设置,表示本区所包含的内容在进程运行过程中是可写的。
SHF_ALLOC       0x02       如果此标志被设置,表示本区内容在进程运行过程中要占用内存单元。并不是所有区。
                       都会占用实际的内存,有一些起控制作用的区,在目标文件映射到进程空间时,并不需要占用内存。
SHF_EXECUTE     0x04        如果此标志被设置,表示本区内容是指令代码。

解读:

此处看下与数据相关的三个区,仔细对照看参数发现其真正的区别。


Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [18] .rodata           PROGBITS         0000000000002000  00002000
       000000000000001b  0000000000000000   A       0     0     4
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000018  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004018  00003018
       0000000000000008  0000000000000000  WA       0     0     1

小s-readelf -s app

说完大S再来说小s。


root@5e3abe332c5a:/home/docker/case_code_100# readelf -h
    ...
  -S --section-headers   Display the sections' header
     --sections          An alias for --section-headers  
  -s --syms              Display the symbol table
     --symbols           An alias for --syms      

显示所有符号表 | Symbol Table。


root@5e3abe332c5a:/home/docker/case_code_100# readelf -s app
Symbol table '.dynsym' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     6: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 67 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000318     0 SECTION LOCAL  DEFAULT    1
     2: 0000000000000338     0 SECTION LOCAL  DEFAULT    2
     3: 0000000000000358     0 SECTION LOCAL  DEFAULT    3
     4: 000000000000037c     0 SECTION LOCAL  DEFAULT    4
     5: 00000000000003a0     0 SECTION LOCAL  DEFAULT    5
     6: 00000000000003c8     0 SECTION LOCAL  DEFAULT    6
     7: 0000000000000470     0 SECTION LOCAL  DEFAULT    7
     8: 00000000000004f4     0 SECTION LOCAL  DEFAULT    8
     9: 0000000000000508     0 SECTION LOCAL  DEFAULT    9
    10: 0000000000000528     0 SECTION LOCAL  DEFAULT   10
    11: 0000000000000600     0 SECTION LOCAL  DEFAULT   11
    12: 0000000000001000     0 SECTION LOCAL  DEFAULT   12
    13: 0000000000001020     0 SECTION LOCAL  DEFAULT   13
    14: 0000000000001040     0 SECTION LOCAL  DEFAULT   14
    15: 0000000000001050     0 SECTION LOCAL  DEFAULT   15
    16: 0000000000001060     0 SECTION LOCAL  DEFAULT   16
    17: 0000000000001218     0 SECTION LOCAL  DEFAULT   17
    18: 0000000000002000     0 SECTION LOCAL  DEFAULT   18
    19: 000000000000201c     0 SECTION LOCAL  DEFAULT   19
    20: 0000000000002068     0 SECTION LOCAL  DEFAULT   20
    21: 0000000000003db8     0 SECTION LOCAL  DEFAULT   21
    22: 0000000000003dc0     0 SECTION LOCAL  DEFAULT   22
    23: 0000000000003dc8     0 SECTION LOCAL  DEFAULT   23
    24: 0000000000003fb8     0 SECTION LOCAL  DEFAULT   24
    25: 0000000000004000     0 SECTION LOCAL  DEFAULT   25
    26: 0000000000004018     0 SECTION LOCAL  DEFAULT   26
    27: 0000000000000000     0 SECTION LOCAL  DEFAULT   27
    28: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    29: 0000000000001090     0 FUNC    LOCAL  DEFAULT   16 deregister_tm_clones
    30: 00000000000010c0     0 FUNC    LOCAL  DEFAULT   16 register_tm_clones
    31: 0000000000001100     0 FUNC    LOCAL  DEFAULT   16 __do_global_dtors_aux
    32: 0000000000004018     1 OBJECT  LOCAL  DEFAULT   26 completed.8060
    33: 0000000000003dc0     0 OBJECT  LOCAL  DEFAULT   22 __do_global_dtors_aux_fin
    34: 0000000000001140     0 FUNC    LOCAL  DEFAULT   16 frame_dummy
    35: 0000000000003db8     0 OBJECT  LOCAL  DEFAULT   21 __frame_dummy_init_array_
    36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    37: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    38: 000000000000218c     0 OBJECT  LOCAL  DEFAULT   20 __FRAME_END__
    39: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
    40: 0000000000003dc0     0 NOTYPE  LOCAL  DEFAULT   21 __init_array_end
    41: 0000000000003dc8     0 OBJECT  LOCAL  DEFAULT   23 _DYNAMIC
    42: 0000000000003db8     0 NOTYPE  LOCAL  DEFAULT   21 __init_array_start
    43: 000000000000201c     0 NOTYPE  LOCAL  DEFAULT   19 __GNU_EH_FRAME_HDR
    44: 0000000000003fb8     0 OBJECT  LOCAL  DEFAULT   24 _GLOBAL_OFFSET_TABLE_
    45: 0000000000001000     0 FUNC    LOCAL  DEFAULT   12 _init
    46: 0000000000001210     5 FUNC    GLOBAL DEFAULT   16 __libc_csu_fini
    47: 0000000000004010     8 OBJECT  GLOBAL DEFAULT   25 my_name
    48: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    49: 0000000000004000     0 NOTYPE  WEAK   DEFAULT   25 data_start
    50: 0000000000004018     0 NOTYPE  GLOBAL DEFAULT   25 _edata
    51: 0000000000001218     0 FUNC    GLOBAL HIDDEN    17 _fini
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    53: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    54: 0000000000004000     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
    55: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    56: 0000000000004008     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle
    57: 0000000000002000     4 OBJECT  GLOBAL DEFAULT   18 _IO_stdin_used
    58: 00000000000011a0   101 FUNC    GLOBAL DEFAULT   16 __libc_csu_init
    59: 0000000000004020     0 NOTYPE  GLOBAL DEFAULT   26 _end
    60: 0000000000001060    47 FUNC    GLOBAL DEFAULT   16 _start
    61: 0000000000004018     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
    62: 0000000000001174    30 FUNC    GLOBAL DEFAULT   16 main
    63: 0000000000001149    43 FUNC    GLOBAL DEFAULT   16 say_hello
    64: 0000000000004018     0 OBJECT  GLOBAL HIDDEN    25 __TMC_END__
    65: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    66: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.2

解读:

.dynsym,.symtab两区的类型如下,是一个含义。


SHT_SYMTAB      同DYNSYM
SHT_DYNSYM      这两类区都含有符号表。目前,目标文件中最多只能各包含一个这两种区,但这种限制以后可能会取消。
                一般来说,SYMTAB提供的符号用于在创建目标文件的时候编辑链接,在运行期间也有可能会用于动态链接。
                SYMTAB包含完整的符号表,它往往会包含很多在运行期间(动态链接)用不到的符号。所以,一个目标文件
                可以再有一个DYNSYM区,它含有一个较小的符号表,专门用于动态链接。

正如描述所言,.dynsym是.symtab的缩小版,在其中能看到亲切的printf。具体请参考以下四个维度来理解符号表。

小s-符号表绑定 | Symbol Table Bind(STB)


STB_LOCAL       表明本符号是一个本地符号。它只出现在本文件中,在本文件外该符号无效。
                所以在不同的文件中可以定义相同的符号名,它们之间不会互相影响。
STB_GLOBAL      表明本符号是一个全局符号。当有多个文件被链接在一起时,在所有文件中该符号都是可见的。
                正常情况下,在一个文件中定义的全局符号,一定是在其它文件中需要被引用,否则无须定义为全局。
STB_WEAK        类似于全局符号,但是相对于STB_GLOBAL,它们的优先级更低。
                全局符号(global symbol)和弱符号(weak symbol)在以下两方面有区别:
                • 当链接编辑器把若干个可重定位目标文件链接起来时,同名的STB_GLOBAL符号不允许出现多次。
                而如果在一个目标文件中已经定义了一个全局的符号(global symbol),当一个同名的弱符号(weak symbol)出现时,并不会发生错误。
                链接编辑器会以全局符号为准,忽略弱符号。与全局符号相似,
                如果已经存在的是一个公用符号,即st_shndx域为SHN_COMMON值的符号,当一个同名的弱符号(weak symbol)出现时,也不会发生错误。
                链接编辑器会以公用符号为准,忽略弱符号。
                • 在查找符号定义时,链接编辑器可能会搜索存档的库文件。如果是查找全局符号,
                链接编辑器会提取包含该未定义的全局符号的存档成员,存档成员可能是一个全局的符号,
                也可能是弱符号。而如果是查找弱符号,链接编辑器不会去提取存档成员。未解析的弱符号值为0。
STB_LOPROC ~ STB_HIPROC 为特殊处理器保留的属性区间。

小s-符号表类型 | Symbol Table Type(STT)


STT_NOTYPE      本符号类型未指定。
STT_OBJECT      本符号是一个数据对象,比如变量,数组等。
STT_FUNC        本符号是一个函数,或者其它的可执行代码。函数符号在共享目标文件中有特殊的意义。当另外一个目标文件引用一个共享目标文件中的函数符号时,链接编辑器为被引用符号自动创建一个链接表项。非STT_FUNC类型的共享目标符号不会通过这种链接表项被自动引用。
STT_SECTION     本符号与一个区相关联,用于重定位,通常具有STB_LOCAL属性。
STT_FILE        本符号是一个文件符号,它具有STB_LOCAL属性,它的区索引值是SHN_ABS。在符号表中如果存在本类符号的话,它会出现在所有STB_LOCAL类符号的前部。
STT_LOPROC ~ STT_HIPROC 这一区间的符号类型为特殊处理器保留。

小s-符号表可见性 | Symbol Table Visibility(STV)


STV_DEFAULT     当符号的可见性是STV_DEFAULT时,那么该符号的可见性由符号的绑定属性决定。
                这类情况下,(可执行文件和共享库中的)全局符号和弱符号默认是外部可访问的,
                本地符号默认外部是无法被访问的。但是,可见性是STV_DEFAULT的全局符号和弱符号是可被覆盖的。
                什么意思?举个最典型的例子,共享库中的可见性值为STV_DEFAULTD的全局符号和弱符号
                是可被可执行文件中的同名符号覆盖的。
STV_HIDDEN      当符号的可见性是STV_HIDDEN时,证明该符号是外部无法访问的。这个属性主要
                用来控制共享库对外接口的数量。需要注意的是,一个可见性为STV_HIDDEN的数据对象,
                如果能获取到该符号的地址,那么依然是可以访问或者修改该数据对象的。在可重定位文件中,
                如果一个符号的可见性是STV_HIDDEN的话,那么在链接生成可执行文件或者共享库的过程中,
                该符号要么被删除,要么绑定属性变成STB_LOCAL。
STV_PROTECTED   当符号的可见性是STV_PROTECTED时,它是外部可见的,这点跟可见性是STV_DEFAULT的一样,
                但不同的是它是不可覆盖的。这样的符号在共享库中比较常见。不可覆盖意味着如果是在该符号
                所在的共享库中访问这个符号,那么就一定是访问的这个符号,尽管可执行文件中也会存在
                同样名字的符号也不会被覆盖掉。规定绑定属性为STB_LOCAL的符号的可见性不可以是STV_PROTECTED。
STV_INTERNAL    该可见性属性的含义可以由处理器补充定义,以进一步约束隐藏的符号。 处理器补充程序的定义
                应使通用工具可以安全地将内部符号视为隐藏符号。当可重定位对象包含在可执行文件或共享对象中时,
                可重定位对象中包含的内部符号必须被链接编辑器删除或转换为STB_LOCAL绑定。

小s-符号表索引 | Symbol Table Ndx(STN)

任何一个符号表项的定义都与某一个”区”相联系,因为符号是为区而定义,在区中被引用。本数据成员即指明了相关联的区。本数据成员是一个索引值,它指向相关联的区在区头表中的索引。在重定位过程中,区的位置会改变,本数据成员的值也随之改变,继续指向区的新位置。当本数据成员指向下面三种特殊的区索引值时,本符号具有如下特别的意义:


SHN_ABS     符号的值是绝对的,具有常量性,在重定位过程中,此值不需要改变。
SHN_COMMON  本符号所关联的是一个还没有分配的公共区,本符号的值规定了其内容的字区对齐规则,
            与sh_addralign相似。也就是说,链接器会为本符号分配存储空间,而且其起始地址是
            向st_value对齐的。本符号的值指明了要分配的字区数。
SHN_UNDEF   当一个符号指向第1区(SHN_UNDEF)时,表明本符号在当前目标文件中未定义,在链接过程中,
            链接器会找到此符号被定义的文件,并把这些文件链接在一起。
            本文件中对该符号的引用会被链接到实际的定义上去。

百文说内核 | 抓住主脉络

子曰:“诗三百,一言以蔽之,曰‘思无邪’。”——《论语》:为政篇。百文相当于摸出内核的肌肉和器官系统,让人开始丰满有立体感,因是直接从注释源码起步,在开源鸿蒙内核源码加注释过程中,每每有心得处就整理,慢慢形成了以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。
百万汉字注解内核目的是要看清楚其毛细血管,细胞结构,等于在拿放大镜看内核。内核并不神秘,带着问题去源码中找答案是很容易上瘾的,你会发现很多文章对一些问题的解读是错误的,或者说不深刻难以自圆其说,你会慢慢形成自己新的解读,而新的解读又会碰到新的问题,如此层层递进,滚滚向前,拿着放大镜根本不愿意放手。与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。百篇博客系列思维导图结构如下:

根据上图的思维导图,我们未来将要和大家一一分享以上大部分关键技术点的博客文章。

百万汉字注解.精读内核源码

如果大家觉得看文章不过瘾,想直接撸代码的话,可以去下面四大码仓围观同步注释内核源码:

gitee仓

https://gitee.com/weharmony/kernel_liteos_a_note

github仓 :

https://github.com/kuangyufei/kernel_liteos_a_note

codechina仓

https://codechina.csdn.net/kuangyufei/kernel_liteos_a_note

coding仓

https://weharmony.coding.net/public/harmony/kernel_liteos_a_note/git/files

写在最后

我们最近正带着大家玩嗨OpenHarmony。如果你有用OpenHarmony开发的好玩的东东,或者有对OpenHarmony的深度技术剖析,想通过我们平台让更多的小伙伴知道和分享的,欢迎投稿,让我们一起嗨起来!有点子,有想法,有Demo,立刻联系我们:

合作邮箱:zzliang@atomsource.org