windows LIB文件格式
静态库
以Math.c为例,将其分别编译静态库文件libMath.lib
Math.c
1 2 3 4 5 6 7 8 9 __declspec(dllexport) double Add (double a,double b) { return a+b; } __declspec(dllexport) double Sub (double a,double b) { return a-b; } __declspec(dllexport) double Mul (double a,double b) { return a*b; }
制作静态库
1 lib Math.c /NAME:libMath.lib
lib以一个文件魔数!<arch>\n
开头,
接下来顺次是First Section,Second Section,Long Section,Obj
Section.
如果本lib文件中包括了多个obj文件则ObjSection可以有多个.
Long
Section用来存放太长的obj名称,如果没有超过16个字节的obj名则该节不存在
libMath.lib的结构
文件头魔数
不管是静态库还是导入库, 只要是lib文件,开头八个字节都得是文件魔数,
arch,archieve,归档
image-20220818144331204
节区
每个节区都是以一个结构体开始,可以认为是"节区头"
1 2 3 4 5 6 7 8 9 struct SectionHeader { char Name[16 ]; char Time[12 ]; char UserID[6 ]; char GroupID[6 ]; char Mode[8 ]; char Size[10 ]; char EndOfHeader[2 ]; } ;
全都是字符串格式用字节存储,其好处是不管大小端机器,都可以兼容
很奇怪的是FirstSection采用大端模式,到了SecondSection就又成了小端存储了
节区头后面紧跟着是该节的节区正文
即每个节区都是节区头+节区正文格式
相邻两个节区之间可能存在胸垫填充,比如FirstSection的节区正文结束和SecondSection的节头开始之间就存在一个字节的填充
image-20220818164735293
怎么判断有没有填充呢?怎么寻找下一个节的开始位置呢?
每个节头大小是固定的60字节,最后两个字节一定是endMarker[2]="\60
\0A"
First Section
大端模式
包含库中所有 符号名以及这些符号所在目标文件在本lib库文件中的偏移量
此处"所有"是指参与组成本lib文件的所有目标文件中的所有符号,不只是一个目标文件中的所有符号
其节名就用了一个字符"/"
Size指明了本节正文占用了多少个字节
节正文结构:
1 2 3 4 5 struct FirstSection { unsigned long SymbolNum; unsigned long SymbolOffset[SymbolNum]; char StrTable[m]; }
对于libMath.lib的第FirstSection,其节区正文的16进制表示为:
1 2 3 4 5 6 7 8 9 10 11 SymbolNum = 00 00 00 03 SymbolOffset[SymbolNum] = 00 00 00 C2 00 00 00 C2 00 00 00 C2 StrTable[m] = 5F 41 64 64 00 5F 4D 75 6C 00 5F 53 75 62 00
在32位机器上long和int一样长都是32位,因此unsigned
long占用了前4个字节
由于大端存储因此03在最高位(最右侧)
SymbolNum=3
,这与Math.c中正好有三个函数符号相吻合
SymbolOffset[3]={C2,C2,C2}
这表明三个符号同属于一个目标模块,这个目标模块在本lib文件中的偏移量是C2
image-20220818152323244
C2位置确实是第一个也是唯一一个ObjSection的偏移量,这个ObjSection就是Math.obj
StrTable[m]=_Add._Mul._Sub.
即所有符号名,每个符号名都以00结尾
Second Section
小端模式
内容和FirstSection相同,但是是一个有序表,查这个比查First
Section来的快
名字也和FirstSection相同,都是"/"
正文结构:
1 2 3 4 5 6 7 struct SecondSection { unsigned long ObjNum; unsigned long ObjOffset[ObjNum]; unsigned long SymbolNum; unsigned short SymbolIdx[SymbolNum]; char StrTable[m]; }
1 2 3 4 5 6 7 8 9 10 11 ObjNum= 01 00 00 00 ObjOffset[ObjNum] = C2 00 00 00 SymbolNum = 03 00 00 00 SymbolIdx[SymbolNum] = 01 00 01 00 01 00 StrTable[SymbolNum] = 5F 41 64 64 00 _Add\0 5F 4D 75 6C 00 _Mul\0 5F 53 75 62 00 _Sum\0
比较FirstSection和SecondSection
FirstSection
SecondSection
意义是否相同
存储方式
大端
小端
记录符号个数
SymbolNum
SymbolNum
相同
记录符号位置
SymbolOffset[i]第i个符号所在obj的节偏移量
SymbolIdx[i]第i个符号所在的obj节是第几个obj节
不同
记录符号名
StrTable[m]
StrTable[m]
相同
记录Obj节数
无
ObjNum
记录每个Obj节的偏移量
无
ObjOffset[ObjNum]
Long Section
长名称节,存放太长的obj名
每个节开始时的SectionHeader会记录该节的信息,但是SectionHeader.Name只有16字节,如果一个obj文件名比如"LinkedDoubleList.obj"长度就超过了16字节,用Name[16]显然放不下,就得放到Long
section节里,Name[16]存放的是"/<LongSection中的偏移量>
"表明该节名需要去Long
Section找,并且给出了相对于该节的位置
image-20220818154931447
Math.obj显然不够16个字节,本lib文件中没有该节
Obj Section
目标文件节,存放不同的目标文件的原始数据.
本节的节区正文相当于把COFF文件直接乎过来了
节头:
image-20220818154534122
从名称上可以看出Math.obj/
Size=794本节正文长794个字节
节区正文是直接抄的obj文件
节区正文
Math.obj
节区正文和Math.obj完全相同
导入库
还是以Math.c制作动态库时形成的导入库Math.lib为例
1 2 3 4 5 6 7 8 9 __declspec(dllexport) double Add (double a,double b) { return a+b; } __declspec(dllexport) double Sub (double a,double b) { return a-b; } __declspec(dllexport) double Mul (double a,double b) { return a*b; }
1 2 cl Math.c /c link /dll Math.obj
编译链接完成后同一目录下面形成Math.lib和Math.dll
此Math.lib就是导入库
image-20220818165520115
从节头名看,前两个节分别是FirstSection和SecondSection,后面就都是目标文件了
文件头魔数
所有lib文件不管是静态库还是导入库都一样!<arch>
节区
First Section
节头除了本节正文大小之外就没有什么有效信息了主要是看节区:
1 2 3 4 5 struct FirstSection { unsigned long SymbolNum; unsigned long SymbolOffset[SymbolNum]; char StrTable[m]; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 SymbolNum = 00 00 00 09 SymbolOffset[SymbolNum] = 00 00 01 CA 00 00 03 E8 00 00 05 1C 00 00 06 68 00 00 06 68 00 00 07 24 00 00 07 24 00 00 06 C6 00 00 06 C6 StrTable[m] = __IMPORT_DESCRIPTOR_Math: 5F 5F 49 4D 50 4F 52 54 5F 44 45 53 43 52 49 50 54 4F 52 5F 4D 61 74 68 00 __NULL_IMPORT_DESCRIPTOR: 5F 5F 4E 55 4C 4C 5F 49 4D 50 4F 52 54 5F 44 45 53 43 52 49 50 54 4F 52 00 Math_NULL_THUNK_DATA: 7F 4D 61 74 68 5F 4E 55 4C 4C 5F 54 48 55 4E 4B 5F 44 41 54 41 00 _Add: 5F 41 64 64 00 __imp__Add: 5F 5F 69 6D 70 5F 5F 41 64 64 00 _Sub: 5F 53 75 62 00 __imp__Sub: 5F 5F 69 6D 70 5F 5F 53 75 62 00 _Mul: 5F 4D 75 6C 00 __imp__Mul: 5F 5F 69 6D 70 5F 5F 4D 75 6C 00
可以看出本导入库中有9个符号,前三个符号
1 2 3 __IMPORT_DESCRIPTOR_Math __NULL_IMPORT_DESCRIPTOR Math_NULL_THUNK_DATA
是预定义的,就算我们啥也不写,照样有这三个符号
然后每个我们自己写的函数都有两个名字,比如
这是x64符号名修饰造成的,实际上两个符号指向同一函数
Second Section
1 2 3 4 5 6 7 struct SecondSection { unsigned long ObjNum; unsigned long ObjOffset[ObjNum]; unsigned long SymbolNum; unsigned short SymbolIdx[SymbolNum]; char StrTable[m]; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ObjNum = 06 00 00 00 ObjOffset[ObjNum]= CA 01 00 00 E8 03 00 00 1C 05 00 00 68 06 00 00 24 07 00 00 C6 06 00 00 SymbolNum=09 00 00 00 SymbolIdx[SymbolNum]= 04 00 06 00 05 00 01 00 02 00 04 00 06 00 05 00 03 00 StrTable[m]= 5F 41 64 64 00 5F 4D 75 6C 00 5F 53 75 62 00 5F 5F 49 4D 50 4F 52 54 5F 44 45 53 43 52 49 50 54 4F 52 5F 4D 61 74 68 00 5F 5F 4E 55 4C 4C 5F 49 4D 50 4F 52 54 5F 44 45 53 43 52 49 50 54 4F 52 00 5F 5F 69 6D 70 5F 5F 41 64 64 00 5F 5F 69 6D 70 5F 5F 4D 75 6C 00 5F 5F 69 6D 70 5F 5F 53 75 62 00 7F 4D 61 74 68 5F 4E 55 4C 4C 5F 54 48 55 4E 4B 5F 44 41 54 41 00
共有6个ObjSection段
每个ObjSection的节头中的名字都叫"Math.dll"
Obj Section
前三个Obj节都是关于三个预定义符号的,所有的导入库中他仨基本相同
后面三个Obj节是关于我们自定义的函数的,每个自定义函数自成一节,这也就解释了为啥默认状态下动态库的链接是以函数为单位,而静态库的链接是以模块为单位的了.导入库中每个函数自成一个模块
下面炎鸠后三节的结构,参考The
Structure of import Library File (.lib) - CodeProject
以Add函数所在节为例
image-20220819104759118
节头表明正文部分有34字节
描述正文的结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct SYMBOL_DESCRIPTOR // size = 0x14 { WORD a; WORD b; WORD c; WORD Architecture; DWORD Id; DWORD Length; union { WORD Hint; WORD Ordinal; WORD Value; } WORD Type; };
1 2 3 4 5 6 7 8 9 10 11 00 00 FF FF 00 00 4C 01 //Architecture,x86 F2 29 95 FA //随机数 0E 00 00 00 //_Add\0Math.dll\0 两个字符串的总长度(包括00) 00 00 //Hint=0,表示AddressOfNames中的下标 08 00 //Type=8,表示x86 __cdecl调用约定 5F 41 64 64 00 //_Add 4D 61 74 68 2E 64 6C 6C 00 //Math.dll
工具
MSVC
cl /c
只编译,不链接,生成目标模块
lib Math.c
/NAME:libMath.lib 制作静态库
link /dll Math.obj 制作动态库