- 1、本文档共19页,可阅读全部内容。
- 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
- 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
查看更多
Windows加载器与模块初始化
作者:Matt Pietrek
在最近的 MSJ专栏(1999年六月)中,我讨论了COM 类型库和数据库访问层,例如ActiveX®
数据对象(ADO)和 OLE DB。MSJ 专栏的长期读者可能认为我已经不行了(写不出技术层次比较
高的文章了)。为了重振雄风,这个月我要讲解一部分 Windows NT®加载器代码,它是操作系统
和你的代码接合的地方。同时,我也会向你演示一些获取加载器状态信息的高超技巧,以及可以
用在Developer Studio®调试器中的相关技巧。
考虑一下你对 EXE、DLL 以及它们是如何被加载的和初始化的到底知道多少。你可能知道当
一个用C++写成的DLL被加载时,它的DllMain 函数会被调用。想一想当你的 EXE隐含链接到一
些 DLL(例如,KERNEL32.DLL 和 USER32.DLL)时到底发生了什么。这些 DLL 是以什么顺序被初
始化的?某个DLL将要被初始化,而它所依赖的其它DLL还未被初始化,这可能吗?Platform SDK
在“Dynamic Link Library Entry Point Function(动态链接库入口点函数)”一节中对此描述
如下:
“你的函数应该仅进行一些简单的初始化任务,例如设置线程局部存储(TLS),创建同步对
象和打开文件等。它绝对不能调用LoadLibrary 函数,因为这可能在 DLL 加载顺序上造成循环依
赖。这可能导致即将使用一个 DLL 但是系统还未对它进行初始化。同样,你也不能在入口点函数
中调用FreeLibrary函数,因为这可能导致即将使用一个 DLL 但是系统已经执行完了它的终止代
码。”
“调用除 TLS 函数、同步函数和文件函数之外的 Win32®函数也可能引起很难诊断的问题。
例如,调用 User 函数、Shell 函数和 COM 函数可能引起访问违规,因为这些 DLL 中一些函数调
用 LoadLibrary 加载其它系统组件。”
看了上述文档后我的第一反应是它太含糊了。例如你想在自己的 DllMain 函数中读取注册表
是再正常不过的事了,它当然可以作为初始化的一部分。但不幸的是,在你的 DllMain代码开始
执行时ADVAPI32.DLL 还没有初始化。这样,对注册表 API 的调用将会失败。
在上述文档中对使用 LoadLibrary 给出了严厉的警告。但非常有趣的是,Windows NT 的
USER32.DLL却明确地忽略前面的忠告。你可能知道Widnows NT上的一个注册表键AppInit_Dlls,
它用来加载一系列 DLL 到每个进程。事实表明,是USER32 在初始化时加载这些DLL 的。USER32
在它的DllMain 代码中查看这个注册表键并调用 LoadLibrary 加载这些DLL。稍微思考一下就会
知道,如果你的应用程序不使用USER32.DLL的话,AppInit_Dlls这个技巧就不能发挥作用。不
过,这有点跑题了。
我之所以要讲解这方面的内容是因为 DLL的加载与初始化还是一片盲区。在大多数情况下,
对操作系统加载器是如何工作的有一个简单的印象就足够了。然而,在极少数情况下,除非你对
操作系统加载器的行为方式有比较详细的了解,否则就会陷入困境之中。
加载器醒来!
大多数程序员所认为的模块加载过程实际上分为两个截然不同的步骤。第一步是把 EXE 或
DLL 映射进内存。此时加载器查看模块的导入地址表 (IAT)来判断这个模块是否依赖于其它DLL。
如果它依赖的 DLL 还未被加载进那个进程,加载器也将它们映射进内存。这个过程递归进行,直
到所有依赖的模块都被映射进内存。要查看一个可执行文件隐含依赖的所有DLL,最好的方法是
使用Platform SDK 附带的DEPENDS 程序。
第二步是初始化所有 DLL。在第一步中,当操作系统把 EXE和 DLL映射进内存时,它并不调
用相应的初始化例程。初始化例程是在所有模块都被映射进内存之后才被调用的。关键是:DLL
被映射进内存的顺序并不需要与它们被初始化的顺序一样。我曾经见到有人看到 Developer
Studio调试器中对DLL 映射时的通知而误认为DLL 是以相同的顺序被初始化的。
文档评论(0)