在《編譯、鏈接與庫》一書中第5.5節《大家都有符號表》,有以下內容:
在輸出結果的最左列是符號的編號,也是符號在符號表中的下標。接着是符號的大小,即符號所表示的對象所占用的空間。第三列是符號所在的位置,……
SimpleSection.c
源程序代碼:
int printf(const char *format, ...);
int global_init_var = 84;
int global_uninit_var;
void func1(int i)
{
printf("%d\n", i);
}
int main(void)
{
static int static_var = 85;
static int static_var2;
int a = 1;
int b;
func1(static_var + static_var2 + a + b);
return a;
}
命令行命令:
cl /c /Za .\SimpleSection.c
dumpbin /ALL .\SimpleSection.obj > .\SimpleSection.txt
關於符號表的輸出摘錄:
COFF SYMBOL TABLE
000 01047558 ABS notype Static | @comp.id
001 80010190 ABS notype Static | @feat.00
002 00000001 ABS notype Static | @vol.md
003 00000000 SECT1 notype Static | .drectve
Section length 18, #relocs 0, #linenums 0, checksum 0
005 00000000 SECT2 notype Static | .debug$S
Section length 94, #relocs 0, #linenums 0, checksum 0
007 00000000 SECT3 notype Static | .data
Section length C, #relocs 0, #linenums 0, checksum AC5AB941
009 00000000 SECT3 notype External | global_init_var
00A 00000004 UNDEF notype External | global_uninit_var
00B 00000000 SECT4 notype Static | .text$mn
Section length 64, #relocs 5, #linenums 0, checksum D696A53
00D 00000000 UNDEF notype () External | printf
00E 00000000 SECT4 notype () External | func1
00F 00000030 SECT4 notype () External | main
010 00000000 SECT4 notype Label | $LN3
011 00000030 SECT4 notype Label | $LN3
012 00000000 SECT5 notype Static | .xdata
Section length 10, #relocs 0, #linenums 0, checksum 434E1581
014 00000000 SECT5 notype Static | $unwind$func1
015 00000000 SECT6 notype Static | .pdata
Section length 18, #relocs 6, #linenums 0, checksum 5710F00F
017 00000000 SECT6 notype Static | $pdata$func1
018 00000008 SECT5 notype Static | $unwind$main
019 0000000C SECT6 notype Static | $pdata$main
01A 00000004 SECT3 notype Static | $SG6143
01B 00000008 SECT3 notype Static | ?static_var@?1??main@@9@9 (`main'::`2'::static_var)
01C 00000000 SECT7 notype Static | .bss
Section length 4, #relocs 0, #linenums 0, checksum 0
01E 00000000 SECT7 notype Static | ?static_var2@?1??main@@9@9 (`main'::`2'::static_var2)
01F 00000000 SECT8 notype Static | .chks64
Section length 40, #relocs 0, #linenums 0, checksum 0
String Table Size = 0x8F bytes
Summary
4 .bss
40 .chks64
C .data
94 .debug$S
18 .drectve
18 .pdata
64 .text$mn
10 .xdata
可是對照global_init_var
那一行的信息,第二列是00000000
,如果這一列表示符號的大小,那顯示的應該是00000004
,這顯然與書上說的矛盾,這讓我很困惑。
對照.data
中的數據:
SECTION HEADER #3
.data name
0 physical address
0 virtual address
C size of raw data
200 file pointer to raw data (00000200 to 0000020B)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
C0300040 flags
Initialized Data
4 byte align
Read Write
RAW DATA #3
00000000: 54 00 00 00 25 64 0A 00 55 00 00 00 T...%d..U...
54 00 00 00
的十進制是84,那么這個是變量global_init_var
,而25 64 0A 00
這個顯然是字符串"%d\n"
了,55 00 00 00
是變量static_var
。
再次結合之前講到的關於ELF文件關於符號的內容:
符號值(st_value)我們前面已經介紹過,每個符號都有一個對應的值,如果這個符號是一個函數或變量的定義,那么符號的值就是這個函數或變量的地址……在目標文件中,如果是符號的定義並且該符號不是“COMMON塊”類型的,則st_value表示該符號在段中的偏移。即符號所對應的函數或變量位於由st_shndx指定的段,偏移st_value的位置。這也是目標文件中定義全局變量的符號的最常見情況……
那么可以初步斷定這一列表示的意義實際上是變量或函數在該段中的偏移量,再結合代碼段看看:
SECTION HEADER #4
.text$mn name
0 physical address
0 virtual address
64 size of raw data
20C file pointer to raw data (0000020C to 0000026F)
270 file pointer to relocation table
0 file pointer to line numbers
5 number of relocations
0 number of line numbers
60500020 flags
Code
16 byte align
Execute Read
RAW DATA #4
00000000: 89 4C 24 08 48 83 EC 28 8B 54 24 30 48 8D 0D 00 .L$.H.?.T$0H...
00000010: 00 00 00 E8 00 00 00 00 48 83 C4 28 C3 CC CC CC ...?...H.?錳燙
00000020: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC 燙燙燙燙燙燙燙燙
00000030: 48 83 EC 38 C7 44 24 20 01 00 00 00 8B 05 00 00 H.?荄$ ........
00000040: 00 00 8B 0D 00 00 00 00 03 C8 8B C1 03 44 24 20 .........??D$
00000050: 03 44 24 24 8B C8 E8 00 00 00 00 8B 44 24 20 48 .D$$.辱.....D$ H
00000060: 83 C4 38 C3 .??
RELOCATIONS #4
Symbol Symbol
Offset Type Applied To Index Name
-------- ---------------- ----------------- -------- ------
0000000F REL32 00000000 1A $SG6143
00000014 REL32 00000000 D printf
0000003E REL32 00000000 1E ?static_var2@?1??main@@9@9 (`main'::`2'::static_var2)
00000044 REL32 00000000 1B ?static_var@?1??main@@9@9 (`main'::`2'::static_var)
00000057 REL32 00000000 E func1
將目標文件反匯編:
dumpbin /disasm .\SimpleSection.obj > .\SimpleSection.asm
反匯編文件內容:
Microsoft (R) COFF/PE Dumper Version 14.29.30040.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file .\SimpleSection.obj
File Type: COFF OBJECT
func1:
0000000000000000: 89 4C 24 08 mov dword ptr [rsp+8],ecx
0000000000000004: 48 83 EC 28 sub rsp,28h
0000000000000008: 8B 54 24 30 mov edx,dword ptr [rsp+30h]
000000000000000C: 48 8D 0D 00 00 00 lea rcx,[$SG6143]
00
0000000000000013: E8 00 00 00 00 call printf
0000000000000018: 48 83 C4 28 add rsp,28h
000000000000001C: C3 ret
000000000000001D: CC int 3
000000000000001E: CC int 3
000000000000001F: CC int 3
0000000000000020: CC int 3
0000000000000021: CC int 3
0000000000000022: CC int 3
0000000000000023: CC int 3
0000000000000024: CC int 3
0000000000000025: CC int 3
0000000000000026: CC int 3
0000000000000027: CC int 3
0000000000000028: CC int 3
0000000000000029: CC int 3
000000000000002A: CC int 3
000000000000002B: CC int 3
000000000000002C: CC int 3
000000000000002D: CC int 3
000000000000002E: CC int 3
000000000000002F: CC int 3
main:
0000000000000030: 48 83 EC 38 sub rsp,38h
0000000000000034: C7 44 24 20 01 00 mov dword ptr [rsp+20h],1
00 00
000000000000003C: 8B 05 00 00 00 00 mov eax,dword ptr [?static_var2@?1??main@@9@9]
0000000000000042: 8B 0D 00 00 00 00 mov ecx,dword ptr [?static_var@?1??main@@9@9]
0000000000000048: 03 C8 add ecx,eax
000000000000004A: 8B C1 mov eax,ecx
000000000000004C: 03 44 24 20 add eax,dword ptr [rsp+20h]
0000000000000050: 03 44 24 24 add eax,dword ptr [rsp+24h]
0000000000000054: 8B C8 mov ecx,eax
0000000000000056: E8 00 00 00 00 call func1
000000000000005B: 8B 44 24 20 mov eax,dword ptr [rsp+20h]
000000000000005F: 48 83 C4 38 add rsp,38h
0000000000000063: C3 ret
Summary
4 .bss
40 .chks64
C .data
94 .debug$S
18 .drectve
18 .pdata
64 .text$mn
10 .xdata
可以看到func1確實是以00000000
開頭,main以00000030
開頭,那么可以確定之前的結論是正確的。對於變量和函數來說,符號表輸出的第二列是其在段內的偏移量。而對於global_uninit_var
這樣的(未定義的)變量才表示是符號的大小,即符號所表示的對象所占用的空間:
// ...
long long global_uninit_var;
int global_uninit_var2;
// ...
int main()
{
// ...
}
00A 00000008 UNDEF notype External | global_uninit_var
00B 00000004 UNDEF notype External | global_uninit_var2