【问题标题】:Understanding Linker Error: DSO missing from command line了解链接器错误:命令行中缺少 DSO
【发布时间】:2021-03-23 22:40:35
【问题描述】:

所以最近,我一直试图更好地理解共享库的符号解析是如何工作的。我创建了 2 个共享对象(libfoolibbar)和 1 个可执行文件(test)。考虑以下一组程序:

foo.c

#include <stdio.h>
void foo()
{
    puts(__func__);
}

bar.c

#include <stdio.h>
extern void foo(void);
void bar()
{
    puts(__func__);
    foo();
}

test.c

#include <stdio.h>
extern void foo(void);
extern void bar(void);
int main()
{
    puts(__func__);
    foo();
    bar();
    return 0;
}

libbar 依赖于libfoo,而test 依赖于libfoolibbar

gcc -c -Wall -fPIC foo.c bar.c
gcc -shared -o libfoo.so foo.o
gcc -shared -o libbar.so bar.o -L. -lfoo

现在在构建测试时,我故意不提供对 libfoo 的直接依赖:

cheshar@editsb:~/2-test $ gcc -o test test.o -L. -lbar -Wl,-rpath-link=.
    /usr/bin/ld: test.o: undefined reference to symbol 'foo'
    ./libfoo.so: error adding symbols: DSO missing from command line
    collect2: error: ld returned 1 exit status

cheshar@editsb:~/2-test $ ldd libbar.so
    linux-vdso.so.1 =>  (0x00007ffe27bf7000)
    libfoo.so => not found
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7d203e0000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f7d209ac000)

正如我们所见,test 需要符号 foo 并依赖于 libbar,而 libbar 又依赖于 libfoo 并具有所需的符号。所以我的问题是为什么链接器不能解析符号?是不是应该可以扫描所有的依赖和链接来生成可执行文件?

【问题讨论】:

    标签: gcc linker shared-libraries ldd


    【解决方案1】:

    为了链接您的可执行文件,您需要在命令行中添加-lfoo。 命令行传递的库和 rpath “传递”的库不会有相同的处理。通过命令行传递的那些将由open_input_bfds 处理,而由rpath“传递”的onse 将由ldemul_after_open 处理,对于x86 来说是gldelf_x86_64_after_open
    您看到的错误来自bfd_elf_link_add_symbols,它是 gnu 的选择。在这个函数中你有这个代码:

          /* A symbol from a library loaded via DT_NEEDED of some
         other library is referenced by a regular object.
         Add a DT_NEEDED entry for it.  Issue an error if
         --no-add-needed is used and the reference was not
         a weak one.  */
          if (old_bfd != NULL
          && (elf_dyn_lib_class (abfd) & DYN_NO_NEEDED) != 0)
        {
          _bfd_error_handler
            /* xgettext:c-format */
            (_("%B: undefined reference to symbol '%s'"),
             old_bfd, name);
          bfd_set_error (bfd_error_missing_dso);
          goto error_free_vers;
        }
    

    从更改日志中:

    • elflink.c (elf_link_add_object_symbols):检查 DYN_AS_NEEDED, DYN_DT_NEEDED 和 DYN_NO_NEEDED 位查看是否有 DT_NEEDED 需要输入。如果需要 DT_NEEDED 条目,则发出错误 对于标记为 DYN_NO_NEEDED 的文件。

    事实上 libfoo.so 将被 libbar.so 标记为需要,而不是 test.o 。 gldelf_x86_64_search_needed 中将创建一个 dt_needed 结构。

    lang_process
    -> ldemul_after_open ()=gldelf_x86_64_after_open
    -->gldelf_x86_64_search_needed (dt_needed created)
    --->gldelf_x86_64_try_needed
    ---->bfd_elf_link_add_symbols
    ----->elf_link_add_object_symbols ( the error )
    

    另外,如果你不想添加-lm,你可以在test.c extern void __attribute__((weak)) foo(void); 的foo 中添加weak 属性,这样链接器就不会再抱怨了。如前所述,这是一个 gnu 选择(我在评论、文档或更改日志中没有找到任何解释)。但仅在我看来,这是一种避免选择错误符号的保护措施。

    【讨论】:

    • "事实上 libfoo.so 将被 libbar.so 标记为需要,而不是 test.o" 所以你的意思是只有提供的库-L&lt;path&gt; -l&lt;libname&gt; 将在 SO/可重定位目标文件中标记为 DT_NEEDED?即使libbar 在动态部分中将libfoo 标记为需要,bar.o 中也不存在相同的情况。其次,我理解您所说的是“这就是它的完成方式”,但不明白为什么要这样做。
    • 使用 '-l -L' 传递的库由在 gldelf_x86_64_search_needed 之前出现的 open_input_bfd 函数处理。需要的将被标记为 DT_NEEDED。此示例中的 libfoo 稍后将使用结构 dt_needed 传递,该结构将包含两个字段:needed=libfoo.so 和 by=libbar。但是,代码或更改日志中没有提到为什么不能从 libfoo.so 中选择 foo 的原因,除非它是弱的。也许你可以直接在邮件列表中询问。但在我看来,这是为了确保使用正确的符号(这就是弱者被接受的原因)。
    猜你喜欢
    • 2016-02-18
    • 1970-01-01
    • 2016-01-20
    • 2019-01-12
    • 1970-01-01
    • 2016-11-17
    • 2014-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多