Understanding Linker Error: DSO missing from command line

5.5k Views Asked by At

So lately, I have been trying to better understand how the symbol resolution with shared libraries works. I have created 2 shared objects (libfoo and libbar) and 1 executable (test). Consider following set of programs:

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 depends on libfoo, and test depends on both libfoo and libbar:

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

Now while building test, I deliberately don't provide the direct dependency on 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)

As we can see that test needs symbol foo and depends on libbar which in turn depends on libfoo and has the required symbol. So my question is why can't the linker resolve the symbol? Shouldn't it be able to scan all the dependencies and link to make the executable?

1

There are 1 best solutions below

3
On

In order to link your executable you will need to add -lfoo in your command line. The libraries passed by command line and the ones "passed" by rpath will not have the same treatments. The ones passed by command lines will be treated by open_input_bfds while the onse "passed" by rpath will be treated by ldemul_after_open which is gldelf_x86_64_after_open for x86.
The error you are seeing is coming from bfd_elf_link_add_symbols and it is a choice of gnu. in this function you have this code:

      /* 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;
    }

And from the change log:

  • elflink.c (elf_link_add_object_symbols): Check DYN_AS_NEEDED, DYN_DT_NEEDED and DYN_NO_NEEDED bits to see if a DT_NEEDED entry is needed. Issue an error if a DT_NEEDED entry is needed for a file marked DYN_NO_NEEDED.

the fact is that libfoo.so will be marked as needed by libbar.so and not by test.o . There is a dt_needed structure that will be created in gldelf_x86_64_search_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 )

Also if you don't want to add -lm, you can add the weak attribute to foo in test.c extern void __attribute__((weak)) foo(void); and the linker will not complain anymore. As mentioned earlier , it a gnu choice ( and i did not find any explanation in the commentary, documentation or change log). But only in my opinion it is a protection to avoid picking the wrong symbol.