133

I have an old executable that's scheduled for the scrap heap, but it's not there yet. It relies on some libs that have been removed from my environment, but I have some stub libs someplace where it works fine. Id like to point this executable to these stub libs. Yes, i could set LD_LIBRARY_PATH, but this executable is called from many scripts, and many users and I'd love to fix it in one spot.

I don't have source for this, and would be hard to get it. I was thinking - can I edit this file, using an ELF aware editor, and add a simple PATH to rpath to have it hit the new libs? Is this possible, or once you create an ELF binary, you fix things to locations and they can't be moved?

afuzzyllama
  • 6,538
  • 5
  • 47
  • 64
Rich Homolka
  • 1,805
  • 2
  • 13
  • 13

4 Answers4

225

There is a more universal tool than chrpath called patchelf. It was originally created for use in making packages for Nix and NixOS (packaging system and a GNU/Linux distribution).

In case there is no rpath in a binary (here called rdsamp), chrpath fails:

chrpath -r '$ORIGIN/../lib64' rdsamp 
rdsamp: no rpath or runpath tag found.

On the other hand,

patchelf --set-rpath '$ORIGIN/../lib64' rdsamp

succeeds just fine.

user7610
  • 25,267
  • 15
  • 124
  • 150
  • 12
    Especially, `patchelf` is able to add an rpath to a binary that does not contain an rpath, yet - where `chrpath` only seem to be able to modify an already present entry. – maxschlepzig Feb 20 '14 at 12:30
  • 6
    As a general note, it's worth understanding the subtle distinction between `rpath` and `runpath`. Basically, one can override `LD_LIBRARY_PATH` and the other can't. For details, see http://blog.tremily.us/posts/rpath/ – Stuart Berg Jun 26 '14 at 21:37
  • 6
    The annoying thing is that both `chrpath` and `patchelf` are sloppy with their terminology. For example, the `patchelf` command shown above will change `runpath` but not `rpath` unless you also provide the `--force-rpath` option. – Stuart Berg Jun 26 '14 at 21:37
  • 14
    @superbatfish Yes, but the difference usually doesn't matter. This entry from the CHANGELOG of `patchelf` explains it: "`--set-rpath`, `--shrink-rpath` and `--print-rpath` now prefer `DT_RUNPATH` over `DT_RPATH`, which is obsolete. When updating, if both are present, both are updated. If only DT_RPATH is present, it is converted to `DT_RUNPATH` unless `--force-rpath` is specified. If neither is present, a `DT_RUNPATH` is added unless `--force-rpath` is specified, in which case a `DT_RPATH` is added." The name of the option was probably kept unchanged for compatibility reasons. – user7610 Jul 09 '14 at 20:23
  • How do you add more than one? – Andrew Hundt Nov 03 '15 at 01:04
  • 1
    @AndrewHundt Path separator on UNIX is a colon. See examples at https://nixos.org/patchelf.html. `patchelf --set-rpath /opt/my-libs/lib:/foo/lib program` This causes the dynamic linker to search in /opt/my-libs/lib and /foo/lib for the shared libraries needed by program.` – user7610 Nov 03 '15 at 08:21
  • this is so good. i am experimenting with running it on a bunch of .so files and a binary executable. i can go ubuntu -> debian -> fedora with no problem so far. – don bright Nov 23 '15 at 01:40
  • 2
    By far the best answer, this should be the accepted answer instead! – Kenneth Hoste Jan 15 '16 at 09:24
  • What does $ORIGIN mean? – Konstantin Burlachenko Jun 08 '16 at 19:32
  • 2
    @bruziuz `$ORIGIN` expands to the directory containing the program or shared object. Expansion happens when you execute the binary, not when you run `patchel`. To prevent shell expansion when running patchelf, the rpath is single quoted. It's explained in man pages. http://man7.org/linux/man-pages/man8/ld.so.8.html – user7610 Jul 19 '16 at 08:47
  • Recently, I encountered a trap when using `$ORIGIN` and symbolic links to shared libraries: Be aware that `$ORIGIN` denotes the directory of the symbolic link, which is _not_ necessarily the directory containing the program of shared library (yes, I'm talking to you, @user7610). The lesson is: If you want to use symbolic links to shared libraries, try to keep both in the same directory. Else you have to use `patchelf --set-rpath` or `LD_LIBRARY_PATH`. (At least for my version of the dynamic linker.) – hagello Dec 07 '20 at 20:32
90

There is a tool called chrpath which can do this - it's probably available in your distribution's packages.

TomH
  • 8,900
  • 2
  • 32
  • 30
  • 14
    Just a note for mac users, `install_name_tool` can do this with the `-rpath` flag – Kevin Tonon Dec 09 '15 at 17:59
  • 17
    If you get the error: `: no rpath or runpath tag found.`, you can't use `chrpath` to replace it, but you can use `patchelf` in this case: `patchelf --set-rpath /path/to/libaries ` – phyatt Jan 02 '18 at 16:15
  • I prefer chrpath if possible since, while it's more universal, patchelf has some long standing bug that dramatically inflates the size of your libraries/executables. – taranaki Jan 04 '19 at 02:19
  • @taranaki: Which version of patchelf are you talking about? – hagello Oct 08 '20 at 04:59
  • 3
    _chrpath_ has got a severe limitation: _it can only replace an RPATH with one of equal or shorter length_ (man page of rpath version 0.16) – hagello Oct 08 '20 at 05:03
  • @hagello I don't remember, and it may be fixed by now. – taranaki Oct 13 '20 at 20:02
47

Just like @user7610 said, the right way to go is the patchelf tool.

But, I feel that I can give a more comprehensive answer, covering all the commands one needs to do exactly that.

For a comprehensive article on the subject, click here

First of all, many developers talk about RPATH, but they actually mean RUNPATH. These are two different optional dynamic sections, and the loader handles them very differently. You can read more about the difference between them in the link I mentioned before.

For now, just remember:

  • If RUNPATH is set, RPATH is ignored
  • RPATH is deprecated and should be avoided
  • RUNPATH is preferred because it can be overridden by LD_LIBRARY_PATH

See the current R[UN]PATH

readelf -d <path-to-elf> | egrep "RPATH|RUNPATH"

Clear the R[UN]PATH

patchelf --remove-rpath <path-to-elf>

Notes:

  • Removes both RPATH and RUNPATH

Add values to R[UN]PATH

patchelf [--force-rpath] --set-rpath "<desired-rpath>" <path-to-elf>

Notes:

  • <desired-path> is a colon separated directories list, e.g: /my/libs:/my/other/libs
  • If you specify --force-rpath, sets RPATH, otherwise sets RUNPATH
Daniel Trugman
  • 8,186
  • 20
  • 41
  • 1
    `-Wl,-R, -Wl,--enable-new-dtags` sets `DT_RUNPATH`, and that is the one most folks should use. `RUNPATH` can be overridden by `LD_LIBRARY_PATH`, so folks should not use `--force-rpath`. – jww Mar 10 '19 at 18:15
  • @jww I see that I didn't add a comment about the deprecation of RPATH, so I added one just now. Thanks! – Daniel Trugman Mar 11 '19 at 09:24
  • Note that the example `` uses a colon; it should be a comma (that is: `/my/libs,/my/other/libs`). – Alan De Smet Sep 13 '19 at 16:30
  • @AlanDeSmet, I don't know about the comma, but the colon works for me. – Daniel Trugman Sep 15 '19 at 08:19
  • 2
    RPATH might be deprecated, but using RUNPATH can lead to "unexpected" corner cases. See, for example, https://www.qt.io/blog/2011/10/28/rpath-and-runpath . – Rob Oct 21 '20 at 08:37
  • 1
    "*... **colon** (not **comma**) separated ...*". – CristiFati Sep 23 '21 at 14:52
0

This worked for me, replacing XORIGIN with $ORIGIN.

chrpath -r '\$\ORIGIN/../lib64' httpd

Donovan Solms
  • 941
  • 12
  • 17