C / C ++动态链接如何在不同的平台上运行?

时间:2014-04-04 10:44:22

标签: c++ c compilation dynamic-linking loadlibrary

动态链接如何工作?

在Windows(LoadLibrary)上,您需要在运行时调用.dll,但在链接时,您需要提供相应的.lib文件或程序不会链接... .lib是什么文件包含? .dll方法的描述?不是标题包含的内容吗?

相关地,在* nix上,您不需要lib文件...编译器如何知道标题中描述的方法在运行时可用?

作为一个新手,当你想到两个方案中的任何一个,那么另一个,它们都没有意义......

9 个答案:

答案 0 :(得分:22)

逐一回答您的问题:

  • 动态链接将部分链接过程延迟到运行时。 它可以以两种方式使用:隐式和显式。 隐式地,静态链接器将信息插入到 可执行文件,将导致库加载和解决 必要的符号。显然,您必须致电LoadLibrary或 手动dlopen,然后GetProcAddress / dlsym 你需要使用的符号。隐式加载用于事物 就像系统库一样,实现依赖于 系统的版本,但保证接口。 显式加载用于插件之类的东西 要加载的库将在运行时确定。

  • 只有隐式加载才需要.lib文件。它 包含库实际提供的信息 符号,所以链接器不会抱怨符号是 undefined,它告诉链接器在哪个库中的符号 位于,因此它可以插入必要的信息 这个库自动加载。所有头文件 告诉编译器,符号将存在于某处;该 链接器需要.lib知道在哪里。

  • 在Unix下,所有信息都是从中提取的 .so。为什么Windows需要两个单独的文件,而不是 把所有信息都放在一个文件中,我不知道;它的 实际上复制了大部分信息,因为 .lib中还需要.dll中所需的信息。 (也许许可问题。您可以分发您的程序 .dll,但没有人可以链接到库,除非 他们有.lib。)

要保留的主要内容是,如果要隐式加载, 你必须为链接器提供适当的信息, 使用.lib.so文件,以便它可以插入 信息到可执行文件中。如果你想要明确的话 加载时,不能引用库中的任何符号 直;你必须致电GetProcAddress / dlsym来获取他们的 解决自己的问题(做一些有趣的演员来使用它们)。

答案 1 :(得分:13)

加载动态库不需要Windows上的.lib文件,它只是提供了一种方便的方法。

原则上,您可以使用LoadLibrary加载dll,然后使用GetProcAddress访问该dll提供的功能。在这种情况下,封闭程序的编译不需要访问dll,只需要在运行时(即LoadLibrary实际执行时)。 MSDN有a code example

这里的缺点是您需要手动编写用于从dll加载函数的代码。如果你自己编译了dll,这段代码只是复制了编译器可以自动从dll源代码中提取的知识(比如导出函数的名称和签名)。

这是.lib文件的作用:它包含由编译器生成的Dlls导出函数的GetProcAddress调用,因此您不必担心它。在Windows术语中,这称为Load-Time Dynamic Linking,因为加载封闭程序时,来自.lib文件的代码会自动加载Dll(与手动方法相反,称为运行时)动态链接)。

答案 2 :(得分:9)

  

动态链接如何正常工作?

动态链接库(又名共享对象)文件包含机器代码指令和数据,以及元数据表,说明该代码/数据中的哪些偏移与哪些“符号”,符号的类型相关(例如,函数vs数据),数据中的字节数或字数,以及其他一些东西。不同的操作系统往往会有不同的共享对象文件格式,实际上相同的操作系统可能支持多种操作系统,但这就是它的主旨。

因此,想象共享库是一大块字节,其索引如下:

SYMBOL       ADDRESS        TYPE        SIZE
my_function  1000           function    2893
my_number    4800           variable    4

通常,不需要在元数据表中捕获符号的确切类型 - 期望库的头文件中的声明包含所有缺少的信息。 C ++有点特殊 - 与C相比 - 因为重载可能意味着有几个具有相同名称的函数,并且命名空间允许进一步的符号,否则将被模糊地命名 - 因此 name mangling 是通常用于将命名空间和函数参数的某些表示连接到函数名称,形成在库对象文件中可以唯一的东西。

想要使用共享对象的程序通常可以执行以下两种操作之一:

  • 让操作系统同时加载自身和共享对象(在执行main()之前),OS Loader负责查找符号并检查程序文件图像中有关使用的元数据这些符号然后在程序使用的内存中的符号地址中进行修补,这样程序就可以在功能上运行和工作,就好像它在第一次编译时知道符号地址一样(但可能稍微慢一点)< / p>

  • 或者,在dlopen运行后的某个时间显式地在其自己的源代码中调用main,然后使用dlsym或类似的方法获取符号地址,将它们保存到(function /数据)指针基于程序员对预期数据类型的了解,然后使用指针显式调用它们。

  

在Windows(LoadLibrary)上,您需要在运行时调用.dll,但在链接时,您需要提供相应的.lib文件,否则程序将无法链接...

