Linux ELF 详解3 -- Symbol Table Symbol
上一篇:ELF 詳解2 – Section Header & Section
ELF Symbol Table
Symbol Table 包含了一組 Symbol。這些 Symbol 在程序中,要么表示定義,要么表示引用,它們的作用是在編譯和鏈接的過程中,進行定位或者重定位(后面會講到)。
先查看它在 Section Header 列表中的信息
$ readelf -SW program.o ... Section Headers:[Nr] Name Type Address Off Size ES Flg Lk Inf Al ...[12] .symtab SYMTAB 0000000000000000 000130 000180 18 13 10 8 ...由上可知 Symbol Table Section:
ELF Symbol
看一下 Symbol 的定義
typedef struct {Elf32_Word st_name;Elf32_Addr st_value;Elf32_Word st_size;unsigned char st_info;unsigned char st_other;Elf32_Half st_shndx; } Elf32_Sym;typedef struct {Elf64_Word st_name; // 4 B (B for bytes)unsigned char st_info; // 1 Bunsigned char st_other; // 1 BElf64_Half st_shndx; // 2 BElf64_Addr st_value; // 8 BElf64_Xword st_size; // 8 B } Elf64_Sym; // total size = 24 B我們只關注 Elf64_Sym (64位系統的定義)。
可以看到 Elf64_Sym 的大小為24字節。
用 readelf 查看一下 Symbol Table
# 注意這里是小寫的 s,代表 Symbols,大寫的 S,代表 Sections/Section Headers $ readelf -sW program.oSymbol table '.symtab' contains 16 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS program.c2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 4: 0000000000000000 0 SECTION LOCAL DEFAULT 5 5: 0000000000000000 1 OBJECT LOCAL DEFAULT 3 d6: 0000000000000000 0 SECTION LOCAL DEFAULT 6 7: 0000000000000000 0 SECTION LOCAL DEFAULT 8 8: 0000000000000000 0 SECTION LOCAL DEFAULT 9 9: 0000000000000000 0 SECTION LOCAL DEFAULT 7 10: 0000000000000008 10 OBJECT GLOBAL DEFAULT COM c11: 0000000000000008 8 OBJECT GLOBAL DEFAULT 3 f12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND function13: 0000000000000000 81 FUNC GLOBAL DEFAULT 1 main14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND a15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf這里包含了 program.o 中出現的所有 Symbol。其中 Num=0(index=0) 的 Symbol 用作 undefined Symbol,它的 index 有個特別的名字,叫做 STN_UNDEF。
接下來,我們將選取 Num=5,也就是 Name=d 的 Symbol,從字節級別解讀 Symbol 的定義。
上面已經知道了 Symbol Table 在對象文件中的 offset=304,每個 Symbol 的 size=24,那么 Num=5 的 Symbol(Num 從0開始計算,Num=5表示前面有5個 Symbol),它的 offset = 304 + 24 * 5 = 424。
$ hexdump -C -s424 -n24 program.o 000001a8 0b 00 00 00 01 00 03 00 00 00 00 00 00 00 00 00 |................| 000001b8 01 00 00 00 00 00 00 00 |........| 000001c0st_name
Symbol 名字的索引,類似于 Section Header 的 sh_name,只不過這個索引使用的 String table 跟 Section Header sh_name 使用的 String table 不是同一個。后面會講到。
數據類型:Elf64_Word (4 bytes)
$ hexdump -C -s424 -n4 program.o 000001a8 0b 00 00 00 |....| 000001ac值為:“0b 00 00 00” + little endian = 0xb = 11
st_info
這是個組合字段。它的高4位表示 Symbol Binding,低4位表示 Symbol Type。
數據類型:unsigned char (1 byte)
$ hexdump -C -s428 -n1 program.o 000001ac 01 |.| 000001ad值為:1,二進制表示為 “0000 0001”
Symbol Binding: 1 >> 4 = 0
Symbol Type: 1 & 0xf = 1
Symbol Binding 值的含義:(只需要關注紅框部分)
STB_LOCAL: 對象文件外部不可見。
STB_GLOBAL:所有合并到一起的對象文件都可見。對于同一個 Global Symbol,它的定義可以滿足其他地方對它的引用。
STB_WEAK:類似于 STB_GLOBAL,但匹配時優先級較低。
(后面會詳細講到)
Symbol Type 值的含義:(只需要關注紅框部分)
STT_NOTYPE:類型未指定,也可以認為這個類型的 Symbol,在當前的對象文件中沒找到其定義,需要外部其他對象文件來提供它的定義。
STT_OBJECT:關聯到一個數據對象,比如數組,變量等。
STT_FUNC:關聯到一個函數或者其他可執行的代碼。
STT_SECTION:關聯到可以重定位的 Section。
STT_FILE:給出了這個對象文件的源文件名,譬如 program.o 的源文件就是 program.c。它的 Section index 為 SHN_ABS。(后面會講到)
STT_COMMON:標識了未初始化的公共塊。后面會詳細講到。
STT_TLS:指定了線程本地存儲的實體。(本章不討論多線程,所以先跳過)
st_other
指定了 Symbol 的可見性。當前只有最低2位被使用。
數據類型:unsigned char (1 byte)
$ hexdump -C -s429 -n1 program.o 000001ad 00 |.| 000001ae值為:0
可見性:0 & 0x3 = 0
通常情況下,可見性都為 STV_DEFAULT,也就是使用默認規則(后面會講到)。
其他可見性先跳過。
st_shndx
不同的上下文有不同的含義:
數據類型:Elf64_Half (2 bytes)
$ hexdump -C -s430 -n2 program.o 000001ae 03 00 |..| 000001b0值為:“03 00” + little endian = 0x3 = 3,即關聯到的 Section Header index 為3。
st_value
根據不同的上下文,有不同的定義
數據類型:Elf64_Addr (8 bytes)
$ hexdump -C -s432 -n8 program.o 000001b0 00 00 00 00 00 00 00 00 |........| 000001b8值為:0
st_size
表示 Symbol 的大小,例如數據對象占據多少字節。如果 Symbol 沒有大小或者大小未知,則值為0。
數據類型:Elf64_Xword (8 bytes)
$ hexdump -C -s440 -n8 program.o 000001b8 01 00 00 00 00 00 00 00 |........| 000001c0值為:“01 00 00 00 00 00 00 00” + little endian = 0x1,即占據1個字節。
可以把各字段的信息對照 Symbol Table 中 Num=5 的輸出,從而加深理解。
“.symtab” 的 sh_link & sh_info
首先回顧一下 “.symtab” 的 Section Header 輸出:
$ readelf -SW program.o ... Section Headers:[Nr] Name Type Address Off Size ES Flg Lk Inf Al ...[12] .symtab SYMTAB 0000000000000000 000130 000180 18 13 10 8[13] .strtab STRTAB 0000000000000000 0002b0 000024 00 0 0 1 ...由上可知,".symtab" 的 sh_link = 13,sh_info = 10。
前面 Section Header 的章節已經提到過,sh_link 和 sh_info 在不同的上下文有不同的含義。
對于 “.symtab”,
sh_link:表示關聯到的 String table 的 Section Header index。在這里,這個 String table 就是 “.strtab”。Symbol 的 st_name 就是對應到 “.strtab” 的 index。
sh_info:1 + 最后一個 local Symbol (Binding = STB_LOCAL) 在 Symbol Table 中的 index,也就是說從這個 index 開始的 Symbol 不再是 local。
查看一下 “.strtab” 的內容
$ hexdump -C -s0x2b0 -n36 program.o 000002b0 00 70 72 6f 67 72 61 6d 2e 63 00 64 00 66 75 6e |.program.c.d.fun| 000002c0 63 74 69 6f 6e 00 6d 61 69 6e 00 61 00 70 72 69 |ction.main.a.pri| 000002d0 6e 74 66 00 |ntf.| 000002d4從上面查看過的字節信息可知,Symbol d 的 st_name = 11,在這里對應的就是 “d”。
以下是打印所有 Symbol 名字的程序,類似于前面打印所有 Section Header 名字的程序(為了方便理解,其實就是換了變量名和對應的數值)。
// print_symbol_names.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h>int main() {off_t symbolTableOffset = 0x130;size_t symbolTableSize = 0x180;size_t symbolSize = 0x18;off_t stringTableOffset = 0x2b0;size_t stringTableSize = 0x24;size_t nameSize = 4;int symbolNum = symbolTableSize / symbolSize;char content[stringTableSize];union {char b[4];int off;} symbolName;int fd = open("program.o", O_RDONLY);if (fd == -1)exit(EXIT_FAILURE);if (lseek(fd, stringTableOffset, SEEK_SET) == -1)exit(EXIT_FAILURE);if (read(fd, content, stringTableSize) != stringTableSize)exit(EXIT_FAILURE);int i;int currSymbolOffset = symbolTableOffset;char *start;for (i = 0; i < symbolNum; ++i) {if (lseek(fd, currSymbolOffset, SEEK_SET) == -1)exit(EXIT_FAILURE);if (read(fd, symbolName.b, 4) != 4)exit(EXIT_FAILURE);start = content + symbolName.off;printf("[%2d]: off: 0x%02x, name: %s\n", i, symbolName.off, start);currSymbolOffset += symbolSize;}if (close(fd) == -1)exit(EXIT_FAILURE);return 0; }輸出的結果和 “readelf -sW program.o” 是一致的。
$ gcc -o print_symbol_names print_symbol_names.c $ ./print_symbol_names [ 0]: off: 0x00, name: [ 1]: off: 0x01, name: program.c [ 2]: off: 0x00, name: [ 3]: off: 0x00, name: [ 4]: off: 0x00, name: [ 5]: off: 0x0b, name: d [ 6]: off: 0x00, name: [ 7]: off: 0x00, name: [ 8]: off: 0x00, name: [ 9]: off: 0x00, name: [10]: off: 0x09, name: c [11]: off: 0x22, name: f [12]: off: 0x0d, name: function [13]: off: 0x16, name: main [14]: off: 0x1b, name: a [15]: off: 0x1d, name: printf再來看 sh_info 的含義。
$ readelf -sW program.oSymbol table '.symtab' contains 16 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS program.c2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 4: 0000000000000000 0 SECTION LOCAL DEFAULT 5 5: 0000000000000000 1 OBJECT LOCAL DEFAULT 3 d6: 0000000000000000 0 SECTION LOCAL DEFAULT 6 7: 0000000000000000 0 SECTION LOCAL DEFAULT 8 8: 0000000000000000 0 SECTION LOCAL DEFAULT 9 9: 0000000000000000 0 SECTION LOCAL DEFAULT 7 # --------------------------------------------------------------------10: 0000000000000008 10 OBJECT GLOBAL DEFAULT COM c11: 0000000000000008 8 OBJECT GLOBAL DEFAULT 3 f12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND function13: 0000000000000000 81 FUNC GLOBAL DEFAULT 1 main14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND a15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf由上可知,Num = 10 的 Symbol 是 c,在它之前的 Symbol,Binding 都是 LOCAL,從它開始往后的都是 GLOBAL。
接下來將查看 program.c 中出現的各個 Symbol 在對象文件中是如何定義的。
Symbol 示例
Num=5, Name=d
// program.c ... static char d = 'd'; ... $ readelf -sW program.o ...Num: Value Size Type Bind Vis Ndx Name ...5: 0000000000000000 1 OBJECT LOCAL DEFAULT 3 d ...# 查看關聯到的 Section Header (Ndx=3) $ readelf -SW program.o ... Section Headers:[Nr] Name Type Address Off Size ES Flg Lk Inf Al ...[ 3] .data PROGBITS 0000000000000000 000098 000010 00 WA 0 0 8 ...# 打印 ".data" 的內容 $ hexdump -C -s0x98 -n16 program.o 00000098 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |d...............| 000000a8對于 Symbol d:
Num=10,Name=c
// program.c ... char c[10]; ... $ readelf -sW program.o ...Num: Value Size Type Bind Vis Ndx Name ...10: 0000000000000008 10 OBJECT GLOBAL DEFAULT COM c ...對于 Symbol c:
Num=11,Name=f
// library.h int function(int);// program.c #include <stdio.h> #include "library.h" ... char* f = (char*) function; ... $ readelf -sW program.o ...Num: Value Size Type Bind Vis Ndx Name ...11: 0000000000000008 8 OBJECT GLOBAL DEFAULT 3 f ...# 打印 ".data" 的內容 $ hexdump -C -s0x98 -n16 program.o 00000098 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |d...............| 000000a8對于 Symbol f:
Undefined Symbol
Num=12, Name=function
Num=14, Name=a (聲明為 extern)
Num=15, Name=printf
它們都是外部引用(對外部 Symbol 的引用)
對于這幾個 Symbol:
Ndx = SHN_UNDEF 的 Symbol 都需要在鏈接期間進行 Symbol 解析。
Num=13, name=main
... int main() {int d = function(100) + a;printf("a: %d\n", a);printf("result: %d\n", d); } $ readelf -sW program.o ...Num: Value Size Type Bind Vis Ndx Name ...13: 0000000000000000 81 FUNC GLOBAL DEFAULT 1 main ...# 查看關聯到的 Section Header(Ndx=1) $ readelf -SW program.o ... Section Headers:[Nr] Name Type Address Off Size ES Flg Lk Inf Al ...[ 1] .text PROGBITS 0000000000000000 000040 000051 00 AX 0 0 1 ...# 查看 ".text" 的內容 $ hexdump -C -s0x40 -n81 program.o 00000040 55 48 89 e5 48 83 ec 10 bf 64 00 00 00 e8 00 00 |UH..H....d......| 00000050 00 00 89 c2 8b 05 00 00 00 00 01 d0 89 45 fc 8b |.............E..| 00000060 05 00 00 00 00 89 c6 bf 00 00 00 00 b8 00 00 00 |................| 00000070 00 e8 00 00 00 00 8b 45 fc 89 c6 bf 00 00 00 00 |.......E........| 00000080 b8 00 00 00 00 e8 00 00 00 00 b8 00 00 00 00 c9 |................| 00000090 c3 |.| 00000091# 查看 ".text" 的反匯編內容 $ objdump -d program.o ... Disassembly of section .text:0000000000000000 <main>:0: 55 push %rbp1: 48 89 e5 mov %rsp,%rbp ...4f: c9 leaveq 50: c3 retq對于 Symbol main:
下一篇:ELF 詳解4 – 深入 Symbol
總結
以上是生活随笔為你收集整理的Linux ELF 详解3 -- Symbol Table Symbol的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 语法句子成分
- 下一篇: C# 调用outlook 发送邮件