dustland

dustball in dustland

程序员的自我修养 chapter 5 COFF

COFF文件格式

COFF

common object file format

文件整体结构如图

image-20220811105858318

映像文件头

1
2
3
4
5
6
7
8
9
10
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;

} IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;

与PE文件有区别

在PE文件中映像文件头就是NT文件头,它是PE头的成员NT头的成员

而在COFF中上来就是镜像文件头

使用dumpbin观察main.obj文件的映像文件头

1
2
3
4
5
6
7
8
9
10
11
12
Dump of file main.obj

File Type: COFF OBJECT

FILE HEADER VALUES
8664 machine (x64)
E number of sections
62F46FCA time date stamp Thu Aug 11 10:56:10 2022
4E5 file pointer to symbol table
30 number of symbols
0 size of optional header
0 characteristics

x86_64上的程序

14个节

编译时间2022.8.11 10:56:10

符号表的文件偏移量4E5

符号数量30h

可选头没有,PE中才有,COFF中永远都是0

特征没有

节头表

节头表是一个结构体数组,每个元素都是一个IMAGE_SECTION_HEADER结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节名
union {//联合
DWORD PhysicalAddress;//本段在文件中的地址
DWORD VirtualSize;//本段在虚拟内存中的大小
} Misc;
DWORD VirtualAddress;//相对虚拟地址
DWORD SizeOfRawData;//该段在文件中的大小
DWORD PointerToRawData;//该段在文件中的位置
DWORD PointerToRelocations;//该段的重定位表在文件中的位置
DWORD PointerToLinenumbers;//该段的行号表在文件中的位置
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;//段属性,包括读写执行等性质
} IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;

这个表有多少个元素呢?在映像文件头中的NumberOfSections指明了节头表的元素数量

.drectve

比如第一个节区头就是.drectve的节区头

Directive,编译器传递给链接器的指令,作用和在命令行上link直接指定命令行参数相同

只不过这样写可以给每个目标模块定制一套链接参数,link的时候不需要再写一长串命令行参数了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SECTION HEADER #1
.drectve name
0 physical address
0 virtual address
18 size of raw data
244 file pointer to raw data (00000244 to 0000025B)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
100A00 flags
Info #0x200,该段包含的是注释或者其他信息
Remove #0x800,链接成exe文件时该段最终被抛弃
1 byte align #0x100000,1字节对齐,相当于没有对齐要求

.debug

调试信息

.debug$S表示包含符号相关的调试信息段

.debug$P表示预编译头的调试信息段

.debug$T表示类型相关的调试信息段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SECTION HEADER #2
.debug$S name
0 physical address
0 virtual address
74 size of raw data
11C file pointer to raw data (0000011C to 0000018F)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
42100040 flags
Initialized Data
Discardable
1 byte align
Read Only

节区

节区紧跟在节头表后面,也就是正文部分

使用dumpbin观察节区数量

.drectve

第一个节区就是链接指示信息节区,节区表指示该节区正文位于文件的0x244位置

dumpbin打印的结果中,正文区是紧跟在节头后面的

dumpbin知道这个段是关于链接信息的段,它直接把ASCII码翻译出来了

1
2
3
4
5
6
7
RAW DATA #1
00000000: 20 20 20 2F 44 45 46 41 55 4C 54 4C 49 42 3A 22 /DEFAULTLIB:"
00000010: 4C 49 42 43 4D 54 22 20 LIBCMT"

Linker Directives
-----------------
/DEFAULTLIB:LIBCMT

就相当于在链接时使用

1
link main.obj /DEFAULTLIB:LIBCMT

这也就解释了为啥我们没有显示链接LIBCMT库但是编译器自动要求链接这个库,默认的链接信息已经写入main.obj的.drectve节了

.debug

1
2
3
4
5
6
7
8
9
RAW DATA #2
00000000: 04 00 00 00 F1 00 00 00 66 00 00 00 28 00 01 11 ....?..f...(...
00000010: 00 00 00 00 43 3A 5C 55 73 65 72 73 5C 38 36 31 ....C:\Users\861
00000020: 33 35 5C 64 65 73 6B 74 6F 70 5C 6C 69 6E 6B 5C 35\desktop\link\
00000030: 61 2E 6F 62 6A 00 3A 00 3C 11 00 62 00 00 D0 00 a.obj.:.<..b..?
00000040: 13 00 1F 00 82 79 02 00 13 00 1F 00 82 79 02 00 .....y.......y..
00000050: 4D 69 63 72 6F 73 6F 66 74 20 28 52 29 20 4F 70 Microsoft (R) Op
00000060: 74 69 6D 69 7A 69 6E 67 20 43 6F 6D 70 69 6C 65 timizing Compile
00000070: 72 00 00 00

符号表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

COFF SYMBOL TABLE
000 01047982 ABS notype Static | @comp.id
001 80010190 ABS notype Static | @feat.00
002 00000002 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 74, #relocs 0, #linenums 0, checksum 0
007 00000000 SECT3 notype Static | .text$mn
Section length 24, #relocs 2, #linenums 0, checksum 9B262C10
009 00000000 SECT3 notype () External | main
00A 00000000 UNDEF notype () External | swap
00B 00000000 SECT3 notype Label | $LN3
00C 00000000 SECT4 notype Static | .xdata
Section length 8, #relocs 0, #linenums 0, checksum 37887F31
00E 00000000 SECT4 notype Static | $unwind$main
00F 00000000 SECT5 notype Static | .pdata
Section length C, #relocs 3, #linenums 0, checksum 7D3C6CAC
011 00000000 SECT5 notype Static | $pdata$main
012 00000000 UNDEF notype External | shared
013 00000000 SECT6 notype Static | .chks64
Section length 30, #relocs 0, #linenums 0, checksum 0

最左侧是符号的编号,也就是符号在符号表中的下标,

第二列是符号的大小,即符号所表示的对象占用的空间

第三列是符号所在段位置

ABS表示符号不属于任何段

SECT1表示符号属于COFF文件的第一个段,即.drectve段

UNDEF表示符号未定义,存在于其他文件,需要链接决议

第四列是符号类型

notype:变量和其他符号

notype():函数类型

第五列是访问能见度

Static为本模块可见

Externel为全局变量,可以被其他模块引用

第六列是符号名

如果符号是一个段名则dumpbin会紧接着在下一行列出段属性,包括段长度,段重定位数,行号数和检校和

工具

MSVC

cl /c 编译不链接

cl /Za 禁用Visual C++拓展

cl /Zl 关闭默认C库的链接

dumpbin

类似于GNU objdump

dumpbin /SUMMARY 打印段名段长

dump /ALL 打印全部信息

效果类似于010editor 的模板