v54.04 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 百篇博客分析HarmonyOS源码

原创
2021/05/15 10:08
阅读数 1.8W

子曰:“回也其庶乎,屡空。赐不受命,而货殖焉,亿则屡中。” 《论语》:先进篇

在这里插入图片描述

百篇博客系列篇.本篇为:

v54.xx 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程

下图是一个可执行文件编译,链接的过程.

kernel_liteos_a_note

本篇将通过一个完整的小工程来阐述ELF编译,链接过程,并分析.o和bin文件中各区,符号表之间的关系.从一个崭新的视角去看中间过程.

加载运行相关篇为:

准备工作

先得有个小工程,麻雀虽小,但五脏俱全,标准的文件夹和Makefile结构,如下:

目录结构

root@5e3abe332c5a:/home/docker/test4harmony/54# tree
.
├── bin
│   └── weharmony
├── include
│   └── part.h
├── Makefile
├── obj
│   ├── main.o
│   └── part.o
└── src
    ├── main.c
    └── part.c  

4 directories, 7 files         

看到 .c .h .o 就感觉特别的亲切 : ),项目很简单,但具有代表性,有全局变量/函数,extern,多文件链接,和动态链接库的printf,用cat命令看看三个文件内容。

cat .c .h

root@5e3abe332c5a:/home/docker/test4harmony/54# cat ./src/main.c 
#include <stdio.h>
#include "part.h"
extern int g_int;
extern char *g_str;

int main() {
        int loc_int = 53;
        char *loc_str = "harmony os";
        printf("main 开始 - 全局 g_int = %d, 全局 g_str = %s.\n", g_int, g_str);
        func_int(loc_int);
        func_str(loc_str);
        printf("main 结束 - 全局 g_int = %d, 全局 g_str = %s.\n", g_int, g_str);
        return 0;
}
root@5e3abe332c5a:/home/docker/test4harmony/54# cat ./src/part.c 
#include <stdio.h>
#include "part.h"

int g_int = 51;
char *g_str = "hello world";

void func_int(int i) {
	int tmp = i;
	g_int = 2 * tmp ;
	printf("func_int g_int = %d,tmp = %d.\n", g_int,tmp);
}
void func_str(char *str) {
        g_str = str;
        printf("func_str g_str = %s.\n", g_str);
}
root@5e3abe332c5a:/home/docker/test4harmony/54# cat ./include/part.h 
#ifndef _PART_H_
#define _PART_H_
void func_int(int i);
void func_str(char *str);
#endif

cat Makefile

Makefile采用标准写法,关于makefile系列篇会在编译过程篇中详细说明,此处先看点简单的。

root@5e3abe332c5a:/home/docker/test4harmony/54# cat Makefile 
DIR_INC = ./include
DIR_SRC = ./src
DIR_OBJ = ./obj
DIR_BIN = ./bin

