C++ 导入库如何工作?细节?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3573475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:09:29  来源:igfitidea点击:

How does the Import Library work? Details?

c++cwindowsvisual-c++

提问by smwikipedia

I know this may seem quite basic to geeks. But I want to make it crystal clear.

我知道这对极客来说似乎很基本。但我想说清楚。

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.

当我想使用 Win32 DLL 时,通常我只调用像 LoadLibrary() 和 GetProcAdderss() 这样的 API。但是最近我在用 DirectX9 开发,我需要添加d3d9.libd3dx9.lib等文件。

I have heard enough that LIB is for static linking and DLL is for dynamic linking.

我已经听够了 LIB 用于静态链接而 DLL 用于动态链接。

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.

所以我目前的理解是 LIB 包含方法的实现,并在链接时作为最终 EXE 文件的一部分进行静态链接。虽然 DLL 在运行时动态加载并且不是最终 EXE 文件的一部分。

But sometimes, there're some LIB files coming withthe DLL files, so:

但有时,DLL 文件会附带一些 LIB文件,因此:

  • What are these LIB files for?
  • How do they achieve what they are meant for?
  • Is there any tools that can let me inspect the internals of these LIB files?
  • 这些 LIB 文件是做什么用的?
  • 他们如何实现他们的目标?
  • 有什么工具可以让我检查这些 LIB 文件的内部结构吗?

Update 1

更新 1

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.

检查维基百科后,我记得这些 LIB 文件被称为import library。但我想知道它如何与我的主应用程序和动态加载的 DLL 一起工作。

Update 2

更新 2

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:

正如 RBerteig 所说,在与 DLL 一起生成的 LIB 文件中有一些存根代码。所以调用顺序应该是这样的:

My main application --> stub in the LIB --> real target DLL

我的主要应用程序 --> LIB 中的存根 --> 真正的目标 DLL

So what information should be contained in these LIBs? I could think of the following:

那么这些LIB中应该包含哪些信息呢?我可以想到以下几点:

  • The LIB file should contain the fullpath of the corresponding DLL; So the DLL could be loaded by the runtime.
  • The relative address (or file offset?) of each DLL export method's entry point should be encoded in the stub; So correct jumps/method calls could be made.
  • LIB 文件应包含相应 DLL 的完整路径;因此 DLL 可以由运行时加载。
  • 每个 DLL 导出方法的入口点的相对地址(或文件偏移量?)应该在存根中编码;因此可以进行正确的跳转/方法调用。

Am I right on this? Is there something more?

我是对的吗?还有什么吗?

BTW: Is there any tool that can inspect an import library? If I can see it, there'll be no more doubts.

顺便说一句:有什么工具可以检查导入库吗?如果我能看到它,就不会再有疑问了。

回答by RBerteig

Linking to a DLL file can occur implicitlyat compilelink time, or explicitlyat 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.

链接到 DLL 文件可以在编译链接时隐式发生,也可以在运行时显式发生。无论哪种方式,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.

如果在运行时显式使用,则使用LoadLibrary()GetProcAddress()手动加载 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...)

如果在程序构建时隐式链接,则程序使用的每个 DLL 导出的存根从导入库链接到程序,并且这些存根在进程启动时加载 EXE 和 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.

这些存根需要来自某个地方,在 Microsoft 工具链中,它们来自一种特殊形式的 .LIB 文件,称为导入库。所需的 .LIB 通常与 DLL 一起构建,并且包含从 DLL 导出的每个函数的存根。

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.

令人困惑的是,同一库的静态版本也将作为 .LIB 文件提供。没有简单的方法可以区分它们,除了作为 DLL 导入库的 LIB 通常比匹配的静态 LIB 小(通常小得多)。

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.

如果您使用 GCC 工具链,顺便说一句,您实际上不需要导入库来匹配您的 DLL。移植到 Windows 的 Gnu 链接器版本可以直接理解 DLL,并且可以即时合成大多数所需的存根。

Update

更新

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 Formatis 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.

如果您无法抗拒了解所有具体细节和实际情况,那么 MSDN 总有一些东西可以提供帮助。Matt Pietrek 的文章深入了解 Win32 可移植可执行文件格式非常完整地概述了 EXE 文件的格式以及它如何加载和运行。自从它最初出现在 MSDN Magazine ca 以来,它甚至已经更新以涵盖 .NET 和更多内容。2002 年。

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.

此外,了解如何准确了解程序使用了哪些 DLL 也很有帮助。这个工具是 Dependency Walker,又名depends.exe。它的一个版本包含在 Visual Studio 中,但最新版本可从其作者处获得,网址http://www.dependencywalker.com/。它可以识别在链接时指定的所有 DLL(早期加载和延迟加载),它还可以运行程序并观察它在运行时加载的任何其他 DLL。

Update 2

更新 2

I've reworded some of the earlier text to clarify it on re-reading, and to use the terms of art implicitand explicit linkingfor consistency with MSDN.

我已经改写了一些较早的文本以在重新阅读时对其进行澄清,并使用隐式显式链接的艺术术语以与 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?"

因此,我们可以通过三种方式使库函数可供程序使用。显而易见的后续问题是:“我该如何选择哪条路?”

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.

