今天的內容會複習一下 symbols 是什麼, 以及 linker 是如何把一堆 object files 合併成一個大 ELF 的, 並以 C++ 為例, 用 CMake build 出 libraries, executable, object files 來細看每種檔案裡面包含了哪些東西
那麼這跟 CMake 有什麼關係呢?
希望大家看了今天的內容後, 對 Day 18 將要介紹的 Link Dependencies 能有更多了解, 在日後遇到問題時, 不管是去看 raw linker command 或是 objdump
去看 op 等等, 都能看懂並自己 debug
如果對 C/C++ 已經很熟悉的人可以跳過今天的內容, 可能太簡單了😅
Day 11 在介紹 static libraries 和 shared libraries 時有簡單提過, symbols 包含了各種 變數
但嚴格來說, 以一個 source file 為例, symbols 包含了該檔案中除 static variable 以外宣告的 所有 變數, 由於 static variables 是 local scope, 而 symbols 最主要的目的是讓 linker 能夠找到 reference symbol 需要的變數的真正位置, 才能在最後產生 ELF 時順利 relocate 並塞到字段, 所以僅需要 global scope 的變數
當專案架構越來越複雜, 更多的 libraries 被建立或是引進了更多外部套件, Dependency Tree 的關係也會變得複雜, 就容易產生 build error
所以, 下面會簡單介紹一些方便看 binary 的工具
readelf
我們可以用 readelf
來檢查 binary (通常是 ELF), 可以看到 relocation table, symbols 等等, 關於什麼是 ELF 請見 elf(5)
我們可以用以下指令看看最後可執行檔的 section headers 有哪些
readelf -SW build/src/Main | less
There are 32 section headers, starting at offset 0x3c70:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000000318 000318 00001c 00 A 0 0 1
[ 2] .note.gnu.property NOTE 0000000000000338 000338 000030 00 A 0 0 8
[ 3] .note.gnu.build-id NOTE 0000000000000368 000368 000024 00 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000038c 00038c 000020 00 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003b0 0003b0 000030 00 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003e0 0003e0 0001f8 18 A 7 1 8
[ 7] .dynstr STRTAB 00000000000005d8 0005d8 0002dc 00 A 0 0 1
[ 8] .gnu.version VERSYM 00000000000008b4 0008b4 00002a 02 A 6 0 2
[ 9] .gnu.version_r VERNEED 00000000000008e0 0008e0 0000a0 00 A 7 3 8
[10] .rela.dyn RELA 0000000000000980 000980 000168 18 A 6 0 8
[11] .rela.plt RELA 0000000000000ae8 000ae8 0000f0 18 AI 6 25 8
[12] .init PROGBITS 0000000000001000 001000 00001b 00 AX 0 0 4
[13] .plt PROGBITS 0000000000001020 001020 0000b0 10 AX 0 0 16
[14] .plt.got PROGBITS 00000000000010d0 0010d0 000020 10 AX 0 0 16
[15] .plt.sec PROGBITS 00000000000010f0 0010f0 0000a0 10 AX 0 0 16
[16] .text PROGBITS 0000000000001190 001190 000302 00 AX 0 0 16
[17] .fini PROGBITS 0000000000001494 001494 00000d 00 AX 0 0 4
[18] .rodata PROGBITS 0000000000002000 002000 000013 00 A 0 0 4
[19] .eh_frame_hdr PROGBITS 0000000000002014 002014 00005c 00 A 0 0 4
[20] .eh_frame PROGBITS 0000000000002070 002070 00017c 00 A 0 0 8
[21] .gcc_except_table PROGBITS 00000000000021ec 0021ec 00001f 00 A 0 0 1
[22] .init_array INIT_ARRAY 0000000000003d28 002d28 000018 08 WA 0 0 8
[23] .fini_array FINI_ARRAY 0000000000003d40 002d40 000008 08 WA 0 0 8
[24] .dynamic DYNAMIC 0000000000003d48 002d48 000210 10 WA 7 0 8
[25] .got PROGBITS 0000000000003f58 002f58 0000a8 08 WA 0 0 8
[26] .data PROGBITS 0000000000004000 003000 000018 00 WA 0 0 8
[27] .bss NOBITS 0000000000004040 003018 000140 00 WA 0 0 64
[28] .comment PROGBITS 0000000000000000 003018 00002b 01 MS 0 0 1
[29] .symtab SYMTAB 0000000000000000 003048 0005b8 18 30 27 8
[30] .strtab STRTAB 0000000000000000 003600 000542 00 0 0 1
[31] .shstrtab STRTAB 0000000000000000 003b42 00012c 00 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),
D (mbind), l (large), p (processor specific)
objdump
也可以用 objdump
來查看 object files 的 op, 比如
!objdump -d build/src/CMakeFiles/Main.dir/main.cpp.o
build/src/CMakeFiles/Main.dir/main.cpp.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 53 push %rbx
9: 48 83 ec 38 sub $0x38,%rsp
d: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
...
有了這些概念之後, 下一篇會來介紹一些 CMake 在建立 Link Dependency Tree 時的注意事項