SRC = $(wildcard ${DIR_SRC}/*.c)
OBJ = $(patsubst %.c,${DIR_OBJ}/%.o,$(notdir ${SRC}))

TARGET = weharmony

BIN_TARGET = ${DIR_BIN}/${TARGET}

CC = gcc
CFLAGS = -g -Wall -I${DIR_INC}

${BIN_TARGET}:${OBJ}
        $(CC) $(OBJ)  -o $@

${DIR_OBJ}/%.o:${DIR_SRC}/%.c
        $(CC) $(CFLAGS) -c  $< -o $@
.PHONY:clean
clean:
        find ${DIR_OBJ} -name *.o -exec rm -rf {}

编译.链接.运行.看结果

root@5e3abe332c5a:/home/docker/test4harmony/54# make
gcc -g -Wall -I./include -c  src/part.c -o obj/part.o
gcc -g -Wall -I./include -c  src/main.c -o obj/main.o
gcc ./obj/part.o ./obj/main.o  -o bin/weharmony
root@5e3abe332c5a:/home/docker/test4harmony/54# ./bin/weharmony 
main 开始 - 全局 g_int = 51, 全局 g_str = hello world.
func_int g_int = 106,tmp = 53.
func_str g_str = harmony os.
main 结束 - 全局 g_int = 106, 全局 g_str = harmony os.

结果很简单,没什么好说的.

开始分析

准备工作完成,开始了真正的分析. 因为命令输出内容太多,本篇做了精简,去除了干扰项.对这些命令还不行清楚的请翻看系列篇其他文章,此处不做介绍,阅读本篇需要一定的基础.

readelf 大S小s ./obj/main.o

root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -S ./obj/main.o
There are 22 section headers, starting at offset 0x1498:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       000000000000007b  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000c80
       0000000000000108  0000000000000018   I      19     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000bb
       0000000000000000  0000000000000000  WA       0     0     1
  [ 4] .bss              NOBITS           0000000000000000  000000bb
       0000000000000000  0000000000000000  WA       0     0     1
  [ 5] .rodata           PROGBITS         0000000000000000  000000c0
       000000000000007d  0000000000000000   A       0     0     8
       ......
root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -s ./obj/main.o

Symbol table '.symtab' contains 22 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name  
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND       
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
        ...      
    15: 0000000000000000   123 FUNC    GLOBAL DEFAULT    1 main  
    16: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND g_str 
    17: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND g_int 
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    19: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    20: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND func_int
    21: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND func_str

解读

编译 main.c 后 main.o 告诉了链接器以下信息

  • 有一个文件 叫 main.c (Type=FILE)
  • 文件中有个函数叫 main (Type=FUNC),并且这是一个全局函数,(Bind = GLOBAL , Vis = DEFAULT,全局的意思就是可以被外部文件所引用.
  • 剩下的g_strprintffunc_int,....,都是需要外部提供,并未在本文件中定义的符号 (Ndx = UND , Type = NOTYPE),至于怎么顺藤摸瓜找到这些符号那我不管,.o文件是独立存在,它只是告诉你我用了哪些东西,但我也不知道在哪里.
  • printffunc_int对它来说一视同仁,都是外部链接符号,没有特殊对待.

readelf 大S小s ./obj/part.o

root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -S ./obj/part.o
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000078  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000cf0
       00000000000000c0  0000000000000018   I      21     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000b8
       0000000000000004  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  000000bc
       0000000000000000  0000000000000000  WA       0     0     1
  [ 5] .rodata           PROGBITS         0000000000000000  000000c0
       0000000000000045  0000000000000000   A       0     0     8
  [ 6] .data.rel.local   PROGBITS         0000000000000000  00000108
       0000000000000008  0000000000000000  WA       0     0     8
       ......
root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -s ./obj/part.o

Symbol table '.symtab' contains 22 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS part.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
        ...
    16: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 g_int
    17: 0000000000000000     8 OBJECT  GLOBAL DEFAULT    6 g_str
    18: 0000000000000000    52 FUNC    GLOBAL DEFAULT    1 func_int
    19: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    20: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    21: 0000000000000034    57 FUNC    GLOBAL DEFAULT    1 func_str

解读

编译 part.c 后part.o告诉了链接器以下信息

  • 有一个文件 叫 part.c (Type=FILE)
  • 文件中有两个函数叫 func_intfunc_str (Type=FUNC),并且都是全局函数,(Bind = GLOBAL , Vis = DEFAULT,全局的意思就是可以被外部文件所引用.
  • 文件中有两个对象叫 g_intg_str (Type=OBJECT),并且都是全局对象,同样可以被外部使用.
  • 剩下的printf_GLOBAL_OFFSET_TABLE_,都是需要外部提供,并未在本文件中定义的符号 (Ndx = UND , Type = NOTYPE)
  • 另外 part.c的局部变量tmp并没有出现在符号表中.因为符号表相当于外交部,只有对外的内容.
  • func_intfunc_str在1区代码区.text.
  • g_int 在3区.data数据区, 打开3区,发现了 0x33 就是源码中 int g_int = 51;的值
      root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -x 3 ./obj/part.o
      Hex dump of section '.data':
      0x00000000 33000000                            3...
    
  • g_str 在6区,.data.rel.local数据区,打开6区看结果
      root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -x 6 ./obj/part.o
      Hex dump of section '.data.rel.local':
      NOTE: This section has relocations against it, but these have NOT been applied to this dump.
      0x00000000 00000000 00000000                   ........  
    
    并未发现 char *g_str = "hello world";的身影,反而抛下一句话 NOTE: This section has relocations against it, but these have NOT been applied to this dump.翻译过来是 注意:此部分已针对它进行重定位,但是尚未将其应用于此转储. 最后在5区 '.rodata'找到了 hello world
    root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -x 5 ./obj/part.o
      Hex dump of section '.rodata':
      0x00000000 68656c6c 6f20776f 726c6400 00000000 hello world.....
      0x00000010 66756e63 5f696e74 20675f69 6e74203d func_int g_int =
      0x00000020 2025642c 746d7020 3d202564 2e0a0066  %d,tmp = %d...f
      0x00000030 756e635f 73747220 675f7374 72203d20 unc_str g_str =
      0x00000040 25732e0a 00                         %s..
    
    至于重定向是如何实现的,在系列篇 重定向篇中已有详细说明,不再此展开说.
  • 看完两个符号表总结下来就是三句话
    • 我是谁,我在哪
    • 我能提供什么给别人用
    • 我需要别人提供什么给我用.

readelf 大S小s ./bin/weharmony

weharmony是将 main.opart.o和库文件链接完成后的可执行文件.

root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -S ./bin/weharmony
There are 36 section headers, starting at offset 0x4908:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
       ......
  [16] .text             PROGBITS         0000000000001060  00001060
       0000000000000255  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         00000000000012b8  000012b8
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       00000000000000cd  0000000000000000   A       0     0     8       
       ......
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000020  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004020  00003020
       0000000000000008  0000000000000000  WA       0     0     1       
root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -s ./bin/weharmony 

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 75 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
     ....
    33: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    34: 0000000000001090     0 FUNC    LOCAL  DEFAULT   16 deregister_tm_clones
    35: 00000000000010c0     0 FUNC    LOCAL  DEFAULT   16 register_tm_clones
    36: 0000000000001100     0 FUNC    LOCAL  DEFAULT   16 __do_global_dtors_aux
    37: 0000000000004020     1 OBJECT  LOCAL  DEFAULT   26 completed.8060
    38: 0000000000003dc0     0 OBJECT  LOCAL  DEFAULT   22 __do_global_dtors_aux_fin
    39: 0000000000001140     0 FUNC    LOCAL  DEFAULT   16 frame_dummy
    40: 0000000000003db8     0 OBJECT  LOCAL  DEFAULT   21 __frame_dummy_init_array_
    41: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS part.c
    42: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    43: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    44: 000000000000225c     0 OBJECT  LOCAL  DEFAULT   20 __FRAME_END__
    45: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
    46: 0000000000003dc0     0 NOTYPE  LOCAL  DEFAULT   21 __init_array_end
    47: 0000000000003dc8     0 OBJECT  LOCAL  DEFAULT   23 _DYNAMIC
    48: 0000000000003db8     0 NOTYPE  LOCAL  DEFAULT   21 __init_array_start
    49: 00000000000020c0     0 NOTYPE  LOCAL  DEFAULT   19 __GNU_EH_FRAME_HDR
    50: 0000000000003fb8     0 OBJECT  LOCAL  DEFAULT   24 _GLOBAL_OFFSET_TABLE_
    51: 0000000000001000     0 FUNC    LOCAL  DEFAULT   12 _init
    52: 00000000000012b0     5 FUNC    GLOBAL DEFAULT   16 __libc_csu_fini
    53: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    54: 0000000000004000     0 NOTYPE  WEAK   DEFAULT   25 data_start
    55: 0000000000004020     0 NOTYPE  GLOBAL DEFAULT   25 _edata
    56: 00000000000012b8     0 FUNC    GLOBAL HIDDEN    17 _fini
    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    58: 0000000000004010     4 OBJECT  GLOBAL DEFAULT   25 g_int
    59: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    60: 0000000000004000     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
    61: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    62: 0000000000004008     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle
    63: 0000000000004018     8 OBJECT  GLOBAL DEFAULT   25 g_str
    64: 0000000000002000     4 OBJECT  GLOBAL DEFAULT   18 _IO_stdin_used
    65: 0000000000001240   101 FUNC    GLOBAL DEFAULT   16 __libc_csu_init
    66: 0000000000001149    52 FUNC    GLOBAL DEFAULT   16 func_int
    67: 0000000000004028     0 NOTYPE  GLOBAL DEFAULT   26 _end
    68: 0000000000001060    47 FUNC    GLOBAL DEFAULT   16 _start
    69: 000000000000117d    57 FUNC    GLOBAL DEFAULT   16 func_str
    70: 0000000000004020     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
    71: 00000000000011b6   123 FUNC    GLOBAL DEFAULT   16 main
    72: 0000000000004020     0 OBJECT  GLOBAL HIDDEN    25 __TMC_END__
    73: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    74: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.2

解读

链接后的可执行文件 weharmony将告诉加载器以下信息

  • 涉及文件有哪些 Type = FILE
  • 涉及函数有哪些 Type = FUNC func_str,func_int,_start,main
  • 涉及对象有哪些 Type = OBJECT g_int,g_str,....它将这些数据统一归到了25区. 前往25区查看下数据,同样只发现了 int g_int = 51; 的数据.
    root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -x 25 ./bin/weharmony 
      Hex dump of section '.data':
      0x00004000 00000000 00000000 08400000 00000000 .........@......
      0x00004010 33000000 00000000 08200000 00000000 3........ ......
    
    是不是和part.o一样也被放在了.rodata区,再反查 18区,果然发了 main.c和part.c的数据都放在了这里.
    root@5e3abe332c5a:/home/docker/test4harmony/54# readelf -x 18 ./bin/weharmony
      Hex dump of section '.rodata':
      0x00002000 01000200 00000000 68656c6c 6f20776f ........hello wo
      0x00002010 726c6400 00000000 66756e63 5f696e74 rld.....func_int
      0x00002020 20675f69 6e74203d 2025642c 746d7020  g_int = %d,tmp 
      0x00002030 3d202564 2e0a0066 756e635f 73747220 = %d...func_str 
      0x00002040 675f7374 72203d20 25732e0a 00000000 g_str = %s......
      0x00002050 6861726d 6f6e7920 6f730000 00000000 harmony os......
      0x00002060 6d61696e 20e5bc80 e5a78b20 2d20e585 main ...... - ..
      0x00002070 a8e5b180 20675f69 6e74203d 2025642c .... g_int = %d,
      0x00002080 20e585a8 e5b18020 675f7374 72203d20  ...... g_str = 
      0x00002090 25732e0a 00000000 6d61696e 20e7bb93 %s......main ...
      0x000020a0 e69d9f20 2d20e585 a8e5b180 20675f69 ... - ...... g_i
      0x000020b0 6e74203d 2025642c 20e585a8 e5b18020 nt = %d, ...... 
      0x000020c0 675f7374 72203d20 25732e0a 00       g_str = %s...   
    
  • 另外还有注意printf的变化,从Type = NOTYPE 变成了Type = FUNC,告诉了后续的动态链接这是个函数
        57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    
    但是内容依然是Ndx=UND,weharmony也提供不了,内容需要运行时环境提供.并在需要动态链接表中也已经注明了内容清单,运行环境必须提供以下内容才能真正跑起来weharmony.
    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)  
    
    本例在windows环境中一般是跑不起来的.除非提供对应的运行时环境.

百篇博客分析.深挖内核地基

  • 给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
  • 与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。

按功能模块:

基础工具 加载运行 进程管理 编译构建
双向链表 位图管理 用栈方式 定时器 原子操作 时间管理 ELF格式 ELF解析 静态链接 重定位 进程映像 进程管理 进程概念 Fork 特殊进程 进程回收 信号生产 信号消费 Shell编辑 Shell解析 编译环境 编译过程 环境脚本 构建工具 gn应用 忍者ninja
进程通讯 内存管理 前因后果 任务管理
自旋锁 互斥锁 进程通讯 信号量 事件控制 消息队列 内存分配 内存管理 内存汇编 内存映射 内存规则 物理内存 总目录 调度故事 内存主奴 源码注释 源码结构 静态站点 时钟任务 任务调度 任务管理 调度队列 调度机制 线程概念 并发并行 CPU 系统调用 任务切换
文件系统 硬件架构
文件概念 文件系统 索引节点 挂载目录 根文件系统 字符设备 VFS 文件句柄 管道文件 汇编基础 汇编传参 工作模式 寄存器 异常接管 汇编汇总 中断切换 中断概念 中断管理

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

WeHarmony/kernel_liteos_a_note

鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
2 收藏
2
分享
返回顶部
顶部