Alexey
Alexey

Reputation: 800

Exclude dynamic dependencies from build command?

Let's suppose we have a library libutils.so:

ldd libutils.so
...
libdependency.so
...

Let's further suppose that we need to build an application:

g++ appliation.cpp -lutils -o application

May we omit -ldependency in the above command or must we write:

g++ appliation.cpp -lutils -ldependency -o application

Upvotes: 1

Views: 416

Answers (1)

Mike Kinghan
Mike Kinghan

Reputation: 61192

May we omit -ldependency in the above command

If you control the linkage of libutils.so itself, yes you can. An illustration:

main.c

extern void foo(void);

int main(void)
{
    foo();
    return 0;
}

foo.c

extern void bar(void);

void foo(void)
{
    bar();
}

bar.c

#include <stdio.h>

void bar(void)
{
    puts(__func__);
}

We'll make a program that depends on libfoo.so, which depends on libbar.so. Make the object files:

$ gcc -Wall -c -fPIC foo.c bar.c
gcc -Wall -c main.c

Now link libbar.so the No Frills way:

$ gcc -shared -o libbar.so bar.o

Next link libfoo.so like this:

$ gcc -shared -o libfoo.so foo.o -L. -lbar -Wl,-rpath=$(pwd)

The effect of the -rpath linker option is:

-rpath=dir

Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly included in the link; see the description of the -rpath-link option. If -rpath is not used when linking an ELF executable, the contents of the environment variable LD_RUN_PATH will be used if it is defined.

The result is that:

$ objdump -x -j .dynamic libfoo.so | egrep '(RUNPATH|NEEDED)'
  NEEDED               libbar.so
  RUNPATH              /home/imk/develop/so/scrap

libfoo.so has a NEEDED entry inscribed in its .dynamic section saying that the library has a runtime dependency on libbar.so. Likewise it has a RUNPATH entry there saying that runtime dependencies may be searched for in /home/imk/develop/so/scrap That's just the pwd where I did the linkage: it doesn't have to be that, as long as is indeed a directory where libbar.so can be found when the linker or loader comes looking for it.

This information can be read by the linker, when libbar.so is linked with something else, and by the loader at runtime. So finally I can link prog like this:

$ gcc -o prog main.o -L. -lfoo -Wl,-rpath=$(pwd)

I don't need to mention -lbar, because libfoo.so itself provides the linker with the information that libfoo.so depends on libbar.so, and where to look for it.

Since I also passed -rpath=$(pwd) in the linkage of prog, we see that prog will provide this information

$ objdump -x -j .dynamic prog | egrep '(RUNPATH|NEEDED)'
  NEEDED               libfoo.so
  NEEDED               libc.so.6
  RUNPATH              /home/imk/develop/so/scrap

to the runtime loader: prog needs libfoo.so, and it can be looked for in /home/imk/develop/so/scrap. When the loader finds libfoo.so and loads it, it will discover from it that:

  NEEDED               libbar.so
  RUNPATH              /home/imk/develop/so/scrap

and will in turn find and load libbar.so, which will enable it to resolve all symbols referred to in the process under construction. Consequently, prog can be run immediately:

$ ./prog
bar

I didn't have to pass -rpath=$(pwd) in the linkage of prog. But if I hadn't:

$ gcc -o prog main.o -L. -lfoo
$ ./prog
./prog: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

the loader wouldn't know where to find libfoo.so. See:

$ ldd prog
    linux-vdso.so.1 (0x00007ffffcc35000)
    libfoo.so => not found
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4d1aff9000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f4d1b5ec000)

And then I'd have to resort to:

$ export LD_LIBRARY_PATH=.
$ ldd prog
    linux-vdso.so.1 (0x00007fff964dc000)
    libfoo.so => ./libfoo.so (0x00007fc2a7f35000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc2a7b44000)
    libbar.so => ./libbar.so (0x00007fc2a7942000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fc2a8339000)    
