【问题标题】:Why does ld need library that my executable depends on?为什么 ld 需要我的可执行文件所依赖的库?
【发布时间】:2019-05-16 10:22:19
【问题描述】:

我正在尝试使用以下命令构建我的可执行文件(取决于库 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)。

【问题讨论】:

  • 我没有运行它——我只是在构建它。可能有未解析的符号,对吧?
  • “拥有所有符号”是什么意思?我有所有的符号——它们在头文件中声明。这足以编译源文件并将目标文件放入可执行文件中。
  • 那你为什么要加-lutils?如果所有符号都在标头中,则无需链接库。但如果它们不是,那么你需要链接它。
  • @πάνταῥεῖ 我认为关键是utils 是一个共享库,只有在需要它的可执行文件运行时才需要它。严格来说,编译只需要头文件中的信息,所以这种行为看起来就像一个额外的安全检查。
  • @Alexey “选项 -utils 会导致链接器做什么?它真的会出于某种原因访问这个库吗?” 除了选项应该是 -lutils 它将尝试在您使用 -L 选项指定的路径中查找名为 libutils.a 的库。甚至运行时链接库也需要作为存根链接。

标签: c++ linux g++ ld


【解决方案1】:

一般来说,ELF 链接器需要对所链接的共享对象进行足够准确的表示。它不必是实际工作的共享对象,只需对其进行足够接近的表示即可。一些东西绝对需要对象本身不可用的数据:

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

但是,如果您只使用 C 函数符号(不是数据符号,或 C++ 所需的各种符号)并且目标库不使用符号版本控制,您可以使用存根库链接。这是一个库,它定义了您需要的所有函数并具有适当的 soname,但这些函数只是虚拟的,实际上并没有做任何事情。

【讨论】:

    【解决方案2】:

    链接选项-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 被发现: libexecutable.so.dynamic 部分包含 DT_NEEDED 表示对libutils.so 的运行时依赖的结构。 libutils.so 将需要包含在任何包含libexecutable.so 的链接中,但此类链接的输出文件本身将包含仅对libexecutable.so 的运行时依赖项。

    • libutils.a 被发现: libexecutable.so 本身包含所有符号的定义 它使用由libutils.a 中的目标文件定义。2libexecutable.so 可以包含在后续链接中,而无需libutils.{so|a}

    在第二个链接中,libexecutable.so.dynamic 部分将表示运行时 对libutils.so 的依赖也不 文件将包含libutils.{so|a} 提供的任何符号的定义。 libutils.so 将(再次)需要包含在包含libexecutable.so 的后续链接中,但此类链接的输出文件将获得对libexecutable.solibutils.so 的独立运行时依赖项。

    但是,如果您在链接中指定 -lutils - 或任何链接 - 并且链接器找不到 libutils.{so|a} 在它的任何搜索目录中,你都会看到你观察到的错误,因为你告诉了链接器 输入一个文件,它对链接的影响只有在找到该文件的情况下才能确定和实施 - 并且找不到。


    [1] 可能会失败的尝试,因为it consumes libraries before the object files that refer to them

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

    【讨论】:

      猜你喜欢
      • 2016-01-09
      • 2020-06-01
      • 1970-01-01
      • 2018-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-27
      • 1970-01-01
      相关资源
      最近更新 更多