RPATH and RUNPATH

Published Friday October 28th, 2011
6 Comments on RPATH and RUNPATH
Posted in C++ | Tags: , , , , , , , ,

The DT_RPATH tag is commonly set in executables that depend on libraries that can’t be found in the default locations. For instance Qt Creator comes with its own copy of the Qt libraries and has an rpath to the directory that contains them. It’s also useful when you build your own version of Qt without installing it globally: Running binaries like qmlviewer will ‘just work’ because they contain an rpath to the Qt libraries they were compiled with.

If these files didn’t have an rpath set, you’d need to explicitly set LD_LIBRARY_PATH before starting them – or write a script that does that for you. And since LD_LIBRARY_PATH is an environment variable that comes with its own set of problems.

Unforunately, there’s some confusion about the dynamic loader’s search paths on glibc based systems. In particular on the differences between RPATH and RUNPATH. Documentation on the subject is lacking: the first google hit is incorrect, probably outdated. The man pages on ld and ld.so are incomplete and the one on dlopen is wrong about how RUNPATH applies. The debian wiki starts out well, but has the same error as the dlopen page and later contradicts itself.

An additional source of confusion is that, depending on your distribution, the -rpath argument in ‘ld’ behaves differently. For some it generates a DT_RPATH and for others DT_RPATH and DT_RUNPATH.

Ok, that’s not great, but what’s the problem?

In short: Setting DT_RUNPATH (or DT_RUNPATH and DT_RPATH) in your application is not enough to guarantee that it will prefer to link to libraries in the directory you specified.

A concrete example where this has caused problems:

  • Qt Creator links to the QtGui it shipped
  • that dlopens the system’s KDE plugin, which (indirectly) depends on QtDBus
  • if the Qt Creator binary has a RPATH and no RUNPATH: the QtDBus that came with Creator is loaded
  • if the Qt Creator binary has a RUNPATH: the system’s QtDBus is loaded and Creator aborts

The error would be something like “Cannot mix incompatible Qt library (version 473) with this library (version 474)”. Because even though the versions are binary compatible, you cannot mix them. Some code in QtDBus may rely on private API in QtCore that may have changed or have been added in 4.7.4.

This can happen for all applications that link to their own QtGui and are run on a system with KDE. But the problem is not limited to KDE either: any plugin that pulls in previously unloaded Qt libraries can look up the wrong ones.

Why does this happen?

Some experiments and reading the glibc source code (check _dl_map_object in elf/dl-load.c) indicate that this is the library search order:

Unless loading object has RUNPATH:
    RPATH of the loading object,
        then the RPATH of its loader (unless it has a RUNPATH), ...,
        until the end of the chain, which is either the executable
        or an object loaded by dlopen
    Unless executable has RUNPATH:
        RPATH of the executable
LD_LIBRARY_PATH
RUNPATH of the loading object
ld.so.cache
default dirs

That explains the behavior: We were depending on plugin loads checking the RPATH of the executable. The executable’s RUNPATH is not used for finding indirect library dependencies.

What should I do?

When you ship binaries, either use RPATH and not RUNPATH or ensure LD_LIBRARY_PATH is set before they are run. When you build Qt on a system where ‘ld’ defaults to –enable-new-dtags (and thus -rpath adds a RUNPATH), be aware that you might have to set LD_LIBRARY_PATH before starting applications built against that Qt version.

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in C++ | Tags: , , , , , , , ,

6 comments

DT_RPATH is considered a flaw in the ELF specification. It’s not recommended relying on them.

Eli says:

@Thiago – so what should be used instead of DT_RPATH?

ckamm says:

@Thiago: Yes, I should have mentioned that DT_RPATH is deprecated. However, it also does exactly what one would want in the cases I outlined. And some distributions still default to –disable-new-dtags today, twelve years after RPATH was deprecated. I don’t see it going away any time soon.

As for alternatives: It is impossible to make everything work with DT_RUNPATH alone. You would need to go for setting LD_LIBRARY_PATH.

David Faure says:

Interesting. I always considered that RPATH was buggy (and therefore happy that it’s deprecated) because it made LD_LIBRARY_PATH useless; i.e. as a user I couldn’t control the libs being used if the app had decided to use some libs at compile time. RUNPATH fixes this. But the QtCreator+plugin example shows that in some cases RPATH functionality is actually better πŸ™‚

Looks like there is a use case for both after all.

Yuriy says:

RPATH – we remove. What remains?

LD_LIBRARY_PATH=/usr/lib:/usr/Qt-4.8.0/lib
ls /usr/lib # libQtCore.so (from Qt-4.6.2)
ls /usr/Qt-4.8.0/lib # libQtCore.so

Run QtApp-4.8.0 ???
Who and whom does useless?

There are some more considerations to make: dlopen, suid, fork, etc

Commenting closed.

Get started today with Qt Download now