【程序员的自我修养】第9章 Windows下的动态链接
来源:互联网 发布:java file类能干嘛 编辑:程序博客网 时间:2024/05/22 01:39
第9章 Windows下的动态链接
DLL简介
DLL和EXE文件实际上是一个概念,都是PE格式的二进制文件,不同的是在PE文件头部将两者区分,DLL文件扩展名也可以是.oxc(空间)或者.CPL(控制面板程序)
一个DLL在不同的进程中拥有不同的私有数据版本,DLL的代码不是地址无关的
一个PE文件被装载时,其进程地址空间的起始地址就是基地址,任何一个PE文件都有一个优点装载的基地址,即Image Base
Windows可以将DLL的数据段设为共享,这就意味着一个DLL中有两个数据段,一个进程间共享,另一个私有。
当我们使用“__declspec(dllexport)”时表示该符号是从本DLL导出的符号。“__declspec(dllimport)”表示该符号是从别的DLL导入的符号。
创建DLL:
/*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;
}
使用cl编译器进行编译:
cl /LDd Math.c
参数/LDd表示产生Debug版的DLL,不加任何参数则表示产生EXE可执行文件,可以使用/LD来编译生成Release版的DLL。
通过dumpbin 查看DLL的导出符号:
D:\Program Files\home\*>dumpbin /EXPORTS Math.dll
Microsoft (R) COFF/PE Dumper Version 11.00.50214.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file Math.dll
File Type: DLL
Section contains the following exports for math.dll
00000000 characteristics
4F878A19 time date stamp Fri Apr 13 10:06:17 2012
0.00 version
1 ordinal base
3 number of functions
3 number of names
ordinal hint RVA name //三个导出函数即相对地址
1 0 00001000 Add
2 1 00001040 Mul
3 2 00001020 Sub
Summary
3000 .data
9000 .rdata
3000 .reloc
1E000 .text
使用DLL:程序使用DLL的过程就是一弄DLL中的导出函数和符号的过程,即导入过程
/*test_math.c*/
#include <stdio.h>
__declspec(dllexport) double Sub(double a, double b);
int main(int argc, char *argv[])
{
double result=Sub(3.0, 2.0);
printf("result=%f\n", result);
return 0;
}
编译:
D:\Program Files\home\*>cl /c test_math.c
D:\Program Files\home\*> link test_math.obj math.lib
声明DLL中的某个函数为导出函数的办法有两种,使用“__declspec(dllexport)”扩展;另一种就是采用模块定义(.def)文件声明。
创建math.def文件:
/*math.def*/
LIBRARY Math
EXPORTS
Add
Sub
Mul
Div
编译:
D:\Program Files\home\*>cl math.c /LD /DEF math.def
最终输出跟第一种方法相同的结果。
DLL支持显式运行时链接,Windows提供了3个API为:
l LoadLibrary,这个函数用来装载一个DLL到进程的地址空间,它的功能跟dlopen类似。
l GetProcAddress, 用来查找某个符号的地址,与dlsym类似
l FreeLibrary, 用来卸载某个已加载的模块,与dlclose类似
/*RunDllSimple.c*/
#include <windows.h>
#include <stdio.h>
typedef double (*Func)(double, double);
int main(int argc, char *argv[])
{
Func function;
double result;
HINSTANCE hinstLib=LoadLibrary("math.dll");
if (hinstLib==NULL)
{
printf("ERROR:unable to load DLL.\n");
return 1;
}
function=(Func)GetProcAddress(hinstLib, "Add");
if (function==NULL)
{
printf("ERROR:unable to find DLL function.\n");
FreeLibrary(hinstLib);
return 1;
}
result=function(1.0, 2.0);
FreeLibrary(hinstLib);
printf("result=%f\n", result);
return 0;
}
D:\Program Files\home\*>cl RunDllSimple.c
D:\Program Files\home\*>RunDllSimple.exe
result = 3.000000
符号导出导入表
导出表
当一个PE需要将一些函数或变量提供给其他PE文件使用时,我们把这种行为叫做符号导出。导出表提供了一个符号名与符号地址的映射关系。
在ELF中,“.rel.dyn”和“.rel.plt”两个段中分别保存了该模块所需要导入的变量和函数的符号以及所在的模块等信息。而“.got”和“.got.plt”则保存着这些变量和函数的真正地址。
PE文件头一个DataDirectory的结构数组,其第一个元素就是导出表的地址和长度,导出表是一个IMAGE_EXPORT_DIRECTORY的结构体,导出表最后的三个成员指向的是三个数组:导出地址表、符号名表、名字序号对应表。
使用序号的好处就是省去了函数名查找过程,最大的问题就是函数会发生变化
导入表
查看依赖了哪些DLL:
D:\Program Files\home\*>dumpbin /IMPORTS math.dll
我们的math.dll导入了KERNEL32.dll
在PE文件中,导入表是一个IMAGE_IMPORT_DESCRIPTOR的结构数组,每个IMAGE_IMPORT_DESCRIPTOR结构对应一个被导入的DLL。
EXE文件的基地址默认为0x00400000,而DLL文件基地址默认为0x10000000
DLL优化
n 重定基地址(Rebasing),链接时可以指定DLL的基地址,还有一个editbin工具改变已有的DLL的基地址
n 序号
n 导入函数绑定
DLL绑定:把导出函数的地址保存到模块的导入表中,可以省去每次启动时符号解析的过程。
DLL绑定实现,editbin对被绑定的程序的导入符号进行遍历查找,找到以后就把符号的运行时的目标地址写入到被绑定程序的导入表内。
c++与动态链接
Windows平台型,尽量遵循以下指导意见使编写动态链接库:
(1)所有接口函数都应该是抽象的。所有的方法都应该是纯虚的。(或者inline的方法也可以)
(2)所有的全局函数都应该使用extern “C”来防止名字修饰的不兼容。并且导出函数的都应该是__stdcall调用规范(COM的DLL都使用这样的规范)。这样即使用户本身的程序默认以__cdecl方式编译的,对于DLL的调用也能正确。
(3)不要使用C++标准库STL
(4)不要使用异常
(5)不要使用虚析构函数。可以创建一个destroy()方法并且重载delete操作符并且调用destroy()
(6)不要在DLL里面申请内存。而且在DLL外释放(或者相反)。不同的DLL和可执行文件可能使用不同的堆,在一个堆里面申请内存而在另外一个堆里面释放会导致错误。比如,对于内存分配相关的函数不应该是inline的,以防止它在编译时被展开到不同的DLL和可执行文件
(7)不要在接口中使用重载方法。因为不同的编译器对于vtable的安排可能不同。
DLL HELL(dll噩梦)
Windows缺乏一种有效的DLL版本控制机制,解决方法:
n 静态链接
n 防止DLL覆盖(windows文件保护实现)
n 避免DLL冲突(让每个应用程序拥有自己依赖的DLL)
.Net下DLL Hell的解决方案(Manifest - XML描述文件,(side-by-side manager)SxS Manager实现对相应版本的DLL的加载)
- 【程序员的自我修养】第9章 Windows下的动态链接
- 程序员的自我修养: Windows下的动态链接
- 程序员的自我修养: Windows下的动态链接
- 程序员的自我修养九Windows下的动态链接
- 程序员的自我修养之Windows下的动态链接
- 【程序员的自我修养】第7章 动态链接
- 程序员的自我修养——Windows下的动态链接
- 程序员的自我修养: 动态链接
- 程序员的自我修养 ch7 动态链接
- 【程序员的自我修养】第2章 编译和链接
- 【程序员的自我修养】第4章 静态链接
- 【程序员的自我修养】第5章 Windows PE/COFF
- 《程序员的自我修养》读书笔记3 -- 动态链接
- 程序员的自我修养——动态链接
- 程序员的自我修养——7、动态链接
- 《程序员自我修养》--动态链接
- 《程序员的自我修养》笔记--静态链接
- 程序员的自我修养 ch4 静态链接
- Flex Swf 访问本地文件,本地安全沙箱问题
- vs2005操作word详解
- 【程序员的自我修养】第8章 Linux共享库的组织
- linux下用c语言实现约瑟夫环游戏
- PHP zend framework学习心得
- 【程序员的自我修养】第9章 Windows下的动态链接
- 事务与锁定
- 【程序员的自我修养】第10章 内存
- 直接路径法和在线重定义分区比较
- Web开发的发展史---Web开发技术的演变
- 笔记本禁用自带键盘攻略-------针对shift默认按下的解决方案
- 宏的常见应用领域
- 一句话点评国内外主流BI工具
- Android制作的一个通讯录