动态链接是如何工作的,它的用法以及如何以及为什么要制作dylib



我已经阅读了几篇关于堆栈溢出的文章,并在线阅读了关于动态链接的文章。这就是我从所有这些读数中得到的——

动态链接是为了充分利用系统的虚拟内存而采用的一种优化技术。一个进程可以与其他进程共享其页面。例如,libc++需要与所有C++程序链接,但它可以通过共享虚拟页面与许多进程动态链接,而不是将可执行文件复制到每个进程。

然而,这让我想到了以下问题

  1. 编译C++程序时。它需要引用C++库函数和代码(例如线程库的代码)。编译器如何使可执行文件具有这些引用?这不会导致编译器和操作系统之间的循环依赖关系吗?因为编译器必须在可执行文件中引用动态库
  2. 您将如何以及何时使用动态库?你是怎么做的?用于从标准*.cpp文件生成此类文件的特定编译命令是什么
  3. 通常,当我安装库时,会有一个lib/目录,其中包含*.a文件和*.dylib(在macOSX上)文件。我如何知道像使用常规*.o文件那样静态链接到哪些,以及应该动态链接到哪些?我假设*.dylib文件是动态库。使用哪个编译器标志链接到这些
  4. -L-l标志的作用是什么?例如,在命令行上指定-lusb标志意味着什么

如果你觉得这个问题一次问了太多问题,请告诉我。我完全可以把这个问题分成多个问题。我只是一起问他们,因为我觉得一个问题的答案会引出另一个问题。

编译C++程序时。它需要引用C++库函数和代码(例如库的代码)。

假设我们有一个名为libdyno.so的假设共享库。您最终将能够使用objdumpnm窥视它的内部。

objdump --syms libdyno.so

今天,您可以在任何共享库的系统上执行此操作。MAC上的objdump被称为gobjdump,并在binutils包中附带冲泡。在mac上试试这个。。。

gobjdump --syms /usr/lib/libz.dylib

现在,您可以看到这些符号包含在共享对象中。当你使用共享对象link时,你通常会使用类似的东西

g++ -Wall -g -pedantic -ldyno DynoLib_main.cpp -o dyno_main

注意该命令中的-ldyno。这是在告诉编译器(实际上是链接器ld)在通常查找它们的任何位置查找一个名为libdyno.so的共享对象文件。一旦它找到了那个物体,就可以找到它需要的符号。没有循环依赖关系,因为开发人员要求通过指定-l标志来加载动态库。

您将如何以及何时使用动态库?你是怎么做的?就像在什么是用于从标准.cpp文件

创建一个名为DynoLib.cpp 的文件

#include "DynoLib.h"
DynamicLib::DynamicLib() {}
int DynamicLib::square(int a) {
  return a * a;
}

创建一个名为DynoLib.h 的文件

#ifndef DYNOLIB_H
#define DYNOLIB_H
class DynamicLib {
  public:
  DynamicLib();
  int square(int a); 
};
#endif

将它们编译为共享库,如下所示。这是特定于linux的。。。

g++ -Wall -g -pedantic -shared -std=c++11 DynoLib.cpp -o libdyno.so

你现在可以使用我之前给出的命令来检查这个对象,即

objdump --syms libdyno.so

现在创建一个名为DynoLib_main.cpp的文件,该文件将与libdyno.so链接,并使用我们刚刚在其中定义的函数

#include "DynoLib.h"    
#include <iostream>     
using namespace std;
int main(void) {
  DynamicLib *lib = new DynamicLib();
  std::cout << "Square " << lib->square(1729) << std::endl;
  return 1;
}

按以下编译

g++ -Wall -g -pedantic -L. -ldyno DynoLib_main.cpp -o dyno_main
./dyno_main
Square 2989441

您还可以使用nm查看主二进制文件。在下面的内容中,我将查看其中是否有字符串square,即在二进制文件中以任何方式引用的libdyno.so中的符号。

nm dyno_runner |grep square
U _ZN10DynamicLib6squareEi

答案是肯定的。大写的U表示未定义,但这是我们前面创建的DynamicLib类中方形方法的符号名称。这个奇怪的名字是由于名字篡改,这是它自己的主题。

我如何知道要像使用正则表达式那样静态链接到哪些.o文件以及应该与哪些文件动态链接?

你不需要知道。您可以指定要链接的内容,并让编译器(和链接器等)完成工作。请注意,-l标志命名库,-L告诉它在哪里查找。这里有一篇关于编译器如何在这里找到东西的不错的文章

gcc链接选项-L:如何指定动态库的路径的替代方法

或者看看man ld

-L和-L标志是用来做什么的?具体说明意味着什么例如命令行上的-lusb标志?

请参阅上面的链接。这是来自man ld。。

-L searchdir

将路径searchdir添加到ld将搜索的路径列表中归档库和ld控制脚本。您可以在任何时候使用此选项次数。目录的搜索顺序如下它们是在命令行中指定的。上指定的目录在默认目录之前搜索命令行。全部-L选项应用于所有-l选项,而不考虑选项出现-L选项不影响ld搜索链接器的方式脚本,除非指定了-T选项。`

如果你设法来到这里,了解链接器是有回报的。它起着重要的作用,是大量混乱的根源,因为大多数人一开始都在处理编译器,并认为compiler == linker和这不是真的。

主要区别在于您的应用程序中包含了静态链接库。当你构建应用程序时,它们会被链接。动态库在运行时链接,因此您不需要将它们包含在应用程序中。如今,动态库通过在每个人的计算机上都有许多动态库来减少应用程序的大小。

动态库还允许用户在不重新构建客户端应用程序的情况下更新库。如果在你的应用程序中使用的库中发现了一个错误,并且它是静态链接的,你必须重新构建你的应用,并将其重新发布给所有用户。如果在动态链接的库中发现错误,您的所有用户只需要更新他们的库,您的应用程序不需要更新。

最新更新