11

I have a Linux Qt program. I'd like it to preferentially use the (dynamic) Qt libraries in the executable's directory if they exist, otherwise use the system's Qt libs. RPATH to the rescue.

I add this line to the qmake's .pro file:

QMAKE_LFLAGS    += '-Wl,-rpath,\'\$$ORIGIN\''

and looking at the resulting executable with readelf I see:

0x000000000000000f (RPATH)              Library rpath: [$ORIGIN:/usr/local/Trolltech/Qt-5.2.0/lib]
0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN:/usr/local/Trolltech/Qt-5.2.0/lib]

Seems right, but ldd shows it's using the system version:

libQt5Core.so.5 => /usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5 (0x00007f2d2fe09000)

If I manually edit qmake's resulting Makefile to swap the order of the two rpaths, so $ORIGIN comes after /usr/local/..., I get the right behavior:

0x000000000000000f (RPATH)              Library rpath: [/usr/local/Trolltech/Qt-5.2.0/lib:$ORIGIN]
0x000000000000001d (RUNPATH)            Library runpath: [/usr/local/Trolltech/Qt-5.2.0/lib:$ORIGIN]

libQt5Core.so.5 => ./libQt5Core.so.5 (0x00007fb92aba9000)

My problem is with how qmake constructs the final LFLAGS variable. I can't figure out how to make it put my addition ($ORIGIN) after the system library. Any ideas?

Nejat
  • 31,784
  • 12
  • 106
  • 138
Mike Blackwell
  • 487
  • 1
  • 6
  • 12
  • Why would you like to prefer local libraries over system libraries? In a usual deployment scenario where you ship libraries with the binary, you have local only or system over local. – Simon Warta Dec 09 '14 at 17:03
  • I wouldn't do this for a production product, but it's useful for debugging, where the local library has some extra diagnostic output (for example). I realize I can just use something along the lines of LD_LIBRARY_PATH in a wrapper script, but I scurried down this QMAke / RPATH rabbit hole, and now I'm curious about how to make QMake do what I want... – Mike Blackwell Dec 09 '14 at 19:50

4 Answers4

14

You can add the following to your .pro file to force the dynamic linker to look in the same directory as your Qt application at runtime in Linux :

unix:{
    # suppress the default RPATH if you wish
    QMAKE_LFLAGS_RPATH=
    # add your own with quoting gyrations to make sure $ORIGIN gets to the command line unexpanded
    QMAKE_LFLAGS += "-Wl,-rpath,\'\$$ORIGIN\'"
}

If you want it to look in a subdirectory of the executable path, you can use :

QMAKE_LFLAGS += "-Wl,-rpath,\'\$$ORIGIN/libs\'"

Note that you should have the .so files with the exact same name in your application directory. For example you should copy libQt5Core.so.5.2.0 to your application directory with the name libQt5Core.so.5. Now the ldd shows the directory of the application.

You can also have libQt5Core.so.5.2.0 and a link to it with the name libQt5Core.so.5 in the application directory.

Nejat
  • 31,784
  • 12
  • 106
  • 138
  • This is close. Setting `QMAKE_FLAGS_RPATH=` to suppress the default was the key. Then I can do: `QMAKE_FLAGS += "-Wl,-rpath,\'/usr/local/Trolltech/Qt-5.2.0/libs\' -Wl,-rpath,\'\$$ORIGIN\'"` to get the right elements in RPATH in the right order. There's probably some variable that holds the system path I could use, but I couldn't find it... – Mike Blackwell Dec 10 '14 at 18:22
1

As far as my research can say, you can only add RPATH at the beginning of the list with QMake.

But if you are on Linux and can install chrpath, you can hack your way around that.

Add this block at the end of your .pro file

# Add spacing since chrpath cannot expand RPATH length
QMAKE_RPATHDIR = \
    /XYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXY1\
    /XYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXY2\
    /XYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXY3\
    /XYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXYXY4
QMAKE_POST_LINK += 'chrpath -r \'/my/qt/installation:\$$ORIGIN\' $$OUT_PWD/mybinaryname;'
Simon Warta
  • 10,850
  • 5
  • 40
  • 78
0

I'm taking a bit of a guess at what's happening, but it's based on knowing some of the odd behaviours of ld.

check for the presence of an LD_LIBRARY_PATH variable that will come into effect before the processing of a RUNPATH variable. Because of the presence of both RPATH and RUNPATH, the LD_LIBRARY_PATH rule comes into effect, so if it's set then unset it.

Secondly, I'd never expect to see:

libQt5Core.so.5 => ./libQt5Core.so.5 (0x00007fb92aba9000)

in the output of ldd, I would always see the expansion of $ORIGIN to the directory of the binary (maybe you shortened it?), so I would have expected:

libQt5Core.so.5 => /path/to/bin/./libQt5Core.so.5 (0x00007fb92aba9000)

Which means it sounds like the LD_LIBRARY_PATH expansion is .:/usr/local/Trolltech/Qt-5.2.0/lib, which to me sounds like you've got environmental overrides happening.

Anya Shenanigans
  • 91,618
  • 3
  • 107
  • 122
  • That is the verbatim output from ldd, no shortening involved. LD_LIBRARY_PATH is not set in any of these examples. – Mike Blackwell Dec 09 '14 at 19:45
  • Do you have *any* `LD_` environment variables set? do you get the same behaviour with `env -i ldd `? What linux distro are you using (trying to check for bugs)? – Anya Shenanigans Dec 09 '14 at 22:33
0

qmake would always append the QMAKE_RPATHDIR with the QT_INSTALL_LIBS internally defined in $(QT_DIR)/mkspecs/features/qt.prf file:

170:    relative_qt_rpath:!isEmpty(QMAKE_REL_RPATH_BASE):contains(INSTALLS, target):\
173:        QMAKE_RPATHDIR += $$relative_path($$[QT_INSTALL_LIBS], $$qtRelativeRPathBase())
175:        QMAKE_RPATHDIR += $$[QT_INSTALL_LIBS/dev]
179:!isEmpty(QMAKE_LFLAGS_RPATHLINK):!contains(QT_CONFIG, static) {
189:    QMAKE_RPATHLINKDIR *= $$unique(rpaths)

So to avoid your application using the QT library from system path, comment out the lines above which append the QMAKE_RPATHDIR and add QMAKE_RPATHDIR=$ORIGIN into your .pro file.

KernelPanic
  • 2,328
  • 7
  • 47
  • 90