111

I'm aware that it is possible to use readelf -d <elf> | grep RPATH to inspect a given binary from the shell, but is it possible to do this within a process?

Something like (my completely made up system call):

  /* get a copy of current rpath into buffer */
  sys_get_current_rpath(&buffer);

I'm trying to diagnose some suspect SO linking issues in our codebase, and would like to inspect the RPATH this way if possible (I'd rather not have to spawn an external script).

Justicle
  • 14,761
  • 17
  • 70
  • 94
  • 3
    Keep in mind that when diagnosing shared library issues you should also inspect the RUNPATH tag. Thus you should `grep PATH` instead. It's up to the linker whether RPATH or RUNPATH is used, and there are subtle but important differences between the two: https://stackoverflow.com/a/52020177 – Nicolas Capens Aug 28 '20 at 13:55

5 Answers5

188

For the record, here are a couple of commands that will show the rpath / runpath header.

objdump -x binary-or-library |grep 'R.*PATH'

Maybe an even better way to do it is the following:

readelf -d binary-or-library |head -20

The second command also lists the direct dependencies on other libraries followed by rpath.

legends2k
  • 31,634
  • 25
  • 118
  • 222
Michael Dillon
  • 31,973
  • 6
  • 70
  • 106
  • 8
    On ubuntu 15.04 i have to use: objdump -x binary-or-library |grep RUNPATH – Andreas Roth Aug 14 '15 at 05:20
  • 6
    RPATH != RUNPATH, see http://stackoverflow.com/questions/7967848/use-rpath-but-not-runpath & http://blog.qt.io/blog/2011/10/28/rpath-and-runpath/ – Fernando Gonzalez Sanchez Oct 26 '16 at 01:10
  • 1
    @AndreasRoth Fixed answer now to show both `RPATH` and `RUNPATH`. It's better to set the latter as it has lesser priority than the env. variable `LD_LIBRARY_PATH`, giving more flexibility to user. – legends2k Dec 08 '22 at 07:19
66

The question is specifically about RPATH, but modern linkers use RUNPATH instead. See this answer -- there is a subtle semantic difference between the two.

The answer is updated to print either.

#include <assert.h>
#include <stdio.h>
#include <elf.h>
#include <link.h>

int main()
{
  const ElfW(Dyn) *dyn = _DYNAMIC;
  const ElfW(Dyn) *rpath = NULL;
  const ElfW(Dyn) *runpath = NULL;
  const char *strtab = NULL;
  for (; dyn->d_tag != DT_NULL; ++dyn) {
    if (dyn->d_tag == DT_RPATH) {
      rpath = dyn;
    } else if (dyn->d_tag == DT_RUNPATH) {
      runpath = dyn;
    } else if (dyn->d_tag == DT_STRTAB) {
      strtab = (const char *)dyn->d_un.d_val;
    }
  }

  assert(strtab != NULL);

  if (rpath != NULL) {
    printf("RPATH: %s\n", strtab + rpath->d_un.d_val);
  } else if (runpath != NULL) {
    printf("RUNPATH: %s\n", strtab + runpath->d_un.d_val);
  }
  return 0;
}
Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • 1
    it is brilliant, but it does not work with $ORIGIN. $ORIGIN is not interpreted, and returned as is by the function. Is there a way to add the $ORIGIN interpretation? – Jérôme Jul 10 '14 at 15:30
  • 6
    @Jérôme If you are executing in an environment where `/proc` is mounted, then expanding `$ORIGIN` is as simple as `readlink("/proc/self/exe", ...)` then NUL-terminate at the last slash. – Employed Russian Jul 11 '14 at 16:09
  • 1
    While the question was specifically about `RPATH`, I'd like to note that it's equally important to check the `DT_RUNPATH` tag if one wishes to know the paths a binary might be loading its shared libraries from. – Nicolas Capens Aug 28 '20 at 13:57
18

You can also use:

chrpath -l binary-or-library
4

Here's what I use for convenience, as a shell function:

function getrpath {
    eu-readelf -d "${1:?}" | sed -e '/RUNPATH/{s~.*\[\(.*\)\]~\1~;n};d'
}

This consumes eu-readelf output from elfutils like:

Type              Value
NEEDED            Shared library: [libpq.so.5]
NEEDED            Shared library: [libc.so.6]
RUNPATH           Library runpath: [/some/path/to/lib]
....

and emits

 /some/path/to/lib

It should work fine with binutils readelf instead of elfutils eu-readelf too.

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
0

There is a way. Follow the example code in man dlinfo [1], but use NULL as the first parameter of dlopen().

[1] https://man7.org/linux/man-pages/man3/dlinfo.3.html

Daniel
  • 137
  • 1
  • 4