Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proxy shared library (sharedlib, shlib, so) for ELF?

On Windows, it's more or less common to create "proxy DLLs" which take place of the original DLL and forward calls to it (after any additional actions as needed). You can read about it here and here for example.

However, shlib munging culture under Linux is quite different. It starts with the fact that LD_PRELOAD is the builtin feature with ld.so under Linux, which simply injects separate shlib into process and uses any symbols it defines as override. And that "injection" technique seems to define whole direction of thought - here's a typical ELF hacking tool or this question, where gentleman seems to have the same usecase as me, but starts with asking how he can patch existing binaries.

No, thanks. I don't want to inject into or modify something which is nor mine. All I want to do is to make a standalone proxy shlib which will call out to the original. Ideally, there would be a tool which can be fed with the original .so and create a C source code which would just redirect to original's functions, while letting me easily override anything I want. So, where's such tool? ;-) Thanks.

like image 592
pfalcon Avatar asked Jan 20 '12 02:01

pfalcon


Video Answer


1 Answers

Using LD_PRELOAD doesn't really involve modifying something which isn't yours, and the injection isn't all that different from normal dynamic library loading. The “typical ELF hacking tool” from the ERESI project is unrelated to LD_PRELOAD. You should not be afraid of it. A good introduction to writing LD_PRELOAD-able “proxies” is here.

That being said, if you want to create a system-wide proxy for some library, you might argue that globally setting LD_PRELOAD (and thus loading your proxy into every binary that ever runs on your system) is undesirable. It is commonly used to override functions from glibc by tools such as libeatmydata or socksify, but if you're overriding a function in a library that is bigger and/or less widespread than glibc, it makes sense to try to find another approach, to really create a proxy for just that one library.

One such approach is to use patchelf --replace-needed or --add-needed to hardcode the full pathname of the original library and then make sure the proxy library is found first by setting LD_LIBRARY_PATH¹. So, the complete procedure is:

  1. create an LD_PRELOAD-able library that overrides some functions of the original one (test that it works using only LD_PRELOAD before proceeding further!)
  2. compile and link this library with the original library so that ldd libwrapper-foo.so includes something like:
    libfoo.so.0 => /usr/lib/x86_64-linux-gnu/libfoo.so.0 (0x0000deadbeef0000)
  3. hardcode the full path using patchelf:
    patchelf --replace-needed libfoo.so.0 /usr/lib/x86_64-linux-gnu/libfoo.so.0 libwrapper-foo.so
  4. symlink libwrapper-foo.so to libfoo.so.0
  5. now LD_LIBRARY_PATH=. ldd $(which program-that-uses-libfoo) should include these lines:
    libfoo.so.0 => ./libfoo.so.0 (0x0000dead56780000)
    /usr/lib/x86_64-linux-gnu/libfoo.so.0 (0x0000dead1234000000)
  6. set LD_LIBRARY_PATH to full path to the wrapper library in your .bashrc or somewhere

A real-life example of such proxy libary is my wrapper for libpango that enables subpixel positioning for all applications.


¹) It might also be possible to put this proxy library into /usr/local/lib, but ldconfig (the tool that updates shared libraries cache) refuses to use libraries with hardcoded absolute paths.

like image 89
Tomáš Janoušek Avatar answered Sep 22 '22 22:09

Tomáš Janoušek