为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

Win32动态连接库基址重置技术

2017-11-14 10页 doc 27KB 16阅读

用户头像

is_004283

暂无简介

举报
Win32动态连接库基址重置技术Win32动态连接库基址重置技术 Win32动态连接库基址重置技术 摘要本文讨论了Microsoft Windows NT和Microsoft Windows 95动态链接库dynamic-link libraryDLL基址重置rebasing的若干问题在本文中quotRebasing基址重置quot一词指的是在内存空间中改变动态链接库基地址的过程。随本文章提供了一个应用程序实例和一个动态链接库套件读者可以从中进行一些比较。引言开发人员常问到的一个问题是quot当操作系统对动态链接库DLL进行基址重置时会出现什么效果基址重置...
Win32动态连接库基址重置技术
Win32动态连接库基址重置技术 Win32动态连接库基址重置技术 摘要本文讨论了Microsoft Windows NT和Microsoft Windows 95动态链接库dynamic-link libraryDLL基址重置rebasing的若干问在本文中quotRebasing基址重置quot一词指的是在内存空间中改变动态链接库基地址的过程。随本文章提供了一个应用程序实例和一个动态链接库套件读者可以从中进行一些比较。引言开发人员常问到的一个问题是quot当操作系统对动态链接库DLL进行基址重置时会出现什么效果基址重置会带来什么损失有什么办法可以避免这些损失是否有办法修改代码以减少基址重置所带来的修补fixup工作quot由于这些问题很有代表性所以本文将集中讨论一下加载动态链接库DLL的有关问题希望为使用动态链接库DLL的读者提供一定的参考。事实上本文给出的结果也许并不太新奇也不太具有quot革命性quot比方说最好选择较大的动态链接库DLL而不宜选择一大堆小的动态链接库确定系统不需要花费很长时间搜索动态链接库如果操作系统有可能对动态链接库进行基址重置应尽量避免由此而带来的修补工作或者也可以使用另一种办法尽量选定用户基地址防止基址重置现象的发生。然而正如一句古老言语所说的那样quot过程即目标The journey is the goal.quot。换句话说作者在写作本文时发现了大量的有关动态链接库和内存管理的小问题、小技巧作者认为这些问题和技巧是值得与大家分享的。也许这篇文章更合适的名字应该是quot动态链接库点滴quot。在本文中将给出一个作者自己写的样本测试应用程序此程序用于测试动态链接库的加载时间。本文还将提供一些测试用的动态链接库。应用程序用于测试动态链接库DLL加载时间的测试集结构非常简单主要包括使用Microsoft基础类库Foundation Class librariesMFC编写的PAGETEST应用程序此程序包含两个线程。第一个线程主应用程序建立并拥有默件mutex对象。此线程对当前时间进行采样然后调用LoadLibrary函数以显式方式加载作者提供的库本文下一节将讨论这些库。与此同时第二个线程等待默件mutex对象发送信号。所有的库都由动态链接库的入口程序组成。在动态链接库DLL入口程序的PROCESS_ATTACH发送点默件mutex对象发送信号。此时辅助的应用程序线程被唤醒在调用LoadLibrary函数之前计算采样时间和当前时间之间的差值。这个时间差值大致上就是DLL装入内存所需的时间。MFC应用程序有一个反复加载和卸载DLL的选项50次通过反复加载可以计算出有意义的平均加载时间。文章不需要讨论应用程序的特性因为本文中使用的程序都是简单的MFC应用程序所有的相关代码都存在于显示类中。显示类由CEasyOutputView函数引出用于提供简单的显示结果。欲知进一步的细节请参阅quotWindows NT Security in Theory and PracticequotWindows NT安全性理论与实践读者应该注意这种经验式的测试有很多缺陷因此测试结果有可能与实际结果相背离这主要因为作者假设时间抽样机制是有效而可靠的抽样力度足够细。作者使用了系统性能计数器作者假设线程切换机制是连续有效的对唤醒辅助线程所需时间没有非常不良的影响。测试结果在很大程度上依赖于底层硬件也就是说下列一些因素也会对测试结果产生影响如运行测试程序的计算机速度、所使用的处理器数目、硬盘控制器的速度、等等。测试结果使用特定版本的软件进行抽样。操作系统版本、C运行时间库版本、等等。通常情况下大多数动态链接库都是隐式加载的而不是显示加载的。这就要求作出如下假设隐式加载DLL与显示加载DLL所用的时间一样长相应的其他参数也相同。更糟糕的是作者所得到的数据有时相差很大。因此读者应该有保留地接受这些结果。在测试结果中更有参考价值的不是加载所需的绝对时间而是相对时间。换句话说重要的是调整 某一参数对加载行为的影响以及不同策略之间的加载结果比较。如果读者希望在自己的计算机上重建测试结果可以按照下一节将讨论的动态链接库定位指令运行PTAPP.EXE并在Multiple Test菜单中选择Run All Tests选项。动态链接库下面是动态链接库与加载时间有关的一些性质动态链接库的大小。需要重定位的条目数量。动态链接库是否初始化C启动代码。动态链接库是否输出符号。动态链接库是否与其他库隐式链接。操作系统需要多长时间才能重定位可执行的动态链接库。除了上述因素以外还有一些独立于动态链接库的因素也决定着动态链接库加载速度的快慢。例如底层操作系统、当前计算机上总的工作负荷、应用程序的工作集、动态链接库是否需要基址重置、等等。为简单起见作者给出18个小的动态链接库其中有些并不太小这些DLL几乎代表了以下特性的全部组合动态链接库大小可以大也可以小如果是大的动态链接库是否需要修正加载时间C运行时间支持没有支持、隐式链接的、显示链接的动态链接库输出的符号yes或no作者在同一台机器上在Windows NT版本3.51和Windows 95环境下将全部18个动态链接库通过各自首选的虚拟地址区加载到各自首选的基地址中。每个测试程序也首先将动态链接库定位到当前目录中然后沿着路径下行搜索测试将动态链接库定位到当前搜索到的目录中操作系统所需花费的时间。如前所述每次测试运行50此得到统计平均值。作者的第一个发现是在Windows NT环境下任何给定的动态链接库的初始加载时间平均是在以后加载同一个动态链接库所需时间的三倍。这种现象是由Windows NT内存管理的副作用造成的一旦初始加载了某一动态链接库卸载后属于此动态链接库映象的页仍然存在内存中这些页被放在等待列表中等待列表是系统为丢弃的页维护的一个列表如果原来的应用程序又需要此列表中的页或新的应用程序需要访问列表中的页这些页可以被重新起用。读者如果想了解有关等待列表的进一步信息请参阅Helen Custer的Inside Windows NTWindows NT内幕一书的194页到196页。从等待列表页中重载动态链接库的页比从磁盘上重载有关页效率高的多。随着时间的推移有关的页会从等待列表中移到自由列表中。如果在加载初始动态链接库和加载后续动态链接库之间有很多内存分配和内存访问操作两种情况下加载动态链接库所花的时间将不会有太大差别。为模拟上述行为一定通过多次测试取得加载动态链接库时间的统计平均值作者加入了一个选项此选项允许应用程序尽可能多地占用内存使等待列表尽快用完。伴随着Windows NT Resource Kit微软将提供一个小小的实用程序此程序可以强制将一页从等待列表中取出此程序是CLEARMEM.EXE。实用上述方法确实十分有效但遗憾的是作者释放了所占用的内存之后加载时间一下变成平均时间的20倍--初始加载时间的7倍。这种现象使作者陷入了一个两难境地一方面作者希望得到在正常工作条件下加载动态链接库的可靠的统计平均时间另一方面作者所能得到的唯一的可靠的、一致的时间不是在正常工作条件下得到的。于是作者使用了如下的冒险策略但却是合理的方法解决了这一两难问题假设初始加载时间和随后的平均加载时间之间的关系是固定的将测试结果建立在比较加载动态链接库的平均时间的基础上。使用这种办法在正常工作情况下所得的比较结果仍然是有意义的。读者如果希望重建动态链接库、加入自己的动态链接库变量、或者只是想了解一下作者如何建立了18个动态链接库可以继续阅读或浏览一下下一小节。否则可以跳过这小节直接阅读quot理论quot一节。建立动态链接库使用Visual C版本2.2产生makefile可以建立动态链接库。读者可以在PAGETEST子目录下附加的样本代码中找到相应的工程文件。全部 18个动态链接库都是由相同的工程文件产生的读者可以建立自己的动态链接库原始版本未经调试的版本然后使用以下的命名规则将产生的可执行代码复制到新的位置。PTAPP样本应用程序要求在动态链接库的名字中包含DLL内容信息。动态链接库名字的每一个字母代表一种属性对应的命名规则如下第一个字母代表动态链接库是小库不包含任何数据还是大库包含100000个静态数据元素。此位置如果是S表示动态链接库是小库此位置如果是L表示动态链接库是大库F表示所有的100000个数据元素都初始化为可重定位的自串当动态链接库进行基址重置时字串的地址必须在加载时进行调整。注意有100000个可重定位的字串并不表示一定要进行100000次重定位。对于Visual C版本2.x来说还存在一个问题Visual C的连接器将可移植的可执行文件中的可重定位项数目限制在64K之内。因此如果在某个名字以F开始的动态链接库中运行.EXE头实用程序例如YAHU用户会发现只有34K可重定位的地址。这一问题将在新版本的Visual C中得到解决。第二个字母表示动态链接库是否支持CRT代码C run-time。如果此位为N表示DLL有一个自定义入口点不调用CRT初始化代码C run-time如果此位为C表示动态链接库的入口地址是DllMain可以隐式地初始化CRTC run-time如果此位为D表示动态链接库有一个自定义的入口地址调用_CRT_INIT动态地初始化CRTC run-time库。最后第三个字母如果是N表示动态链接库不输出任何符号如果是E表示输出一个函数。其余的字母现在还未指定含义。例如SCNNNNNN.DLL是一个小的动态链接库隐式调用CRTC run-time初始化代码但不输出任何符号。FNENNNNN.DLL是一个大的动态链接库有许多重定位地址不调用CRTC run-time初始化代码但输出一个符号。为了不将任何不需要的副作用引入比较过程中去作者将动态链接库编写的尽可能的小。作者提供的最小的动态链接库含有一些自定义的动态链接库入口点不初始化CRTC run-time支持代码。在作者提供的任何动态链接库中都不支持MFC。这主要因为MFC动态链接库隐式链接到其他动态链接库上并执行作者不希望引入到测试程序中的自定义初始化过程。所有其他动态链接库的变量都是按照如下方式通过对工程文件略作修改而得到的为了将小的动态链接库制成大的动态链接库需要将MANYPAGES符号加入到预处理器伪指令preprocessor directive中。可以通过在预处理器定义中加入MANYPAGES和FIXUPS产生大的动态链接库这种动态链接库中有很多修复地址。如果需要建立一个使用自定义入口指针调用CRTC run-time初始化代码的动态链接库可以加入DYNACRT符号如果想建立一个具有隐式CRT初始化代码的动态链接库可以加入STANDARDENTRY符号并在Settings/Link/Entry文本框中将动态链接库的入口指针指向DllMain。最后如果需要动态链接库输出一个符号可以加入预处理器伪指令HASSYMBOLS。作者还定义了一个名为HUGEBINARY的符号用于产生真正的大动态链接库即具有大约15000个数据页的动态链接库将此符号与FIXUPS符号连用将产生大约15000个重定位地址。动态链接库的大小在40到61MB之间具体大小取决于用户是否定义了FIXUPS。在测试实例中作者没有在动态链接库测试集中加入二进制代码。无论用户选择哪一种选项来建立动态链接库最终产生的可执行程序都会存储在PAGETEST工程的WINREL子目录下的PAGETEST.DLL文件中。建立动态链接库后用户可以将这个DLL拷贝到不同的位置并根据上述的命名规则将此动态链接库重新命名。为了察看搜索动态链接库二进制字对加载时间的影响可以在计算机上保存每个动态链接库的两份拷贝一个与PTAPP.EXE测试应用程序存在同一目录下另一个 存在搜索路径最末端的子目录中作者计算机上的CDOS目录。使用与可执行文件在相同目录下的动态链接库运行测试程序之后将所有动态链接库重命名强制操作系统在另一目录下查找动态链接库。在建立动态链接库时作者为了测试方便改变了一些选项测试之后无法重新建立原来的结构。因此读者在重建DLL时一定要确定自己的重建方式和作者的方式相同以下是作者采用的工程选项编译器 /nologo/MT/W3/GX/YX/O2/D/FAcs/FaquotWinRel/quotFRquotWinRel/quot/FpquotWi nRel/pagetest.pchquot/FoquotWinRelquot/c预处理器确切的预处理器选项依赖于所建立的库的类型库类型在前文中已经讨论过。连接器kernel32 advapi msvcrt/nologo/subsystemwindows/DLL/incrementalno/PDBquotWinRel/pagetest.pdbquo t/MACHINEI386注意事项PE文件格式包含时间标记。也就是说如果用户两次建立相同的动态链接库产生的结果二进制映象是不同的。作者提供的Byte-Byte-Byte文件比较实用程序可以分两组报告六个不同位元组的信息每个组包括三个连续的位元组。每一对独立开发的、完全相同的动态链接库对应一个位元组。理论要加载动态链接库操作系统必须完成以下各步在磁盘上定位动态链接库可执行文件。仔细查看已经加载进应用程序地址空间中的动态链接库列表判断动态链接库是否已经加载了。为动态链接库分配驻留内存并将动态链接库二进制文件映射到内存中在Windows NT中映射跨越了段对象。为使动态链接库正常运行执行一系列必要的处理例如解析动态链接库中做过的修正等等。不同的参数决定了动态链接库的加载时间不同。以下给出了需要考虑的各种因素事实上可能还有一些因素也会影响到动态链接库的加载时间底层软硬件计算机本身的速度以及运行哪一种操作系统。当前系统和应用程序的状态在虚拟内存中系统的紧密情况以及动态链接库是否可以被加载在首选基地址。动态链接库本身动态链接库本身有多大动态链接库中有多少位置需要修正结合两者综合考虑此动态链接库是否隐式链接到另一个同样需要加载的动态链接库中。由上述可知对某一动态链接库进行基址重置绝不是影响动态链接库加载时间的唯一决定因素。在本文中作者使用了很多数据了动态链接库加载时间的变化范围以及应用程序可能对加载时间的影响程度。读者应该注意到对某一动态链接库进行基址重置不仅可能造成加载时间的大幅度增加还需要增加页文件pagefile的开销。加载动态链接库的第一步需要创建区段对象section object区段对象是由动态链接库可执行文件支持的、内存中的一个相邻的区段。一旦动态链接库的某一页被从应用程序工作集中移去了在下一次访问此页时操作系统就会从动态链接库可执行文件中重新加载这一页。当然当动态链接库进行基址重置时这一策略不再起作用这主要是因为包含重定位地址的页与动态链接库可执行文件映射中的相应页不同。因此一旦在加载可执行文件时操作系统企图修正地址就会拷贝相应的页由于区段是通过COPY_ON_WRITE标志打开的。在拷贝中进行的所有的修改操作系统都会记住从现在开始页将在系统页文件中换入换出而不再在可执行映象中进行页交换。采用这种机制潜在的性能命中有两种首先每个包含需要重定位地址的页都占用一页系统页文件其结果是减少了所有应用程序可用的虚拟内存数另外由于操作系统执行动态链接库页中的第一次修正操作新的页必须从页文件中分配并将整个页拷贝下来。尽管扫描动态链接库重定位区段以及进行内存修正的算法都有很高的效率执行修正操作还是会增加动态链接库的加载时间。跨段操作的复杂性是需要修复地址数量的线性函数。地址修正关于动态链接库基址重置的一类经常问到的问题是quot地址修正究竟 是什么含义程序员是否有办法调整代码以减少可执行程序中的地址修正quot对于这两个问题的回答是这一切在很大程度上依赖于可执行程序建立在哪一种平台上。在本文中我们将平台限制在Intel 386、486、和奔腾Pentium处理器上讨论相应的可执行程序。注意为其他平台建立的可执行程序相应的地址修复概念与本文讨论的概念不同。在386、486、或奔腾处理器上两种情况可能导致某地址被标识为quot可重定位quot的状态一是静态对象static objects另一种情况是绝对跳转absolute jumps。首先如果动态链接库引用了静态对象就会使用对象的绝对地址假设动态链接库被加载到首选地址中。例如在如下的代码段中LPSTR lpNamequotNamequot动态链接库的载入程序就会将quotNamequot字串分配到动态链接库数据段中并将此字串的起始地址填写到lpName变量对应的位置中去。如果由于DLL不能被加载到基地址而使得quotNamequot字串必须重定位lpName必须相应地进行调整。注意在这种情况下代码段中每个引用lpName的变量也必须作相应的地址修正。可以重定位的对象包括文字字串例如在上例中的quotNamequot字串、任何一种类型的全局或静态数据、包含静态分配的C对象。应该注意到特别是在C中可能存在着许多从一个静态对象到另一个静态对象的交叉索引。未初始化的数据不需要在重定位过程中修正地址但指向未初始化静态数据的索引需要进行地址修正。在i386可执行代码中另一类可以进行重定位的项是绝对跳转和函数调用包括系统函数调用。注意到程序开发人员很难通过修改程序代码来避免地址重定位唯一可以采用的办法就是缩减静态分配数据的数目。要缩减静态分配数据一种办法就是尽量避免使用名字进行资源索引而应该通过坐标进行资源索引因为程序员在代码中显式使用的每一个名字都会自动变成一个可重定位的项。尽管如此作者并不建议程序开发人员带着减少加载时间这一特殊目的开发动态链接库代码除非存在以下两种情况1静态分配对象的数据可以大幅度减少2程序员依据此种方式进行编程时不会影响其他编程中需要考虑到的因素。除此之外程序开发人员还可以通过简单的优化方法减少加载时间。例如可以将所有可以重新定位的数据集中到几页中。很显然如果动态链接库需要进行基址重置每页含有一个重定位项的两页都需要有页文件来支持。如果所有的重定位项都出现在同一页中只有一页需要页文件来支持因此只有一页会受到影响。在必要的情况下读者可以使用pragmadata_seg数据段伪指令确保尽可能多的可重定位项被分配到尽可能少的页中去。工具作者在研究动态链接库加载时间时最有趣的收获是作者进一步了解了操作系统的内部工作机制以及可执行文件的格式。以下本文将介绍一些工具这些工具对分析动态链接库的映象机制和运行时的状态非常有帮助可执行头实用程序例如在文章quotYAHUor Yet Another Header Utilityquot中论述的YAHU实用程序通过该程序程序员几乎可以得到动态链接库在编译连接后和加载之前的任何信息--例如共有多少个段、每个段有多大、以及在所有段中多少可以重定位、等等。过程漫步者程序process walker例如Win32 SDK中提供的PWALK程序。此程序可以告诉开发人员动态链接库在过程地址空间的何处加载、以及动态链接库的独立段放在何处。过程显示程序process viewer例如Windows NT Resource KitWindows NT资源箱中提供的PVIEW。此程序可以告诉程序员在任意给定时刻动态链接库占用了内存中的多少页。性能监控程序performance monitor此程序随Windows NT提供在缺省情况下安装在Administrative Tools组中。此程序是一个非常好的性能监控工具可以用来监控加载动态链接库对系统的影响例如使用页文件的情况。下面本文将向大家介绍一下如 何使用这些工具使大家能够进一步了解动态链接库内部的工作机制。在动态链接库SNNNNNNN.DLL上运行YAHU读者可以得到关于动态链接库中五个段的如下信息.TEXT段段大小为0x28字节位于文件头后的0x400字节处。此段中包括全部的动态链接库代码。该段将在动态链接库映象的起始地址之后的第一页处0x1000加载。.DATA段段大小为9位于文件头后的0x600字节处。此段中包含全部的初始数据。该段将在动态链接库映象的起始地址之后的第二页处0x2000加载。.IDATA段段大小为0x6c位于文件头后的0x800字节处。在此段中包含了很多重要数据其中有可执行文件需要连接的动态链接库的名字、动态连接库中调用的函数名等。该段将在动态链接库映象的起始地址之后的第三页处0x3000加载。.EDATA段段大小为0x35位于文件头后的0xA00字节处。在此段中包含了动态链接库的输出信息。该段将在动态链接库映象的起始地址之后的第四页处0x4000加载。.RELOC段段大小为0x32位于文件头后的0xC00字节处。在此段中包含了整个动态链接库的全部重定位信息。该段将在动态链接库映象的起始地址之后的第五页处0x5000加载。在其他动态链接库中读者可能会发现更多的段--例如.BSS段其中包括未初始化的数据。注意文件中每个段各自的偏.
/
本文档为【Win32动态连接库基址重置技术】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索