How to build a shared library (.so) without hardco

2019-02-06 09:20发布

问题:

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.

回答1:

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.



回答2:

-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


回答3:

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.



回答4:

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/



回答5:

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.



回答6:

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)?