18

I need to build two 3rd party shared libraries, so their .so files will be reused by other projects. However, after build one of these libraries contains hardcoded path to another. This path is invalid on other machines and causes linker warnings. How can I prevent the full path from being embedded in the resulting .so files?

Details:

First library source: ~/dev/A
Second library source: ~/dev/B

Both of them have configure script to generate make files. Library B depends on A. So, first I build A:

$ ~/dev/A/configure --prefix=~/dev/A-install
$ make && make install

Then I build B:

$ ~/dev/B/configure --prefix=~/dev/B-install --with-A=~/dev/A-install
$ make && make install

Then I want to upload the contents of ~/dev/A-install and ~/dev/B-install to our file server, so other teams and build machines can use the binaries. But they get linker warnings when they try to use B:

/usr/bin/ld: warning: libA.so.2, needed by /.../deps/B/lib/libB.so, not found (try using -rpath or -rpath-link)

When I run ldd libB.so it gives:

...
libA.so.2 => /home/alex/dev/A-install/lib/libA.so.2

Obviously this path exists only on my machine and cannot be found on other machines.

How can I remove full hardcoded path from libB.so?

Thanks.

Alex Blekhman
  • 3,875
  • 2
  • 19
  • 18
  • How do you link? With libtool? In that case `patchelf` might help you get rid of the encoded path (the RPATH). – Benjamin Bannier Oct 27 '11 at 04:51
  • 1
    Why not use a prefix which is not dependent on your environment i.e `~`? Why not use something like say `/opt` as prefix which is not dependent on environment variable like `$HOME` (`~`)? You can try [`LD_PRELOAD`](http://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick) maybe – another.anon.coward Oct 27 '11 at 04:56
  • Ideally you could change the `configure` script to prevent this problem. If you want to edit the libraries, I've heard good things about elfsh (http://www.eresi-project.org). – Beta Oct 27 '11 at 05:18
  • @another.anon.coward, I use ~ prefix here for clarity. In reality it's full path to some directory. But in any case, this directory is unique to my machine and not necessarily present on others. – Alex Blekhman Oct 27 '11 at 05:51
  • 1
    The `configure` script is designed to be used in this situation. Yes, it's not completely obvious from the documentation until you read it very carefully. – Jan Hudec Oct 27 '11 at 09:40

6 Answers6

6

You have to use --prefix value that will be valid in the runtime environment for both packages!

Than you override prefix or DESTDIR (prefix replaces the prefix, DESTDIR is prepended to it, but works more reliably) on the make command-line when installing. Like:

~/dev/A$ ./configure
~/dev/A$ make 
~/dev/A$ make install prefix=~/dev/A-install
~/dev/B$ ./configure --with-A=~/dev/A-install
~/dev/B$ make
~/dev/B$ make install prefix=~/dev/B-install

or (which is preferred and is how all package-building tools use it):

~/dev/A$ ./configure
~/dev/A$ make 
~/dev/A$ make install DESTDIR=~/dev/A-install
~/dev/B$ ./configure --with-A=~/dev/A-install/usr/local
~/dev/B$ make
~/dev/B$ make install prefix=~/dev/B-install

because this way you are installing to ~/dev/A-install/$prefix, so with default prefix ~/dev/A-install/usr/local. The advantage of this later option is, that if you redefine some specific installation paths without refering to prefix (say --sysconfdir=/etc), DESTDIR will still get prepended to it, while it won't be affected by prefix.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
3
-Wl,-rpath,~/deps/A/lib:~/deps/B/lib:~/dev/MyApp/bin

This linker option is responsible for saving the path inside the library. You need somehow to remove this.

See with ./configure --help if there's some option that could influence this. Another option is to edit manually the makefile and remove this option.

== edit2 == One more thing

-L~/deps/A/lib -L~/deps/B/lib ~/deps/A/lib/libA.so ~/deps/B/lib/libB.so

If libA.so and libB.so don't have SONAME, linking them like "~/deps/A/lib/libA.so" will also cause saving the path. Soname is set using -Wl,-soname,<soname> linker option when building shared library.

If soname is set in the shared library, linking it using "~/deps/A/lib/libA.so" form is ok.

Like Jan mentioned in the comments, the better way is using "-Llibrary/path -llibrary_name" without rpath.

-L~/deps/A/lib -L~/deps/B/lib -lA -lB
Dmitry Yudakov
  • 15,364
  • 4
  • 49
  • 53
  • IIRC the -Lpath -lname does *not* encode the full path while `path/name` (no `-l`; you can't write `-lpath/name`) *does* encode it. So the `-L` + `-l` combination must be used the the `-Wl,-rpath` must not be used, because that adds the path back after use of `-l` avoided it. – Jan Hudec Oct 27 '11 at 09:27
  • Anyway, it's libtool that adds the rpath and it does so so that things work in the more common case where you want to install things in your home. – Jan Hudec Oct 27 '11 at 09:42
  • @Jan Hudec I didn't say that -L/-l are the problem (rpath is), just mentioned that there's some unneeded duplication of library path in several parameters. Anyway, removing the edit to avoid confusion. – Dmitry Yudakov Oct 27 '11 at 11:23
1

When I run ldd libB.so it gives:

libA.so.2 => /home/alex/dev/A-install/lib/libA.so.2

The low-level solution to this problem is to use the option "-soname=libA.so" when you link the libA.so library. By having SONAME defined for a shared object, the linker will not embed absolute paths when linking against that shared object.

The OP is using "configure", so this isn't an easy solution to implement unless he is willing to go into the bowels of the Makefile generated by the configure script.

deltamind106
  • 638
  • 7
  • 19
1

Shared libraries and executables have a list of directories to look for shared libraries, in addition to the list in the operating system's configuration. RPATH is used to add shared library search paths, used at runtime. If you want a relative path to be used in RPATH, there is a special syntax that most Linux/UNIX (but not AIX) systems support - $ORIGIN or ${ORIGIN}.
$ORIGIN will expand at runtime to the directory where the binary resides - either the library or executable.

So if you put executable binaries in prefix/bin/ and shared libraries in prefix/lib/ you can add an entry to RPATH like ${ORIGIN}/../lib and this will expand at runtime to prefix/bin/../lib/

It's often a little trick to get the syntax correct in a Makefile, because you have to escape the $ in ORIGIN so it will not be expanded. It's typical to do this in a Makefile:

 g++  -Wl,-rpath=\$${ORIGIN}/../lib

Both Make and the shell will want to look in your environment for a variable called ORIGIN - so it needs to be double-escaped.

Robert Boehne
  • 271
  • 3
  • 11
0

I just got caught out thinking I had the same problem. None of the above answers helped me. Whenever I tried

ldd libB.so 

I would get in the output:

libA.so.1 => /home/me/localpath/lib/libA.so.1.0

and so I thought I had a hardcoded path. Turns out that I had forgotten I had LD_LIBRARY_PATH set for local testing. Clearing LD_LIBRARY_PATH meant that ldd found the correct installed library in /usr/lib/

David Woo
  • 749
  • 4
  • 13
-1

Perhaps using the -rpath and -soname options to ld could help (assuming a binutils or binutils.gold package for ld on a recent Linux system)?

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547