windows动态链接机制(三)
来源:互联网 发布:kmp算法next数组例题 编辑:程序博客网 时间:2024/05/29 17:28
3. 导入库(lib)
动态库(dll)有两种使用方法:
(1)静态链接:库的发行方提供头文件(.h)和导入库文件(.lib)供用户使用,在链接生成用户目标模块(.dll或.exe)时,链接器根据导入库文件(.lib)的指示在目标模块中生成导入表。目标模块在装载时根据导入表的信息解析符号地址。这种在编译、链接和装载期间确定符号地址的机制称为静态链接。
(2)动态链接:用户模块使用Dynamic-Link Library Functions(LoadLibraryEx、GetModuleHandle和GetProcAddress等,可查阅MSDN)在程序运行期间动态获取动态链接库的符号地址。这种在运行期间确定符号地址的机制称为动态链接。
本篇主要研究导入库(lib)的原理,理解链接如何根据导入库信息将依赖符号绑定到导出符号。
3.1 导入库(lib)示例分析
3.1.1 示例1
(1)示例代码
(a)Math1.c
__declspec(dllexport) double __cdecl Add(double a, double b){ return a+b;}__declspec(dllexport) double __cdecl Sub(double a, double b){ return a-b;}__declspec(dllexport) double __cdecl Mul(double a, double b){ return a*b;}__declspec(dllexport) double __cdecl Div(double a, double b){ return a/b;}
(2)编译方法
cl /c /Za /MDd Math1.clink /DLL /out:Math1.dll Math1.obj
(3)分析
使用dumpbin查看Math1.lib的信息:
dumpbin /all Math1.lib
由于篇幅的关系此处不贴出输出信息,用下图来总结Math1.lib的内容:
_Add与__imp__Add是同一个符号,_Add(或__imp__Add)将被链接到Math1.dll的导出符号Add,导出符号Add的hint可能是0。
Name type为no preffix表示依赖符号去掉前缀“”,例如依赖符号_Add去掉前缀“”就是导出符号Add。
其他相同,此处略过。
3.1.2 示例2
(1)示例代码
(a)Math2.c
double Add(double a, double b){ return a+b;}double Sub(double a, double b){ return a-b;}double Mul(double a, double b){ return a*b;}double Div(double a, double b){ return a/b;}
(b)Math2.def
LIBRARY Math2EXPORTSAdd @5Mul @6Sub @7Div @8
(2)编译方法
cl /c /Za /MDd Math2.clink /DLL /DEF:Math2.def /out:Math2.dll Math2.obj
(3)分析
使用dumpbin查看Math2.lib的信息:
dumpbin /all Math2.lib
由于篇幅的关系此处不贴出输出信息,用下图来总结Math2.lib的内容:
_Add与__imp__Add是同一个符号,_Add(或__imp__Add)将被链接到Math2.dll中的导出序号为5的符号。
Name type为ordinal表示依赖符号链接到指定序号的导出符号,例如依赖符号_Add链接到Math2.dll中序号为5的导出符号。
其他相同,此处略过。
3.1.3 示例3
(1)示例代码
(a)Math3.c
double Add(double a, double b){ return a+b;}double Sub(double a, double b){ return a-b;}double Mul(double a, double b){ return a*b;}double Div(double a, double b){ return a/b;}
(b)Math3.def
LIBRARY Math3EXPORTS_Add = Add_Mul = Mul_Sub = Sub_Div = Div
(2)编译方法
cl /c /Za /MDd Math3.clink /DLL /DEF:Math3.def /out:Math3.dll Math3.obj
(3)分析
使用dumpbin查看Math3.lib的信息:
dumpbin /all Math3.lib
由于篇幅的关系此处不贴出输出信息,用下图来总结Math3.lib的内容:
__Add与__imp___Add是同一个符号, _Add(或__imp___Add)将被链接到Math3.dll中的导出符号_Add,导出符号Add的hint可能是0。
Name type为no preffix表示依赖符号去掉前缀“”,例如依赖符号__Add去掉前缀“”就是导出符号_Add。
其他相同,此处略过。
3.1.4 示例4
(1)示例代码
(a)Math4.c
__declspec(dllexport) double __stdcall Add(double a, double b){ return a+b;}__declspec(dllexport) double __stdcall Sub(double a, double b){ return a-b;}__declspec(dllexport) double __stdcall Mul(double a, double b){ return a*b;}__declspec(dllexport) double __stdcall Div(double a, double b){ return a/b;}
(2)编译方法
cl /c /Za /MDd Math4.clink /DLL /out:Math4.dll Math4.obj
(3)分析
使用dumpbin查看Math4.lib的信息:
dumpbin /all Math4.lib
由于篇幅的关系此处不贴出输出信息,用下图来总结Math4.lib的内容:
_Add@16与__imp__Add@16是同一个符号,_Add@16(或__imp__Add@16)将被链接到Math4.dll中的导出符号_Add@16,导出符号_Add@16的hint可能是0。
Name type为name表示依赖符号名字与导出符号名字相同,例如依赖符号_Add@16就是导出符号_Add@16。
其他相同,此处略过。
3.1.5 示例5
(1)示例代码
(a)Math5.c
double __stdcall Add(double a, double b){ return a+b;}double __stdcall Sub(double a, double b){ return a-b;}double __stdcall Mul(double a, double b){ return a*b;}double __stdcall Div(double a, double b){ return a/b;}
(b)Math5.def
LIBRARY Math5EXPORTSAddMulSubDivMyMul=Mul
(2)编译方法
cl /c /Za /MDd Math5.clink /DLL /DEF:Math5.def /out:Math5.dll Math5.obj
(3)分析
使用dumpbin查看Math5.lib的信息:
dumpbin /all Math5.lib
由于篇幅的关系此处不贴出输出信息,用下图来总结Math5.lib的内容:
_Add@16与__imp__Add@16是同一个符号,_Add@16(或__imp__Add@16)将被链接到Math5.dll中的导出符号Add,导出符号Add的hint可能是0。
Name type为undecorate表示依赖符号名字去掉修饰就是导出符号名字,例如依赖符号_Add@16去掉修饰就是导出符号Add。
其他相同,此处略过。
3.2 导入函数
通过上一节的分析可以发现,每个导出符号在导入库中都对应有2个依赖符号,其中一个依赖符号是另外一个依赖符号加上前缀“_imp”。
通过静态链接的方法使用动态链接库(dll)时,接口的声明通常如下:
__declspec(dllimport) double Add(double a, double b);
如果接口声明中去掉“__declspec(dllimport)”,程序一样可以正常运行:
double Add(double a, double b);
那么“__declspec(dllimport)”的作用究竟是什么?下面用两个例子来分析该问题。
3.2.1 示例1
(1)示例代码
(a)Math.c
__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;}__declspec(dllexport) double Div(double a, double b){ return a/b;}
(b)TestMath.c
#include <stdio.h>#if 1__declspec(dllimport) double Mul(double a, double b);__declspec(dllimport) double Div(double a, double b);__declspec(dllimport) double Add(double a, double b);__declspec(dllimport) double Sub(double a, double b);#endifint main(int argc, char **argv){ double result = 0.0; result = Add(3.0, 2.0); result = Sub(3.0, 2.0); result = Mul(3.0, 2.0); result = Div(3.0, 2.0); printf("result = %f\n", result); return 0;}
(2)编译方法
cl /c /Za /MDd Math.clink /DLL /out:Math.dll Math.objcl /c /Za /MDd TestMath.clink TestMath.obj Math.lib
(3)分析
使用dumpbin工具查看TestMath.obj的依赖符号:
dumpbin /symbols TestMath.obj
可以看到如果导入符号的接口声明使用“__declspec(dllimport)”进行修饰,则依赖符号为:“__imp__Add” 、“__imp__Div” 、“__imp__Mul”和“__imp__Sub”。
使用dumpbin工具查看TestMath.exe的反汇编指令:
dumpbin /disasm TestMath.exe
可以看到,对符号“__imp__Add” 、“__imp__Div” 、“__imp__Mul”和“__imp__Sub”的调用都只使用call指令进行了一次间接跳转。
3.2.2 示例2
(1)示例代码
(a)Math.c
__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;}__declspec(dllexport) double Div(double a, double b){ return a/b;}
(b)TestMath.c
#include <stdio.h>#if 0__declspec(dllimport) double Mul(double a, double b);__declspec(dllimport) double Div(double a, double b);__declspec(dllimport) double Add(double a, double b);__declspec(dllimport) double Sub(double a, double b);#endifint main(int argc, char **argv){ double result = 0.0; result = Add(3.0, 2.0); result = Sub(3.0, 2.0); result = Mul(3.0, 2.0); result = Div(3.0, 2.0); printf("result = %f\n", result); return 0;}
(2)编译方法
cl /c /Za /MDd Math.clink /DLL /out:Math.dll Math.objcl /c /Za /MDd TestMath.clink TestMath.obj Math.lib
(3)分析
使用dumpbin工具查看TestMath.obj的依赖符号:
dumpbin /symbols TestMath.obj
可以看到如果导入符号的接口声明未使用“__declspec(dllimport)”进行修饰,则依赖符号为:“_Add” 、“_Div” 、“_Mul”和“_Sub”。
使用dumpbin工具查看TestMath.exe的反汇编指令:
dumpbin /disasm TestMath.exe
可以看到,对符号“_Add” 、“_Div” 、“_Mul”和“_Sub”的调用首先使用call指令跳转到桩代码处(FF 25开头的指令),然后由桩代码使用jmp指令跳转到目标地址。
3.2.3 导入函数总结
使用下面的图例能够清楚地看到在导入函数接口声明中是否使用“__declspec(dllimport)”指示时编译器生成的依赖符号:
3.3 导入库总结
由上面5个示例可以看出:
(1)dll中每个导出符号在导入库都有2个对应的依赖符号(symbol),其中一个依赖符号是另一个依赖符号加上前缀“_imp”。
(2)Name type表示依赖符号名字经过Name type指定:的方法转换后得到导出符号名字。
总结一下导出库的作用就是:在链接时,链接器将目标文件(.obj)中的依赖符号在导入库中进行查询。如果依赖符号在导入库中有匹配,则根据Name type指定的方法将依赖符号转换为对应的导入符号,如果导出符号是按序号到处(Name type为ordianl)则链接器将导入序号写入导入表,如果不是按序号导出,链接器将导入符号名字写入导入表;如果依赖符号在导入库中没有匹配到,则链接器将会报告“符号未定义”错误。
- windows动态链接机制(三)
- windows动态链接机制(一)
- windows动态链接机制(二)
- 静态链接VS动态链接(三)
- 动态链接库(三)
- 专题:Windows动态链接库(一)
- windows下动态链接库(dll)
- windows动态链接库
- windows 动态链接库
- Windows下动态链接
- windows动态链接库
- windows 动态链接库
- 编译与链接(三)——动态链接
- Windows系统和Linux系统中的静态链接库与动态链接库(三)
- Windows系统和Linux系统中的静态链接库与动态链接库(三)
- 链接过程:Windows下动态链接
- 动态链接器(三) 动态链接的步骤和实现
- Windows的动态链接库
- 云计算是什么
- UOJ295,大力分类讨论
- CSS基础(一)--选择器与权重计算练习
- javaSE之多态参数与泛型
- C#--多态与里氏替换原则
- windows动态链接机制(三)
- #NOIP模拟赛#押韵rhyme(TRI树 + Dp)
- CodeForces
- iOS学习笔记-114.多线程13——NSOperationQueue和NSInvocationOperation合用实现多线程
- js-控制页面强制刷新(localStorage或者cookie方式)
- css照片墙展示
- python-request响应的涵义
- 学完java之后总结一下23种设计模式
- python的ConfigParser模块