windows之DLL

第一次写博客……
最近研究了一下windows 下的DLL调用机制及原理(最要是看《windows核心编程》后记录的笔记)。
主要记录了windows下DLL的基本原理以及一些实践测试!但没有涉及到使用的具体历程!

一:DLL和进程的地址空间
1.概述:DLL通常由一组可提供应用程序使用的独立函数组成。在创建DLL的时候,我们必须给连接器制定/DLL 开关。这个开关会使连接器在生成DLL文件印象中保存一些与可执行文件略微不同的信息。这些信息可用于识别DLL而不是可执行程序。

2.映射地址空间:DLL在调用前——>将其映射到进程的地址空间——>两种方法可实现(一是:implicit load-time linking ;二是:explicit linking )
一旦DLL进入进程的内存空间后, 进程中的所用线程就可以调用DLL中的函数(DLL几乎失去了他的DLL身份):给DLL中的函数创建的任何对象都为调用线程或调用进程所有——DLL绝对不会拥有任何对象。
例如:

特别的:一个地址空间可由一个可执行模块和多个DLL模块构成:
eg:DLL 内存中分配的函数可能不会被释放:

VOID EXEFunc(){
    PVOID pv = DLLFunc();
    free(pv);
}
PVOID DLLFunc(){
    return(malloc(100));
}

DLL会被成功释放:
VOID EXEFunc(){
    PVOID pv = DLLFunc();
    DLLFreeFunc(pc);
}
PVOID DLLFunc(){
    PVOID pv = malloc(100);
    return(pv);
}
BOOL DLLFreeFunc(PVOID pv){
    return(free(pv));
}


二:如何构建DLL模块和相应的可执行模块
1.构建DLL:
(1)创建头文件(包含导出函数原型、结构、符号)
(2)创建C/C++ 源文件
(3)构建DLL模块(每一个源文件对应一个.obj模块)
(4)产生DLL印象文件(连接器合并.obj而来)
(5)列出导出的函数名和变量符合(产生.lib文件)
2.构建可执行模块
(1)包含头文件
(2)创建C/C++源文件
(3)产生.obj文件
(4)合并.obj文件,产生可执行印象文件(包含导入段——列出所需的DLL模块名称以及相应的DLL中的导出函数和变量的符号名称)
(5)加载程序问新的进程创建一个虚拟的地址空间——>将可执行模块映射到新的进程地址空间——>解析可执行程序的导入段——>导入段:定位其中的DLL——>导入段:列出可执行文件中引用的函数和变量的符号名——>操作系统的加载程序解析导入段

三:构建DLL模块
1.extern "C" 告诉编译器不对变量名或函数名进行改变;
2.定义MYLIBAPI 为__declspec(dllimport)
3.何为导出:当Microsoft 的C/C++ 编译器看到这个修饰符修饰变量、函数或类的有关嵌入式信息,并生成一个.lib文件,这个.lib文件列出了DLL的导出符号。此外,连接器还会生成DLL文件中嵌入一个导出符号表(导出段:列出了导出的变量、函数和类的符号名)。连接器还会保存相对虚拟地址(RVA)表示每个符号可以在DLL模块中的何处找到。
eg:运用DumpBin.exe 加上-exports 查看DLL中的导出段:


四:构建可执行模块
何为导入:__declspec(dllimport)连接器在解决导入符号的时候,会生成可执行模块中嵌入一段特殊的段(导入段),使用DumpBin.exe加上-imports查看导入段:

五:模块的基地址重定位和模块的的绑定
每个可执行文件和DLL都有一个首选基地址,表示在将模块映射到进程的地址空间 中的最佳地址。
可执行模块的首选基地址:0X00400000
DLL模块的首选基地址:0X10000000
1.重定位:
查看.exe的首选基地址:DumpBin加上/headers

为啥首选基地址如此重要:?
int g_x;
void Func(){
   g_x = 5;
}


编译器在处理Func函数的时候,编译器和连接器会生产:MOV [0x00414540],5
也就是说:编译器和连接器在生成机器代码的时候,将g_x变量地址固定死了( 0x00414540 ),而且必须是首选基地址为)0X00400000的时候这个内存地址才是正确的。
如果一个DLL模块中包含完全相同的代码,编译器也会生成类似的机器码:所以当我们运行自己编写的.exe的时候,加载程序会创建一个虚拟地址空间并将可执行模块映射到内存地址0X00400000——然后加载程序会将第一个DLL程序映射到内存中的0X10000000地址处——这样在加载第二个DLL程序的时候就必须进行基址的重定位。
通过工具:Rebase.exe工具可以实现对模块(DLL)的重定位。
2.绑定:

如果加载程序将导入符号的虚拟地址写入到.exe模块的导入段,那么会写入导入段的后备,那么会写入到导入段的后备存储页面。由于这些页面具有写时复制性,因此他们一页面交换文件为后备存储器。问题:系统必须将印象文件的一部风从内存中交换到页交换文件,并从页交换文件换入到内存,而不能直接抛弃内存中的页面并在需要的时候在从文件的磁盘映像中重新读取。加载程序必须解析所有模块的所有导入符号的地址,这回消耗很长的时间。

对一个模块进行绑定,是用模块导入的所有符号的虚拟地址,来对模块的导入段进行预处理。在载入模块之前执行这一操作。

工具:Bind.exe



















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值