程序员的自我修养之Windows下的动态链接

来源:互联网 发布:c语言e语言 编辑:程序博客网 时间:2024/05/17 22:14

9.1 DLL简介

DLL即动态链接库的缩写,它相对于Linux下的共享对象。
Windows下的DLL文件和EXE文件实际上是一个概念,它们都是有PE格式的二进制文件。
微软希望通过DLL机制加强软件的模块化设计,使得各种模块之间能够松散地组合、重用和升级。

9.1.1 进程地址空间和内存管理

一个DLL在不同进程中拥有不同的使用数据副本。在ELF中,由于代码段是地址无关的,所以它可以实现多个进程之间共享一份代码,但是DLL的代码却并不是地址无关的,所以它只是在某些情况下可以被多个进程间共享。

9.1.2 基地址和RVA

PE里面有两个概念就是基地址相对地址。当一个PE文件被加载时,其进程空间中起始地址就是基地址。对于任何一个PE文件来说,它都有一个优先装载的基地址,这个值就是PE文件头中的Image Base。
对于一个可执行EXE文件来说,Image Base一般值是0x400000,对于DLL文件来说,这个值一般是0x10000000。

9.1.3 DLL共享数据段

正常情况下,每个DLL的数据段在各个进程中都是独立的,每个进程都拥有自己的副本。但是Windows允许将DLL的数据段设置成共享的,即任何进程都可以共享该DLL的同一份数据段。比较常见的做法是将一些需要共享的变量分离出来,放到另外一个数据段中,然后设置成进程之间共享的。也就是说,一个DLL有两个数据段,一个是进程间共享,一个是私有的。

9.1.4 DLL的简单例子

导出概念:在ELF中,共享库所有的全局函数和变量在默认情况下都可以被其他模块使用,也就是说ELF默认导出所有的全局符号。但是在DLL中情况是,我们需要显式的告诉编译器我们需要导出的某个符号,否则编译器默认所有符号都不导出。当我们在程序中使用DLL导出符号时,这个过程被称为导入
在C/C++中,可以使用”_declspec”属性关键字来修饰某个函数或者变量。当使用_declspec(dllexport)时,表示该符号是从本DLL导出符号,_declspec(dllimport)是表示该符号是从别的DLL导入符号。

9.1.5 创建DLL

复制代码
__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 /LDd Math.c

9.1.6 使用DLL

程序使用DLL的过程其实是引用DLL中的导出函数和符号过程,即导入过程。

复制代码
#include<stdio.h>__declspec(dllimport) 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;}
复制代码

使用下面命令将TestMath.c编译成TestMath.obj。

cl /c TestMath.c

使用链接器将TestMath.obj和Math.lib链接起来产生一个可执行文件TestMath.exe。

link TestMath.obj Math.lib

这个过程如下图:

Math.lib中并不包含正在Math.c的代码和数据,它描述Math.dll的导出符号,它包含了TestMath.o链接到Math.dll导入符号以及一部分桩代码,又称作”胶水”代码。Math.lib文件被称为导入库

9.1.7 使用模块定义文件

声明DLL中某个函数为导出函数的办法有两种:

  • 一种就是前面使用的”__declspec(dllexport)”
  • 另外一种就是采用模块定义(.def)文件声明。

.def文件用于控制链接过程,为链接器提供有关链接程序的导出符号、属性、以及其他信息。
使用”_stdcall”调用规范的函数Add就会被修饰成”_Add@16”,前面以开头,后面以@n结尾,n表示函数调用时参数所占堆栈空间大小。使用.def文件可以将导出函数重新命名。
微软以DLL的形式提供Windows的API,而每个DLL中的导出函数又以这种”__stdcall”的方式声明。但也采用了导出函数重命名的方法。

9.1.8 DLL显示运行时链接

DLL也支持运行时链接,即运行时加载,Windows提供了3个API为:

  • LoadLibrary,这个函数用来装载一个DLL到进程空间,它的功能和dlopen类似
  • GetProcAddress,用来查找某个符号地址,与dlsym类似
  • FreeLibrary,用来卸载某个已加载的模块,与dlclose类似
复制代码
#include<Windows.h>#include<stdio.h>typedef double(*Func)(double, double);int main(int argc, char **argv) {Func function;double result;float i=1;HINSTANCE hinstLib = LoadLibrary("Math.dll");if (hinstLib == NULL) {printf("ERROR");}function =(Func)GetProcAddress(hinstLib, "Add");if (function==NULL){printf("ERROR");}result = function(1.0, 6.0);FreeLibrary(hinstLib);printf("Result=%f\n", result);scanf_s("%f", i);return 0;

}

【本文由“孤单程序员”发布,2017年10月12日】

原创粉丝点击