Can I change 'rpath' in an already compiled binary?

79,976

Solution 1

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

Solution 2

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.

Solution 3

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
Share:
79,976

Related videos on Youtube

Rich Homolka
Author by

Rich Homolka

Updated on September 25, 2021

Comments

  • Rich Homolka
    Rich Homolka over 2 years

    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?

    • wildplasser
      wildplasser over 11 years
      Wrap it into a shellscript that sets LD_LIBRARY_PATH and calls the binary. Put the shell script in a place that is in the callers's PATH.
    • Will
      Will almost 8 years
      LD_LIBRARY_PATH is inherited by child processes. You might not want that.
    • Rich Homolka
      Rich Homolka almost 8 years
      @will yeah that and I already said I don't want to do that. :)
  • maxschlepzig
    maxschlepzig over 10 years
    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.
  • Stuart Berg
    Stuart Berg almost 10 years
    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 blog.tremily.us/posts/rpath
  • Stuart Berg
    Stuart Berg almost 10 years
    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.
  • user7610
    user7610 almost 10 years
    @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.
  • Andrew Hundt
    Andrew Hundt over 8 years
    How do you add more than one?
  • user7610
    user7610 over 8 years
    @AndrewHundt Path separator on UNIX is a colon. See examples at 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.`
  • don bright
    don bright over 8 years
    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.
  • Kevin Tonon
    Kevin Tonon over 8 years
    Just a note for mac users, install_name_tool can do this with the -rpath flag
  • Kenneth Hoste
    Kenneth Hoste over 8 years
    By far the best answer, this should be the accepted answer instead!
  • Konstantin Burlachenko
    Konstantin Burlachenko almost 8 years
    What does $ORIGIN mean?
  • user7610
    user7610 almost 8 years
    @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. man7.org/linux/man-pages/man8/ld.so.8.html
  • phyatt
    phyatt over 6 years
    If you get the error: <binary>: 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 <binary>
  • taranaki
    taranaki over 5 years
    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.
  • jww
    jww about 5 years
    -Wl,-R,<desired-rpath> -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.
  • Daniel Trugman
    Daniel Trugman about 5 years
    @jww I see that I didn't add a comment about the deprecation of RPATH, so I added one just now. Thanks!
  • Alan De Smet
    Alan De Smet over 4 years
    Note that the example <desired-path> uses a colon; it should be a comma (that is: /my/libs,/my/other/libs).
  • Daniel Trugman
    Daniel Trugman over 4 years
    @AlanDeSmet, I don't know about the comma, but the colon works for me.
  • hagello
    hagello over 3 years
    @taranaki: Which version of patchelf are you talking about?
  • hagello
    hagello over 3 years
    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)
  • taranaki
    taranaki over 3 years
    @hagello I don't remember, and it may be fixed by now.
  • Rob
    Rob over 3 years
    RPATH might be deprecated, but using RUNPATH can lead to "unexpected" corner cases. See, for example, qt.io/blog/2011/10/28/rpath-and-runpath .
  • hagello
    hagello over 3 years
    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.)
  • CristiFati
    CristiFati over 2 years
    "... colon (not comma) separated ...".