$ ./prog
bar

Later

it is still a little bit unclear whether the presence of libdependency.so among the output of ldd libutils.so is enough to omit -ldependencny during linkage

You would need to ask at least one and at most two questions about the output of ldd utils.so:-

  1. Does the ldd libutils.so output report the so-name libdependency.so at all?
  2. If Yes to 1, does it also resolve that so-name to an actual file?

If No to 1 then libdutils.so contains no informatation about its dependency of libdependency.so and you must specify -lutils -ldependency in any further linkage.

If Yes to 1 but No to 2 (i.e.ldd libutils.so reports libdependency.so => not found) then libutils.so has a NEEDED entry for the so-name libdependency.so but not a RUNPATH entry by which the linker or loader can resolve that so-name to any actual file. In that case again, you must link -lutils -ldependency if you link -lutils, so that the linker will then search for a file that resolves -ldependency. At least, you must do so as long as ldd libutils.so still reports libdependency.so => not found when you do the linkage. Read on...

If Yes to 1 and Yes to 2 then you can drop -ldependency in a further linkage provided it is run in the same environment in which you ran ldd libutils.so

That caveat is needed because if ldd libutils.so resolves libdependency.so, all you know is that ldd was able to resolve libdependency.so using the loader's search algorithm:-

  • The LD_LIBRARY_PATH environment variable (in the active shell), lists a directory in which libdependency.so is found, or
  • libutils.so provides a RUNPATH in which libdependency.so is found, or
  • libdependency.so is found in one of the directories listed in /etc/ld.so.conf (or the recursive include-expansion thereof), or
  • libdependency.so is found in one of the loader's trusted search directories, /lib and /usr/lib

If ldd can resolve libdependency.so in one of those four ways, then the linker will be able to do it the same way, as long as that way still succeeds when you do the linkage.

So going back to my example, and my linkage:

$ gcc -shared -o libfoo.so foo.o -L. -lbar -Wl,-rpath=$(pwd)

After that, thanks to -rpath=$(pwd). I can link prog like:

$ gcc -o prog main.o -L. -lfoo

without mentioning -lbar, and it succeeds. Now I link libfoo.so instead without an -rpath:

$ gcc -shared -o libfoo.so foo.o -L. -lbar

after which:

$ objdump -x -j .dynamic libfoo.so | egrep '(RUNPATH|NEEDED)'
  NEEDED               libbar.so

there's no RUNPATH anymore, and consequently:

$ ldd libfoo.so
    linux-vdso.so.1 (0x00007ffda05e6000)
    libbar.so => not found

because the loader can't resolve libbar.so in any other way either.

Now I can no longer link prog without -lbar:

$ gcc -o prog main.o -L. -lfoo
/usr/bin/ld: warning: libbar.so, needed by ./libfoo.so, not found (try using -rpath or -rpath-link)
./libfoo.so: undefined reference to `bar'

But if I do:

$ export LD_LIBRARY_PATH=$(pwd)

then:

$ ldd libfoo.so 
    linux-vdso.so.1 (0x00007ffe56d1e000)
    libbar.so => /home/imk/develop/so/scrap/libbar.so (0x00007fd2456e8000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd2452f7000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fd245aec000)

libfoo.so's dependency libbar.so is resolved by the loader, using the LD_LIBRARY_PATH, and in the same way by the linker:

$ gcc -o prog main.o -L. -lfoo; echo Done
Done

And if I clear LD_LIBRARY_PATH again:

$ unset LD_LIBRARY_PATH
$ gcc -o prog main.o -L. -lfoo; echo Done
/usr/bin/ld: warning: libbar.so, needed by ./libfoo.so, not found (try using -rpath or -rpath-link)
./libfoo.so: undefined reference to `bar'
collect2: error: ld returned 1 exit status
Done

back to failure.

Upvotes: 2

Related Questions