导入库如何工作?细节?
我知道这对极客来说似乎很基础.但我想说清楚.
I know this may seem quite basic to geeks. But I want to make it crystal clear.
当我想使用 Win32 DLL 时,通常我只调用像 LoadLibrary() 和 GetProcAdderss() 这样的 API.但是最近在用DirectX9开发,需要添加d3d9.lib、d3dx9.lib等文件.
When I want to use a Win32 DLL, usually I just call the APIs like LoadLibrary() and GetProcAdderss(). But recently, I am developing with DirectX9, and I need to add d3d9.lib, d3dx9.lib, etc files.
我已经听够了 LIB 用于静态链接而 DLL 用于动态链接.
I have heard enough that LIB is for static linking and DLL is for dynamic linking.
所以我目前的理解是 LIB 包含方法的实现,并在链接时作为最终 EXE 文件的一部分进行静态链接.虽然 DLL 是在运行时动态加载的,而不是最终 EXE 文件的一部分.
So my current understanding is that LIB contains the implementation of the methods and is statically linked at link time as part of the final EXE file. While DLL is dynamic loaded at runtime and is not part of the final EXE file.
但有时,DLL 文件会附带一些 LIB 文件,因此:
But sometimes, there're some LIB files coming with the DLL files, so:
- 这些 LIB 文件有什么用?
- 他们如何实现目标?
- 有什么工具可以让我检查这些 LIB 文件的内部结构吗?
查了一下维基百科,我记得这些LIB文件叫做import library.但我想知道它如何与我的主应用程序和动态加载的 DLL 配合使用.
After checking wikipedia, I remember that these LIB files are called import library. But I am wondering how it works with my main application and the DLLs to be dynamically loaded.
正如 RBerteig 所说,在与 DLL 一起生成的 LIB 文件中有一些存根代码.所以调用顺序应该是这样的:
Just as RBerteig said, there're some stub code in the LIB files born with the DLLs. So the calling sequence should be like this:
我的主应用程序 --> LIB 中的存根 --> 真正的目标 DLL
My main application --> stub in the LIB --> real target DLL
那么这些 LIB 中应该包含哪些信息?我可以想到以下几点:
So what information should be contained in these LIBs? I could think of the following:
- LIB 文件应包含相应 DLL 的完整路径;因此 DLL 可以由运行时加载.
- 每个 DLL 导出方法的入口点的相对地址(或文件偏移量?)应该在存根中进行编码;因此可以进行正确的跳转/方法调用.
我说得对吗?还有什么吗?
Am I right on this? Is there something more?
顺便说一句:有什么工具可以检查导入库吗?如果我能看到它,就不会再有疑问了.
推荐答案
链接到 DLL 文件可以隐式在compile链接时发生,或显式 在运行时.无论哪种方式,DLL 最终都会加载到进程内存空间中,并且其所有导出的入口点都可供应用程序使用.
Linking to a DLL file can occur implicitly at compile link time, or explicitly at run time. Either way, the DLL ends up loaded into the processes memory space, and all of its exported entry points are available to the application.
如果在运行时显式使用,您可以使用 LoadLibrary()
和 GetProcAddress()
手动加载 DLL 并获取指向您需要调用的函数的指针.
If used explicitly at run time, you use LoadLibrary()
and GetProcAddress()
to manually load the DLL and get pointers to the functions you need to call.
如果在程序构建时隐式链接,则程序使用的每个 DLL 导出的存根从导入库链接到程序,并且这些存根在进程启动时加载 EXE 和 DLL 时更新.(是的,我在这里简化了很多……)
If linked implicitly when the program is built, then stubs for each DLL export used by the program get linked in to the program from an import library, and those stubs get updated as the EXE and the DLL are loaded when the process launches. (Yes, I've simplified more than a little here...)
那些存根需要来自某个地方,在 Microsoft 工具链中,它们来自一种特殊形式的 .LIB 文件,称为导入库.所需的 .LIB 通常与 DLL 同时构建,并且包含从 DLL 导出的每个函数的存根.
Those stubs need to come from somewhere, and in the Microsoft tool chain they come from a special form of .LIB file called an import library. The required .LIB is usually built at the same time as the DLL, and contains a stub for each function exported from the DLL.
令人困惑的是,同一库的静态版本也将作为 .LIB 文件提供.没有简单的方法可以区分它们,除了作为 DLL 导入库的 LIB 通常比匹配的静态 LIB 小(通常小得多).
Confusingly, a static version of the same library would also be shipped as a .LIB file. There is no trivial way to tell them apart, except that LIBs that are import libraries for DLLs will usually be smaller (often much smaller) than the matching static LIB would be.
如果您使用 GCC 工具链,顺便说一句,您实际上不需要导入库来匹配您的 DLL.移植到 Windows 的 Gnu 链接器版本可以直接理解 DLL,并且可以即时合成大多数所需的存根.
If you use the GCC toolchain, incidentally, you don't actually need import libraries to match your DLLs. The version of the Gnu linker ported to Windows understands DLLs directly, and can synthesize most any required stubs on the fly.
如果您无法抗拒知道所有具体细节和实际情况,那么 MSDN 总有一些东西可以提供帮助.Matt Pietrek 的文章 深入了解 Win32 可移植可执行文件格式 非常完整地概述了 EXE 文件的格式以及它如何加载和运行.自从它最初出现在 MSDN 杂志 ca 以来,它甚至被更新以涵盖 .NET 和更多内容.2002.
If you just can't resist knowing where all the nuts and bolts really are and what is really going on, there is always something at MSDN to help. Matt Pietrek's article An In-Depth Look into the Win32 Portable Executable File Format is a very complete overview of the format of the EXE file and how it gets loaded and run. Its even been updated to cover .NET and more since it originally appeared in MSDN Magazine ca. 2002.
此外,了解如何准确了解程序使用了哪些 DLL 也很有帮助.这个工具是 Dependency Walker,又名depends.exe.它的一个版本包含在 Visual Studio 中,但最新版本可从其作者处获得,网址为 http://www.dependencywalker.com/.它可以识别在链接时指定的所有 DLL(早期加载和延迟加载),还可以运行程序并观察它在运行时加载的任何其他 DLL.
Also, it can be helpful to know how to learn exactly what DLLs are used by a program. The tool for that is Dependency Walker, aka depends.exe. A version of it is included with Visual Studio, but the latest version is available from its author at http://www.dependencywalker.com/. It can identify all of the DLLs that were specified at link time (both early load and delay load) and it can also run the program and watch for any additional DLLs it loads at run time.
为了与 MSDN 保持一致,我已经改写了一些早期的文本以在重新阅读时对其进行澄清,并使用艺术术语隐式和显式链接.
I've reworded some of the earlier text to clarify it on re-reading, and to use the terms of art implicit and explicit linking for consistency with MSDN.
因此,我们可以通过三种方式使库函数可供程序使用.显而易见的后续问题是:我该如何选择哪条路?"
So, we have three ways that library functions might be made available to be used by a program. The obvious follow up question is then: "How to I choose which way?"
静态链接是程序本身的大部分链接方式.列出所有目标文件,并由链接器将它们收集到 EXE 文件中.在此过程中,链接器会处理一些琐碎的工作,例如修复对全局符号的引用,以便您的模块可以调用彼此的函数.库也可以静态链接.构成库的目标文件由库管理器收集在一个 .LIB 文件中,链接器在该文件中搜索包含所需符号的模块.静态链接的一种效果是,只有程序使用的库中的那些模块才链接到它;其他模块被忽略.例如,传统的 C 数学库包括许多三角函数.但是如果你链接它并使用 cos()
,你最终不会得到 sin()
或 tan()<的代码副本/code> 除非您还调用了这些函数.对于具有丰富功能集的大型库,这种选择性地包含模块很重要.在许多平台(例如嵌入式系统)上,与可用于在设备中存储可执行文件的空间相比,可在库中使用的代码的总大小可能很大.如果没有选择性的包含,就很难管理为这些平台构建程序的细节.
Static linking is how the bulk of the program itself is linked. All of your object files are listed, and get collected together in to the EXE file by the linker. Along the way, the linker takes care of minor chores like fixing up references to global symbols so that your modules can call each other's functions. Libraries can also be statically linked. The object files that make up the library are collected together by a librarian in a .LIB file which the linker searches for modules containing symbols that are needed. One effect of static linking is that only those modules from the library that are used by the program are linked to it; other modules are ignored. For instance, the traditional C math library includes many trigonometry functions. But if you link against it and use cos()
, you don't end up with a copy of the code for sin()
or tan()
unless you also called those functions. For large libraries with a rich set of features, this selective inclusion of modules is important. On many platforms such as embedded systems, the total size of code available for use in the library can be large compared to the space available to store an executable in the device. Without selective inclusion, it would be harder to manage the details of building programs for those platforms.
然而,在每个运行的程序中拥有相同库的副本会给通常运行大量进程的系统带来负担.使用正确类型的虚拟内存系统,具有相同内容的内存页面只需要在系统中存在一次,但可以被多个进程使用.这有利于增加包含代码的页面可能与尽可能多的其他正在运行的进程中的某个页面相同的机会.但是,如果程序静态链接到运行时库,那么每个程序都有不同的函数组合,每个函数都布置在处理不同位置的内存映射中,并且没有很多可共享的代码页,除非它是一个完全独立的程序在多个进程中运行.因此,DLL 的想法获得了另一个主要优势.
However, having a copy of the same library in every program running creates a burden on a system that normally runs lots of processes. With the right kind of virtual memory system, pages of memory that have identical content need only exist once in the system, but can be used by many processes. This creates a benefit for increasing the chances that the pages containing code are likely to be identical to some page in as many other running processes as possible. But, if programs statically link to the runtime library, then each has a different mix of functions each laid out in that processes memory map at different locations, and there aren't many sharable code pages unless it is a program that all by itself is run in more than process. So the idea of a DLL gained another, major, advantage.
库的 DLL 包含其所有函数,可供任何客户端程序使用.如果许多程序加载该 DLL,它们都可以共享其代码页.每个人都赢了.(好吧,直到你用新版本更新一个 DLL,但这不是故事的一部分.谷歌 DLL 地狱的故事.)
A DLL for a library contains all of its functions, ready for use by any client program. If many programs load that DLL, they can all share its code pages. Everybody wins. (Well, until you update a DLL with new version, but that isn't part of this story. Google DLL Hell for that side of the tale.)
因此,在规划新项目时要做出的第一个重大选择是在动态和静态链接之间进行.使用静态链接,您需要安装的文件更少,并且您不受第三方更新您使用的 DLL 的影响.但是,您的程序更大,并且它不是 Windows 生态系统的好公民.使用动态链接,您需要安装更多文件,您可能会在第三方更新您使用的 DLL 时遇到问题,但您通常对系统上的其他进程更友好.
So the first big choice to make when planning a new project is between dynamic and static linkage. With static linkage, you have fewer files to install, and you are immune from third parties updating a DLL you use. However, your program is larger, and it isn't quite as good citizen of the Windows ecosystem. With dynamic linkage, you have more files to install, you might have issues with a third party updating a DLL you use, but you are generally being friendlier to other processes on the system.
DLL 的一大优点是无需重新编译甚至重新链接主程序即可加载和使用它.这可以允许第三方库提供者(例如 Microsoft 和 C 运行时)修复他们库中的错误并分发它.一旦最终用户安装了更新的 DLL,他们会立即从使用该 DLL 的所有程序中的错误修复中受益.(除非它破坏了某些东西.请参阅 DLL Hell.)
A big advantage of a DLL is that it can be loaded and used without recompiling or even relinking the main program. This can allow a third party library provider (think Microsoft and the C runtime, for example) to fix a bug in their library and distribute it. Once an end user installs the updated DLL, they immediately get the benefit of that bug fix in all programs that use that DLL. (Unless it breaks things. See DLL Hell.)
另一个优势来自隐式加载和显式加载的区别.如果您进行显式加载的额外工作,那么在编写和发布程序时,该 DLL 甚至可能不存在.例如,这允许可以发现和加载插件的扩展机制.
The other advantage comes from the distinction between implicit and explicit loading. If you go to the extra effort of explicit loading, then the DLL might not even have existed when the program was written and published. This allows for extension mechanisms that can discover and load plugins, for instance.
相关文章