3

I am wondering whether it is possible to force the linker to use some specified function to link to when compiling/linking.

I am using the LD_PRELOAD environment variable to hook some specified functions but I am not so familiar with linker so there are some troubles. I am hooking the standard open() system call to add some functionalities so that when users use the open() system call, I can collect some data. Basically, I am doing something like this:

int open(int fd, int flags, ...)  //(1)
{
    // add some functionalities here

    return open(...); // (2), return the original open system call
}

Obviously, this cannot work, as it would call into an infinite loop... So I am wondering whether I can force the linker to link some function to some specified dynamic library so that it would not cause an infinite loop. In the example above, it would be perfect for the "open()" system call at (2) to be linked to the standard library.

As for now, because I set the LD_PRELOAD as:

export LD_PRELOAD=/path/to/my_open.so

whenever a program that has an open() function inside is loaded, the dynamic linker would link that open() to my my_open.so. And that is the same for my open(): when the linker tries to link the open() at (2), it would also try to link that to my open() at (1), resulting in an infinite loop.

Any idea?

walkerlala
  • 1,599
  • 1
  • 19
  • 32

2 Answers2

2

The detailed algorithm about linking dynamic symbols can be found in man ld.so.

The ELF format allows to insert a symbol open@@VERSION. This is useful to keep different versions of libc in the same time. Probably you want to keep your own .so library in LD_LIBRARY_PATH.

See here for details.

alinsoar
  • 15,386
  • 4
  • 57
  • 74
  • If a different version is used, this `open` function will not preempt the original `open` function, so this cannot be used to hook the function (as intended here). – Florian Weimer Jul 21 '17 at 06:44
  • @FlorianWeimer yes... probably he could use dlsym(open@@VERSION) to be sure he can find the correct version, I do not know. But this method is used when want to keep multiple /lib/libc. – alinsoar Jul 21 '17 at 06:46
  • @alinsoar Inserting a symbol `open@@VERSION` is exactly what I want. More hints? – walkerlala Jul 21 '17 at 06:49
  • @walkerlala I told you . The ELF format allows the `@@VERSION` to add for each symbol. You can study this by following the tutorial of Ian Lance (the best tutorial to start with and he is also the creator of the gold linker) `http://www.airs.com/blog/archives/38` This tutorial teaches you how the linkers work and also how to write your own linker... – alinsoar Jul 21 '17 at 06:54
  • @walkerlala this tutorial of Ian has 20 parts. In one such part I remember he explains how to use the @@VERSION. – alinsoar Jul 21 '17 at 06:56
  • If `dlvsym` provides access to the original symbol, that smells like a bug because an unversioned symbol interposes all symbol versions. In addition, the `@@VERSION` syntax is just something printed by `readelf` and used in version scripts, it's not accepted by `dlsym` as an argument. – Florian Weimer Jul 21 '17 at 06:59
1

You can retrieve the original implementation of open using dlsym (RTLD_NEXT, "open"). There is no other reliable way to reach the original definition of open from an LD_PRELOAD library.

It might be instructive to look at projects such as fakeroot and cwrap, to see how they handle this.

Florian Weimer
  • 32,022
  • 3
  • 48
  • 92
  • AFAIK, using `dlsym()` requires a handle from `dlopen()`, which call `open()` internally, and now you have infinite loop... Your solution works for some libc functions such as `malloc()`, but it doesn't work for `open()`. – walkerlala Jul 21 '17 at 06:47
  • `RTLD_NEXT` is the handle here. It's a magic value which instructs `dlsym` to look at the caller address and determine the next symbol based on the calling DSO (not the calling function, alas). This really works; please look at `fakeroot`. (Furthermore, `dlopen` will not reopen a shared object a second time, so there is no loop, which is why the old and long-unused fallback code in `fakeroot` worked originally.) – Florian Weimer Jul 21 '17 at 06:52
  • You are right, RTLD_NEXT is exactly what I want. Thanks. – walkerlala Jul 21 '17 at 09:15