Hello,

I have troubles understanding the interpretation of $ORIGIN on
OpenBSD. I'm switching to OpenBSD from Linux, so I may be biased in my
assumptions.

I built a program (python in this example) with the following ld parameters:

-Wl,origin,z
-Wl,rpath,'$ORIGIN/../lib'

I can then check that the proper attributes were set on the binary:

$ readelf -d python
...
0x0000000000000001 (NEEDED) Shared library: [libpython2.7.so.1.0]
...
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/../lib]
...
0x000000006ffffffb (FLAGS_1) Flags: ORIGIN

On the filesystem, everything is also correct, say I have:

- The python binary located in ~/bin/python
- The python library located in ~/lib/libpython2.7.so.1.0

Now if I run python using its absolute or relative path, everything
works fine. However when I add python to my $PATH, and call it by name
(just "python"), the loader complains it cannot find
libpython2.7.so.1.0.

e.g. with python in my $PATH:

$ python
python: can't load library 'libpython2.7.so.1.0'

That won't work, while:

$ $(which python)

or

$ ~/bin/python

or

$ /home/.../bin/python

These will work.

Strangely, if I cd to the directory where python is located, then I
can issue just "python" and it will work. e.g:

$ cd ~/bin
$ python

It seems to me that $ORIGIN is interpreted as a relative path from the
elf file when calling it with an absolute path, or relative to the
path of the caller when using $PATH.

I did not expect this behavior, as "man ld.so" states:

> When resolving dependencies for the loaded objects, ld-elf.so.1 may be
>     allowed to translate dynamic token strings in rpath and soname by setting
>     -z origin option of the static linker ld(1).  The following strings are
>     recognized now:
>     $ORIGIN Translated to the full path of the loaded object.

Am I doing something wrong here, or is expected behavior?

I've spent some time digging libexec/ld.so/resolve.c for an
explanation (I'm on -current snapshots from Dec 3 2015, last week).
It's not an easy lecture for a newcomer, so please forgive me if what
I say below is complete garbage.

Here is what I found:

1) My elf binary is loaded and ends up in _dl_finalize_object()

2) My binary has RPATH and FLAGS_1 to ORIGIN, so _dl_origin_subst() is called
if (object->dyn.rpath) {
  object->rpath = _dl_split_path(object->dyn.rpath);
if ((object->obj_flags & DF_1_ORIGIN) && _dl_trust)
  _dl_origin_subst(object);
}

3) _dl_origin_subst() retrieves the original path of the elf object,
and applies $ORIGIN interpolation if necessary
if (_dl_origin_path(object, origin_path) != 0)
  return;

/* perform path substitutions on each segment of rpath */
for (pp = object->rpath; *pp != NULL; pp++) {
  _dl_origin_subst_path(object, origin_path, pp);
}

4) _dl_origin_subst_path() will use origin_path in case of $ORIGIN, so
back to _dl_origin_path() to understand what is used.
case SUBST_ORIGIN:
  value = origin_path;
  break;

5) _dl_origin_path() just uses _dl_realpath(_dl_dirname()) on the elf object.

6) _dl_dirname() will return "." if the path provided does not contain a '/'

7) _dl_realpath() will prepend the CWD to this.

These last steps seems odd to me. Why is the CWD used here, when what
we really want is the directory containing the binary of the currently
running process (i.e. /proc/self/exe on linux).
Just my guess, I don't really know what I'm talking about.

Anyway, explanations  greatly appreciated! And again, please forgive
me if all that is just me doing stupid things.

Reply via email to