Recently I've been working on the Comedi ebuild in my Gentoo overlay, wrestling with the following error:
* QA Notice: The following files contain insecure RUNPATHs
* Please file a bug about this at http://bugs.gentoo.org/
* with the maintaining herd of the package.
* /usr/lib:/var/tmp/portage/sci-libs/comedilib-9999/work/comedilib-9999/lib/.libs usr/lib/ruby/site_ruby/1.8/i686-linux/comedi.so
While tracking this down the source of this error, I learned a lot about dynamic linking on Linux, so here's the condensed version.
RPATH
, RUNPATH
, and LD_LIBRARY_PATH
. The current state of
affairs is well summarized on the Debian wiki, which lists
the library search path:
- the
RPATH
binary header (set at build-time) of the library causing the lookup (if any) - the
RPATH
binary header (set at build-time) of the executable - the
LD_LIBRARY_PATH
environment variable (set at run-time) - the
RUNPATH
binary header (set at build-time) of the executable /etc/ld.so.cache
- base library directories (
/lib
and/usr/lib
)
There was a big dust-up between Debian and libtool back in 1999 when
libtool-generated RPATH
s caused problems during the libc5 to libc6
transition. The mailing list discussion makes for amusing and
informative reading, if you've got a spare hour or two ;). If not,
you should at least read the opening post, the description of
competing contracts, and Buddha Buck's description of how
linking works, although I imagine things might have changed
since then. The Debian / libtool compromise (don't set RPATH by
default for directories in the dynamic linker search path) was
implemented in libtool 1.5.2 (released in 2004, see the Debian
wiki), so this is not as big an issue as it once was.
By the way, it looks like RUNPATH
was added since 1999 as a version
of RPATH
that did not override LD_LIBRARY_PATH
, which is good,
since LD_LIBARY_PATH
gives you a way to link against libraries in,
say,
/var/tmp/portage/sci-libs/comedilib-9999/work/comedilib-9999/lib/.libs
to test your executable before installation.
Anyhow, issues with rpaths persist. Since it both hard to
predict all installation configurations at compile time, and tools to
change rpaths later on (i.e. chrpath and patchelf) aren't able
to increase the size of the rpath string on Linux (they can on
Solaris, because Solaris leaves a bit of padding at the end
of the dynamic string table in the compiled ELF file). This means
you will have trouble moving a library out of the standard library
path into some out-of-the-way location. However, in the more common
case of installing a library into the standard library path,
chrpath
is the tool you need, and it solved my comedilib QA issue.
Along the way, I ran across two other interesting posts by Diego Pettenò about not bundling libraries.
Setting RPATH
and RUNPATH
Most of the time, you'll want to avoid setting RPATH
and RUNPATH
and just use libraries in your usual linking path. However, sometimes
they are useful. For example, SimulAVR depends on the
AVR-specific libbfd
, which is hopefully not in your usual path. On
Gentoo, crossdev installs under /usr/lib/binutils/
:
$ binutils-config -c avr
avr-git
$ grep PATH /etc/env.d/binutils/avr-git
LIBPATH="/usr/lib/binutils/avr/git"
When you link against this library, you'll want to set RUNPATH
so
you don't have to remember to use LD_LIBRARY_PATH
every time you run
simulavr
. Of course, if you switch to a different binutils version
(e.g. not git
), you'll need to fix the RUNPATH
to point to the new
target (or just rebuild simulavr
against the new version).
Since you're probably not calling the linker directly when you build
simulavr
, you'll want to set some linker flags at configure time:
$ LDFLAGS=-Wl,-rpath=/usr/lib/binutils/avr/git,--enable-new-dtags ./configure …
The relevant linker flags are -rpath
and --enable-new-dtags
.
Without --enable-new-dtags
, you'll just set the RPATH
flag, which
is probably not what you want. With --enable-new-dtags
, you'll set
both RPAH
and RUNPATH
to the same value. From ld(1)
:
The
DT_RPATH
entries are ignored ifDT_RUNPATH
entries exist.
so setting both is the same as just setting RUNPATH
(except for
tools like chrpath
which are only designed to handle a single tag).
You can use readelf to see if the tags were set:
$ readelf --dynamic /usr/bin/simulavr | grep PATH
0x000000000000000f (RPATH) Library rpath: [/usr/lib/binutils/avr/git]
0x000000000000001d (RUNPATH) Library runpath: [/usr/lib/binutils/avr/git]
On Gentoo, --enable-new-dtags
has been the default since
2005, but explicitly including the flag can't hurt ;).