这听起来不对。应该是我认为的一个或另一个。

  

Wtf是否包含.lib文件? .dll方法的描述?这不是标题包含的内容吗?

lib文件 - 在这个描述级别 - 与共享对象文件几乎相同......主要区别在于编译器在程序发布和运行之前找到符号地址。

答案 3 :(得分:2)

  

相关地,在OS X上(我假设是* nix ... dlopen),您不需要lib文件...编译器如何知道标题中描述的方法将在运行

编译器或链接器不需要此类信息。作为程序员,您需要处理dlopen()尝试打开的共享库可能不存在的情况。

答案 4 :(得分:2)

您可以通过两种方式在Windows中使用DLL文件:要么与它链接,要么已经完成,无需再做任何事情。 在运行时动态加载它。

如果您链接它,则使用DLL库文件。链接库包含链接器用于实际知道要加载哪个DLL以及DLL函数中的位置的信息,因此它可以调用它们。加载程序后,操作系统也会为您加载DLL,基本上它会为您调用LoadLibrary

在其他操作系统(如OS X和Linux)中,它以类似的方式工作。不同之处在于,在这些系统上,链接器可以直接查看动态库(.so / .dynlib文件),并在没有像Windows一样的单独静态库的情况下找出所需的内容。

要动态加载库,您无需链接任何与要加载的库相关的内容。

答案 5 :(得分:2)

Modern * nix系统从Solaris OS派生动态链接过程。特别是Linux,不需要单独的.lib文件,因为所有外部依赖项都包含在ELF格式中。 ELF文件的.interp部分表示此可执行文件中有外部符号需要动态解析。这是动态链接

有一种方法可以处理用户空间中的动态链接。此方法称为动态加载。这是当您使用系统调用从外部* .so。

获取方法的函数指针时

可以在本文http://www.ibm.com/developerworks/library/l-dynamic-libraries/中找到更多信息。

答案 6 :(得分:2)

与其他人已经说过:Windows上的.lib文件中包含的内容直接包含在Linux / OS X上的.so / .dynlib中。但主要问题是......为什么? 不是* nix解决方案更好吗? 我认为是,但.lib有一个优势。链接到DLL的开发人员实际上不需要访问DLL文件本身。

这种情况经常发生在现实世界中吗?是否值得为每个DLL文件维护两个文件?我不知道。

编辑:好吧,伙计们让事情变得更加令人困惑!您可以使用MinGW直接链接到Windows上的DLL。因此整个导入库问题与Windows本身没有直接。摘自MinGW wiki的sampleDLL文章:

  

“--out-implib”链接器选项创建的导入库是   要求iff(== if且仅当if)DLL应与某些接口连接   MinGW工具链以外的C / C ++编译器。 MinGW工具链是   非常乐意直接链接到创建的DLL。更多细节   可以在作为binutils一部分的ld.exe信息文件中找到   包(这是工具链的一部分)。

答案 7 :(得分:1)

Linux也需要链接,但是对于.Lib库需要链接到动态链接器/lib/ld-linux.so.2,但这通常发生在使用GCC的幕后(但是如果使用汇编程序,则需要手动指定。)

这两种方法,无论是Windows .LIB方法还是Linux动态链接器链接方法,都被视为静态链接。但是,有一点不同,在Windows部分工作是在链接时完成的,尽管它仍然在加载时工作(我不确定,但我认为.LIB文件仅供链接器知道物理库名,但符号仅在加载时解析),而在Linux中除了链接到动态链接器之外的所有内容都在加载时发生。

动态链接通常是指在运行时手动打开DLL文件(例如使用LoadLinrary()),在这种情况下,负担完全在程序员身上。

答案 8 :(得分:0)

在共享库中,例如.dll .dylib.so,有一些关于符号名称和地址的信息,如下所示:

------------------------------------
| symbol's name | symbol's address |
|----------------------------------|
| Foo           | 0x12341234       |
| Bar           | 0xabcdabcd       |
------------------------------------

加载函数(例如LoadLibrarydlopen)加载共享库并使其可供使用。

GetProcAddressdlsym找到您符号的地址。例如:

HMODULE shared_lib = LoadLibrary("asdf.dll");
void *symbol = GetProcAddress("Foo");
// symbol is 0x12341234

在Windows中,有.lib个文件可供使用.dll。当您链接到此.lib文件时,您不需要致电LoadLibraryGetProcAddress,只需使用共享库的功能,就好像他们需要一样&#34;正常&#34;功能。它怎么样?

实际上,.lib包含导入信息。它是这样的:

void *Foo; // please put the address of Foo there
void *Bar; // please put the address of Bar there

当操作系统加载程序时(严格来说,模块),操作系统会自动执行LoadLibraryGetProcAddress

如果您编写Foo();等代码,编译器会自动将其转换为(*Foo)();。所以你可以像使用它们一样使用它们#34;正常&#34;功能