Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between `-rpath-link` and `-L`?

The man for gold states:

  -L DIR, --library-path DIR
          Add directory to search path

  --rpath-link DIR
          Add DIR to link time shared library search path

The man for bfd ld makes it sort of sound like -rpath-link is used for recursively included sos.

ld.lld doesn't even list it as an argument.

Could somebody clarify this situation for me?

like image 738
lanza Avatar asked Mar 06 '18 19:03

lanza


People also ask

What is Rpath and Runpath?

Both rpath and runpath are used to specify directories to search for shared libraries(dynamic libraries). If you are not sure what shared libraries is, I have a story written on Static vs Dynamic libraries. Shared libraries are libraries which are not bundles along with the executable. They are loaded at the run time.

What does Rpath stand for?

@rpath stands for Runpath Search Path. In the Xcode, it's set with LD_RUNPATH_SEARCH_PATH setting. In ld command tool it's set with -rpath parameter when linking. So it's a search path for the linker. Runtime Search Path instructs the dynamic linker to search a list of paths in order, to locate the dynamic library.

What is Python Rpath?

rpaths is another path manipulation library. It is heavily inspired by Unipath and pathlib. It aims at total Python 2/3 and Windows/POSIX compatibility. To my knowledge, no other library can handle all the possible paths on every platform.

How do I specify an Rpath?

To create an RPATH entry, you simply tell it to the linker (not the compiler!), so for instance you could add to your ./configure call the string LDFLAGS=-Wl,-rpath,/my/software/base/my/lib/. libs to build and run against a specific version of your library.


2 Answers

Here is a demo, for GNU ld, of the difference between -L and -rpath-link - and for good measure, the difference between -rpath-link and -rpath.

foo.c

#include <stdio.h>

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

bar.c

#include <stdio.h>

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

foobar.c

extern void foo(void);
extern void bar(void);

void foobar(void)
{
    foo();
    bar();
}

main.c

extern void foobar(void);

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

Make two shared libraries, libfoo.so and libbar.so:

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

Make a third shared library, libfoobar.so that depends on the first two;

$ gcc -c -Wall -fPIC foobar.c
$ gcc -shared -o libfoobar.so foobar.o -lfoo -lbar
/usr/bin/ld: cannot find -lfoo
/usr/bin/ld: cannot find -lbar
collect2: error: ld returned 1 exit status

Oops. The linker doesn't know where to look to resolve -lfoo or -lbar.

The -L option fixes that.

$ gcc -shared -o libfoobar.so foobar.o -L. -lfoo -lbar

The -Ldir option tells the linker that dir is one of the directories to search for libraries that resolve the -lname options it is given. It searches the -L directories first, in their commandline order; then it searches its configured default directories, in their configured order.

Now make a program that depends on libfoobar.so:

$ gcc -c -Wall main.c
$ gcc -o prog main.o -L. -lfoobar
/usr/bin/ld: warning: libfoo.so, needed by ./libfoobar.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: warning: libbar.so, needed by ./libfoobar.so, not found (try using -rpath or -rpath-link)
./libfoobar.so: undefined reference to `bar'
./libfoobar.so: undefined reference to `foo'
collect2: error: ld returned 1 exit status

Oops again. The linker detects the dynamic dependencies requested by libfoobar.so but can't satisfy them. Let's resist its advice - try using -rpath or -rpath-link - for a bit and see what we can do with -L and -l:

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

So far so good. But:

$ ./prog
./prog: error while loading shared libraries: libfoobar.so: cannot open shared object file: No such file or directory

at runtime, the loader can't find libfoobar.so.

What about the linker's advice then? With -rpath-link, we can do:

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

and that linkage also succeeds. ($(pwd) means "Print Working Directory" and just "copies" the current path.)

The -rpath-link=dir option tells the linker that when it encounters an input file that requests dynamic dependencies - like libfoobar.so - it should search directory dir to resolve them. So we don't need to specify those dependencies with -lfoo -lbar and don't even need to know what they are. What they are is information already written in the dynamic section of libfoobar.so:-

$ readelf -d libfoobar.so

Dynamic section at offset 0xdf8 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libfoo.so]
 0x0000000000000001 (NEEDED)             Shared library: [libbar.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 ...
 ...

We just need to know a directory where they can be found, whatever they are.

But does that give us a runnable prog?

$ ./prog
./prog: error while loading shared libraries: libfoobar.so: cannot open shared object file: No such file or directory

No. Same as story as before. That's because -rpath-link=dir gives the linker the information that the loader would need to resolve some of the dynamic dependencies of prog at runtime - assuming it remained true at runtime - but it doesn't write that information into the dynamic section of prog. It just lets the linkage succeed, without our needing to spell out all the recursive dynamic dependencies of the linkage with -l options.

At runtime, libfoo.so, libbar.so - and indeed libfoobar.so - might well not be where they are now - $(pwd) - but the loader might be able to locate them by other means: through the ldconfig cache or a setting of the LD_LIBRARY_PATH environment variable, e.g:

$ export LD_LIBRARY_PATH=.; ./prog
foo
bar

rpath=dir provides the linker with the same information as rpath-link=dir and instructs the linker to bake that information into the dynamic section of the output file. Let's try that:

$ export LD_LIBRARY_PATH=
$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath=$(pwd)
$ ./prog
foo
bar

All good. Because now, prog contains the information that $(pwd) is a runtime search path for shared libraries that it depends on, as we can see:

$ readelf -d prog

Dynamic section at offset 0xe08 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libfoobar.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [/home/imk/develop/so/scrap]
 ...                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...

That search path will be tried after the directories listed in LD_LIBRARY_PATH, if any are set, and before the system defaults - the ldconfig-ed directories, plus /lib and /usr/lib.

like image 106
Mike Kinghan Avatar answered Oct 03 '22 19:10

Mike Kinghan


The --rpath-link option is used by bfd ld to add to the search path used for finding DT_NEEDED shared libraries when doing link-time symbol resolution. It's basically telling the linker what to use as the runtime search path when attempting to mimic what the dynamic linker would do when resolving symbols (as set by --rpath options or the LD_LIBRARY_PATH environment variable).

Gold does not follow DT_NEEDED entries when resolving symbols in shared libraries, so the --rpath-link option is ignored. This was a deliberate design decision; indirect dependencies do not need to be present or in their runtime locations during the link process.

like image 38
Cary Coutant Avatar answered Oct 03 '22 19:10

Cary Coutant