鸿蒙关键技术研究,鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 百篇博客分析HarmonyOS源码 | v54.02...
百篇博客系列篇.本篇為:
下圖是一個可執行文件編譯,鏈接的過程.
本篇將通過一個完整的小工程來闡述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
看到 .c .h .o 就感覺特別的親切 : ),項目很簡單,但具有代表性,有全局變量/函數,extern,多文件鏈接,和動態鏈接庫的printf,用cat命令看看三個文件內容.
cat .c .h
root@5e3abe332c5a:/home/docker/test4harmony/54# cat ./src/main.c
#include
#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
#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_str,printf,func_int,....,都是需要外部提供,并未在本文件中定義的符號 (Ndx = UND , Type = NOTYPE),至于怎么順藤摸瓜找到這些符號那我不管,.o文件是獨立存在,它只是告訴你我用了哪些東西,但我也不知道在哪里.
printf和func_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_int,func_str (Type=FUNC),并且都是全局函數,(Bind = GLOBAL , Vis = DEFAULT,全局的意思就是可以被外部文件所引用.
文件中有兩個對象叫 g_int,g_str (Type=OBJECT),并且都是全局對象,同樣可以被外部使用.
剩下的printf,_GLOBAL_OFFSET_TABLE_,都是需要外部提供,并未在本文件中定義的符號 (Ndx = UND , Type = NOTYPE)
另外 part.c的局部變量tmp并沒有出現在符號表中.因為符號表相當于外交部,只有對外的內容.
func_int,func_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.o,part.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環境中一般是跑不起來的.除非提供對應的運行時環境.
百篇博客.往期回顧
在加注過程中,整理出以下文章.內容立足源碼,常以生活場景打比方盡可能多的將內核知識點置入某種場景,具有畫面感,容易理解記憶.說別人能聽得懂的話很重要! 百篇博客絕不是百度教條式的在說一堆詰屈聱牙的概念,那沒什么意思.更希望讓內核變得栩栩如生,倍感親切.確實有難度,自不量力,但已經出發,回頭已是不可能的了.:P 與代碼有bug需不斷debug一樣,文章和注解內容會存在不少錯漏之處,但會反復修正,持續更新,.xx代表修改的次數,精雕細琢,言簡意賅,力求打造精品內容.
關于 51 .c .h .o
看系列篇文章會常看到 51 .c .h .o,希望這對大家閱讀不會造成影響. 分別對應以下四個站點的首個字符,感謝這些站點一直以來對系列篇的支持和推薦,尤其是 oschina gitee ,很喜歡它的界面風格,簡潔大方,讓人感覺到開源的偉大!
而巧合的是.c .h .o是C語言的頭/源/目標文件,這就很有意思了,冥冥之中似有天數,將這四個寶貝以這種方式融合在一起. 51 .c .h .o , 我要CHO ,嗯嗯,hin 順口 : )
百萬漢字注解.百篇博客分析
關注不迷路.代碼即人生
原創不易,歡迎轉載,但麻煩請注明出處.
總結
以上是生活随笔為你收集整理的鸿蒙关键技术研究,鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 百篇博客分析HarmonyOS源码 | v54.02...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云漫圈 | 容器技术docker应用场景
- 下一篇: 中国制式武器92式手枪性能怎么样