views:

224

answers:

6

How do I change the library a function loads from during run time?

For example, say I want to replace the standard printf function with something new, I can write my own version and compile it into a shared library, then put "LD_PRELOAD=/my/library.so" in the environment before running my executable.

But let's say that instead, I want to change that linkage from within the program itself. Surely that must be possible... right?

EDIT
And no, the following doesn't work (but if you can tell me how to MAKE it work, then that would be sufficient).

void* mylib = dlopen("/path/to/library.so",RTLD_NOW);
printf = dlsym(mylib,"printf");
A: 

there is an environment variable LD_LIBRARY_PATH where the linker searches for shred libraries, prepend your path to LD_LIBRARY_PATH, i hope that would work

Neeraj
+2  A: 

AFAIK, that is not possible. The general rule is that if the same symbol appears in two libraries, ld.so will favor the library that was loaded first. LD_PRELOAD works by making sure the specified libraries are loaded before any implicitly loaded libraries.

So once execution has started, all implicitly loaded libraries will have been loaded and therefore it's too late to load your library before them.

R Samuel Klatchko
A: 

You can't change that. In general *NIX linking concept (or rather lack of concept) symbol is picked from first object where it is found. (Except for oddball AIX which works more like OS/2 by default.)

Programmatically you can always try dlsym(RTLD_DEFAULT) and dlsym(RTLD_NEXT). man dlsym for more. Though it gets out of hand quite quickly. Why is rarely used.

Dummy00001
Can you explain what you mean with `dlsym()`? See my edit to the original question for more information.
tylerl
you can't `printf = dlsym(mylib,"printf");` obviously, you should be able though with a global variable `int (*myprintf)(const char *fmt, ...) = dlsym(mylib,"printf")` and then `#define printf myprintf` in the compilation units you need. I have thought that `printf` was more of an example in your case. If `printf` is specifically your target (and override at link time is also an option), you can also try to tell linker to not to link standard libraries, put your library with custom printf() first in the library list - before manually specified standard libraries where orig printf() is located.
Dummy00001
BTW, LD_PRELOAD is a way to add a library *before* standard libraries. It just does it during run time. During link time it is the order of libraries you give to linker is defining the priority of symbols: symbols would be looked up from first lib to the last. Usually standard libraries are put into the link list first (by frontends like cc). But you can turn it off (-nostdlib) and specify them manually in the order you need, e.g. by putting your libmy.so first before anything else. Then printf() from libmy.so would *override* the printf from libc.
Dummy00001
A: 

It should be said that trying to replace functions from the libc in your application has undefined behavior as per ISO C/POSIX, regardless of whether you do it statically or dynamically. It may work (and largely will work on GNU/Linux), but it's unwise to rely on it working. If you just want to use the name "printf" but have it do something nonstandard in your program, the best way to do this is to #undef printf and #define printf my_printf AFTER including any system headers. This way you don't interfere with any internal use of the function by libraries you're using...and your implementation of my_printf can even call the system printf if/when it needs to.

On the other hand, if your goal is to interfere with what libraries are doing, somewhere down the line you're probably going to run into compatibility issues. A better approach would probably be figuring out why the library won't do what you want without redefining the functions it uses, patching it, and submitting patches upstream if they're appropriate.

R..
A: 

Store the dlsym() result in a lookup table (array, hash table, etc). Then #undef print and #define print to use your lookup table version.

Bryan Drewery
A: 

There is no clean solution but it is possible. I see two options:

  1. Overwrite printf function prolog with jump to your replacement function.

    It is quite popular solution for function hooking in MS Windows. You can find examples of function hooking by code rewriting in Google.

  2. Rewrite ELF relocation/linkage tables.

    See this article on codeproject that does almost exactly what you are asking but only in a scope of dlopen()'ed modules. In your case you want to also edit your main (typically non-PIC) module. I didn't try it, but maybe its as simple as calling provided code with:

    void* handle = dlopen(NULL, RTLD_LAZY);
    void* original;
    original = elf_hook(argv[0], LIBRARY_ADDRESS_BY_HANDLE(handle), printf, my_printf);
    

    If that fails you'll have to read source of your dynamic linker to figure out what needs to be adapted.

Marcin Wisnicki