线程的堆栈 改变内存块的大小

作者在 2007-04-24 19:49:00 发布以下内容

20.4函数转发器
函数转发器是DLL的输出节中的一个项目,用于将对一个函数的调用转至另一个DLL中的另一个函数。
如果调用下面的函数,GetProcAddress就会查看Kernel32的输出节,发现HeapAlloc是个转发函数,然后按递归方式调用GetProcAddress函数,查找NTDLL.dll的输出节中的RtlAl-locateHeap。
GetProcAddress(GetModuleHandle(“Kernel32”),”HeapAlloc”);
也可以利用DLL模块中的函数转发器。最容易的方法是像下面这样使用一个pragma指令:
#pragma comment(linker,”/export:SomeFunc=Dllwork.SomeOtherFunc”)
这个pragma告诉链接程序,被编译的DLL应该输出一个名叫SomeFunc的函数。但是SomeFunc函数的实现实际上位于另一个名叫SomeOtherFunc的函数中,该函数包含在称为DllWork.dll的模块中。必须为你想要转发的每个函数创建一个单独的pragma代码行。
20.5已知的DLL
操作系统提供的某些DLL得到了特殊的处理。这些DLL称为已知的DLL。它们与其他DLL基本相同,但是操作系统总是在同一个目录中查找它们,以便对它们进行加载操作。在注册表中有下面的关键字:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnowDlls
当LoadLibrary或LoadLibraryEx被调用时,这些函数首先查看是否传递了包含.dll扩展名的DLL名字。如果没有传递,那么它们将使用通常的搜索规则来搜索DLL。
如果确实设定了.dll扩展名,那么这些函数将删除扩展名,然后搜索注册表关键字KnownDLL,以便确定它是否包含匹配的值名字。如果没有找到匹配的名字,便使用通常的搜索规则。但是,如果找到了匹配的值名字,系统将查找相关的值数据,并设法使用值数据来加载DLL。系统也开始在注册表中的DllDirectory值数据指明的目录中搜索DLL。按照默认设置,
Windows2000上的DllDirectory值的数据是%SystemRoot%\System32。
20.6DLL转移
Windows98 Windows98不支持DLL转移。
Microsoft给Windows2000增加了一个DLL转移特性。这个特性能够强制操作系统的加载程序首先从你的应用程序目录中加载文件模块。只有当加载程序无法在应用程序目录中找到该文件时,它才搜索其他目录。
为了强制加载程序总是首先查找应用程序的目录,要做的工作就是在应用程序的目录中放入一个文件。该文件的内容可以忽略,但是该文件必须称为AppName.local。
例如,如果有一个可执行文件的名字是SuperApp.exe,那么转移文件必须称为SuperApp.exe.local。
在系统内部,LoadLibrary(Ex)已经被修改,以便查看是否存在该文件。如果应用程序的目录中存在该文件,该目录中的模块就已经被加载。如果应用程序的目录中不存在这个模块,LoadLibrary(Ex)将正常运行。
对于已经注册的COM对象来说,这个特性是非常有用的。它使应用程序能够将它的COM对象DLL放入自己的目录,这样,注册了相同COM对象的其他应用程序就无法干扰你的操作。
20.7改变模块的位置
每个可执行模块和DLL模块都有一个首选的基地址,用于标识模块应该映射到的进程地址空间中的理想内存地址。当创建一个可执行模块时,链接程序将该模块的首选基地址设置为0x00400000。如果是DLL模块,链接程序设置的首选基地址是0x10000000。使用VisualStudio的DumpBin实用程序(带有/Headers开关),可以看到一个映像的首选基地址。
现在假设你设计的应用程序需要两个DLL。按照默认设置,链接程序将.exe模块的首选基地址设置为0x00400000,同时,链接程序将两个DLL模块的首选基地址均设置为0x10000000。如果想要运行.exe模块,那么加载程序便创建该虚拟地址空间,并将.exe模块映射到内存地址0x00400000中。然后加载程序将第一个DLL映射到内存地址0x10000000中。但是,当加载程序试图将第二个DLL映射到进程的地址空间中去时,它将无法把它映射到该模块的首选基地址中,必须改变该DLL模块的位置,将它放到别的什么地方。
改变可执行(或DLL)模块的位置是个非常可怕的过程,应该采取措施避免这样的操作。
如果你有多个模块需要加载到单个地址空间中,必须为每个模块设置不同的首选基地址。MicrosoftVisualStudio的ProjectSettings(项目设置)对话框使得这项操作变得非常容易。你只需要选定Link(链接)选项卡,再选定Output(输出)类别。在BaseAddress(基地址)域中(该域默认为空),可以输入一个数字。
另外,始终都应该从高位内存地址开始加载DLL,然后逐步向下加载到低位内存地址,以减少地址空间中出现的碎片。
注意首选基地址必须始终从分配粒度边界开始。在迄今为止的所有平台上,系统的分配粒度是64KB。将来这个分配粒度可能发生变化。第13章已经对分配粒度进行了详细的介绍。
好了,现在已经对所有的内容做了介绍。但是,如果将许多模块加载到单个地址空间中,情况将会如何呢?如果有一种非常容易的方法,可以为所有的模块设置很好的首选基地址,那就好了。幸运的是,这种方法确实有。
VisualStudio配有一个实用程序,称为Rebase.exe。
当你执行Rebase程序,为它传递一组映象文件名时,它将执行下列操作:

(编程)Work | 阅读 3372 次
文章评论,共0条
游客请输入验证码