静态链接是程序本身的大部分链接方式。您的所有目标文件都被列出,并由链接器收集到 EXE 文件中。在此过程中,链接器会处理一些琐碎的工作,例如修复对全局符号的引用,以便您的模块可以调用彼此的函数。库也可以静态链接。组成库的目标文件由库管理器收集在一个 .LIB 文件中,链接器搜索包含所需符号的模块。静态链接的一种效果是,只有程序使用的库中的那些模块才链接到它;其他模块被忽略。例如,传统的 C 数学库包括许多三角函数。但是如果你链接它并使用cos(),除非您还调用了这些函数,否则您不会得到代码的副本,sin()或者tan()除非您也调用了这些函数。对于具有丰富功能集的大型库,这种选择性地包含模块很重要。在许多平台(例如嵌入式系统)上,与可用于在设备中存储可执行文件的空间相比,可在库中使用的代码的总大小可能很大。如果没有选择性的包含,那么管理为这些平台构建程序的细节就会变得更加困难。

However, having a copy of the samelibrary 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 的想法获得了另一个主要优势。

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 包含其所有函数,可供任何客户端程序使用。如果许多程序加载该 DLL,它们都可以共享其代码页。每个人都赢了。(好吧,直到你用新版本更新一个 DLL,但这不是这个故事的一部分。谷歌 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 的影响。但是,您的程序更大,并且它不是 Windows 生态系统的好公民。使用动态链接,您需要安装更多文件,您可能会在第三方更新您使用的 DLL 时遇到问题,但您通常对系统上的其他进程更友好。

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 的一大优点是无需重新编译甚至重新链接主程序即可加载和使用它。这可以允许第三方库提供者(例如 Microsoft 和 C 运行时)修复他们库中的错误并分发它。一旦最终用户安装了更新的 DLL,他们会立即从使用该 DLL 的所有程序中的错误修复中受益。(除非它破坏了事物。请参阅 DLL Hell。)

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.

另一个优点来自隐式加载和显式加载之间的区别。如果您进行显式加载的额外努力,那么在编写和发布程序时,该 DLL 甚至可能不存在。例如,这允许可以发现和加载插件的扩展机制。

回答by jxramos

These .LIB import library files are used in the following project property, Linker->Input->Additional Dependencies, when building a bunch of dll's that need additional information at link time which is supplied by the import library .LIB files. In the example below to not get linker errors I need to reference to dll's A,B,C, and D through their lib files. (note for the linker to find these files you may need to include their deployment path in Linker->General->Additional Library Directorieselse you will get a build error about being unable to find any of the provided lib files.)

这些 .LIB 导入库文件在以下项目属性中使用Linker->Input->Additional Dependencies,当构建一堆 dll 时,这些 dll 在链接时需要由导入库 .LIB 文件提供的附加信息。在下面的示例中,为了避免链接器错误,我需要通过它们的 lib 文件引用 dll 的 A、B、C 和 D。(注意链接器要找到这些文件,您可能需要将它们的部署路径包含在其中,Linker->General->Additional Library Directories否则您将收到关于无法找到任何提供的 lib 文件的构建错误。)

Linker->Input->Additional Dependencies

链接器->输入->附加依赖项

If your solution is building all dynamic libraries you may have been able to avoid this explicit dependency specification by relying instead on the reference flags exposed under the Common Properties->Framework and Referencesdialog. These flags appear to automatically do the linking on your behalf using the *.lib files. Framework and References

如果您的解决方案正在构建所有动态库,您可能已经能够通过依赖在Common Properties->Framework and References对话框下公开的引用标志来避免这种显式依赖项规范。这些标志似乎代表您使用 *.lib 文件自动进行链接。 框架和参考

This however is as it says a CommonProperties, which is not configuration or platform specific. If you need to support a mixed build scenario as in our application we had a build configuration to render a static build and a special configuration that built a constrained build of a subset of assemblies that were deployed as dynamic libraries. I had used the Use Library Dependency Inputsand Link Library Dependenciesflags set to true under various cases to get things to build and later realizing to simplify things but when introducing my code to the static builds I introduced a ton of linker warnings and the build was incredibly slow for the static builds. I wound up introducing a bunch of these sort of warnings...

然而,这正如它所说的通用属性,它不是特定于配置或平台的。如果您需要像在我们的应用程序中一样支持混合构建场景,我们有一个构建配置来呈现静态构建和一个特殊配置,它构建部署为动态库的程序集子集的约束构建。我曾在各种情况下使用Use Library Dependency InputsLink Library Dependencies标志设置为 true 来构建事物,后来意识到简化事物,但是当将我的代码引入静态构建时,我引入了大量链接器警告,并且静态构建的构建速度非常慢。我最后介绍了一堆这样的警告......

warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored  D.lib(JSource.obj)

And I wound up using the manual specification of Additional Dependenciesto satisfy the linker for the dynamic builds while keeping the static builders happy by not using a common property that slowed them down. When I deploy the dynamic subset build I only deploy the dll files as these lib files are only used at link time, not at runtime.

我最终使用 的手动规范Additional Dependencies来满足动态构建的链接器,同时通过不使用减慢它们的公共属性来保持静态构建器满意。当我部署动态子集构建时,我只部署 dll 文件,因为这些 lib 文件仅在链接时使用,而不是在运行时使用。

回答by Zoltán Sz?cs

There are three kinds of libraries: static, shared and dynamically loaded libraries.

库分为三种:静态库、共享库和动态加载库。

The static libraries are linked with the code at the linking phase, so they are actually in the executable, unlike the shared library, which has only stubs (symbols) to look for in the shared library file, which is loaded at run time before the main function gets called.

静态库在链接阶段与代码链接,所以它们实际上在可执行文件中,不像共享库,它只有存根(符号)要在共享库文件中查找,在运行时加载之前主函数被调用。

The dynamically loaded ones are much like the shared libraries, except they are loaded when and if the need arises by the code you've written.

动态加载的库与共享库非常相似,不同之处在于它们是在您编写的代码需要时加载的。