为什么ld需要我的可执行文件依赖的库?

时间:2018-12-15 10:57:37

标签: c++ linux g++ ld

我正在尝试使用以下命令构建可执行文件(取决于库utils.so)

g++ -L/path/to/libutils -lutils -I/path/to/utils_headers executable.cpp -o executable

实际上我没有utils.so-只有utils库的头文件。

我遇到了错误:

ld: cannot find -lutils

链接程序是否真的需要访问我的可执行文件所依赖的所有库才能生成我的可执行文件?如果可以,那么我想知道为什么需要访问它们。

我的可执行文件是共享库。我确定utils lib的头文件足以构建它(即没有utils.so)。

2 个答案:

答案 0 :(得分:3)

默认情况下,链接选项-lutils指导链接器进行搜索, 首先在指定的库搜索目录(-Ldir)中,然后 在其默认搜索目录中,对于文件libutils.so( 共享库)或libutils.a(静态库),最好使用libutils.so 如果两者都在同一搜索目录中。

如果找到了这样的文件,则链接器将停止搜索并添加该文件 链接的输入文件,是否解析其中的任何引用 链接。链接器无法知道文件是否解析任何引用 如果没有输入文件。

如果未找到此类文件,则链接器将给出错误:cannot find -lutils。因为 您告诉它找到libutils.{so|a},但找不到。

您说:

  

我的可执行文件是共享库

但不是。您的编译链接命令:

$ g++ -L/path/to/libutils -lutils -I/path/to/utils_headers executable.cpp -o executable

并非试图链接共享库。试图链接程序。 1

这是尝试链接共享库的方法:

$ g++ -shared -I/path/to/utils_headers -o libexecutable.so executable.cpp -L/path/to/libutils -lutils

您不能使用未解析的引用链接程序。但是您可以链接共享库 带有未解决的引用。

因此,您可以像这样链接libexecutable.so,也可以像这样简单地链接它:

$ g++ -shared -I/path/to/utils_headers -o libexecutable.so executable.cpp

这是两个不同的链接:如果成功,它们将产生不同的输出文件。

在第一个链接中,某些符号(假设)将解析为libutils.solibutils.a中提供的定义 (找到的任何一个),这将通过以下方式反映出来:

    找到
  • libutils.so .dynamic的{​​{1}}部分包含libexecutable.so 表示对DT_NEEDED的运行时依赖性的结构。 libutils.so将需要包含在任何包含libutils.so的链接中,但是这种链接的输出文件本身将仅包含对libexecutable.so的运行时依赖性。

  • 找到
  • libexecutable.so libutils.a本身包含所有符号的定义 它使用libexecutable.so中的目标文件定义的。 2 libutils.a可以包含在后续链接中,而无需libexecutable.so

在第二个链接中,libutils.{so|a}的{​​{1}}部分将表示运行时 对.dynamic nor 的依赖将使文件包含libexecutable.so提供的任何符号的定义。 (再次)需要将libutils.so包括在包含libutils.{so|a}的后续链接中,但是这种链接的输出文件将获得对libutils.so和{{1}的独立运行时依赖关系}。

但是,如果您在链接(或任何链接)中指定libexecutable.so,则链接程序将找不到libexecutable.so 在其任何搜索目录中,然后您都会看到所观察到的错误,因为您告诉链接器 输入一个文件,只有在找到该文件的情况下,才能确定该文件对链接的影响,并且无法找到该文件。


[1]由于it consumes libraries before the object files that refer to them

而可能失败的尝试

[2]请参见static-libraries以了解 为什么。

答案 1 :(得分:2)

通常,ELF链接器需要足够准确地表示链接到的共享库。它不一定是实际工作的共享库,而只是一个足够接近的表述。 绝对需要一些在对象本身中不可用的数据:

  • 编译C程序时,对不完整类型的全局数据对象的引用不包含大小信息。除非链接器从某处获取大小信息,否则它无法将其放入数据段。默认情况下(在编译包括PIE的可执行文件时),由于编译器用于重新编译对全局数据对象的访问所使用的重定位,因此需要在许多目标的数据段中分配对象。
  • 类似地,如果链接编辑器的信息不足,它可能会错误地对齐全局数据对象。
  • 许多库都使用符号版本控制。仅当链接编辑器可以看到共享库时,符号版本信息才可用。如果缺少该信息,则链接编辑器将不会发出符号版本,从而指示动态链接程序在运行时将符号绑定到基本版本,从而导致细微的错误。

但是,如果仅使用C函数符号(而不是数据符号或C ++所需的各种符号),并且目标库不使用符号版本控制,则可以将存根库用于链接。这是一个库,它定义了您需要的所有功能,并具有适当的名称,但是这些功能只是虚拟实体,实际上不执行任何操作。