views:

53

answers:

3

Suppose I have an ELF binary that's dynamic linked, and I want to override/redirect certain library calls. I know I can do this with LD_PRELOAD, but I want a solution that's permanent in the binary, independent of the environment, and that works for setuid/setgid binaries, none of which LD_PRELOAD can achieve.

What I'd like to do is add code from additional object files (possibly in new sections, if necessary) and add the symbols from these object files to the binary's symbol table so that the newly added version of the code gets used in place of the shared library code. I believe this should be possible without actually performing any relocations in the existing code; even though they're in the same file, these should be able to be resolved at runtime in the usual PLT way (for what it's worth I only care about functions, not data).

Please don't give me answers along the line of "You don't want to do this!" or "That's not portable!" What I'm working on is a way of interfacing binaries with slightly-ABI-incompatible alternate shared-library implementations. The platform in question is i386-linux (i.e. 32-bit) if it matters. Unless I'm mistaken about what's possible, I could write some tools to parse the ELF files and perform my hacks, but I suspect there's a fancy way to use the GNU linker and other tools to accomplish this without writing new code.

A: 

You could handle some of the dynamic linking in your program itself. Read the man page for dlsym(3) in particular, and dlopen(3), dlerror(3), and dlclose(3) for the rest of the dynamic linking interface.

A simple example -- say I want to override dup2(2) from libc. I could use the following code (let's call it "dltest.c"):

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>

int (*prev_dup2)(int oldfd, int newfd);

int dup2(int oldfd, int newfd) {
    printf("DUP2: %d --> %d\n", oldfd, newfd);
    return prev_dup2(oldfd, newfd);
}

int main(void) {
    int i;

    prev_dup2 = dlsym(RTLD_NEXT, "dup2");
    if (!prev_dup2) {
        printf("dlsym failed to find 'dup2' function!\n");
        return 1;
    }
    if (prev_dup2 == dup2) {
        printf("dlsym found our own 'dup2' function!\n");
        return 1;
    }

    i = dup2(1,3);
    if (i == -1) {
        perror("dup2() failed");
    }

    return 0;
}

Compile with:

gcc -o dltest dltest.c -ldl

The statically linked dup2() function overrides the dup2() from the library. This works even if the function is in another .c file (and is compiled as a separate .o).

If your overriding functions are themselves dynamically linked, you may want to use dlopen() rather than trusting the linker to get the libraries in the correct order.

EDIT: I suspect that if a different function within the overridden library calls an overridden function, the original function gets called rather than the override. I don't know what will happen if one dynamic library calls another.

Jander
I'm trying to modify binaries, not source.
R..
+1  A: 

ld has the option --wrap that lets you replace a given symbol like malloc by a symbol you'd call __wrap_malloc. With that you could write some stubs for the functions you are interested in and link that to the library in question.

Jens Gustedt
Again this question is about patching binary files, not wrapping library calls in programs I compile.
R..
R.: I don't see why you can't do what you asked in your question with what I propose. A `__wrap_malloc` doesn't necessary call the old `malloc`, it just replaces one symbol by another symbol. `ld` is the best tool to manipulate binaries that I know, portable, flexible, everything you asked for. But you seem to have certain types of answers in your head, that are not necessarily reflected in your question.
Jens Gustedt
@Jens: If you mean there's a way to take an existing executable ELF file and add code in it to define some symbols which would otherwise be resolved to shared library code, using only GNU `ld`, I'd love to know how. But please be aware that the input programs that this needs to be applied to are executable ELF files, not `.o` files or source.
R..
+2  A: 

I suggest the elfsh et al. tools from the ERESI project, if you want to instrument the ELF files themselves. Compatibility with i386-linux is not a problem, as I've used it myself for the same purpose.

The relevant how-tos are here.

Michael Foukarakis
Looks like it'll take some reading, but I think this is the tool I was looking for. Thanks!
R..
Is there *any* documentation for ERESI? I can't find any "getting started", examples, etc.
R..
Hmm, looks like there's none (all links point to svn?).
Michael Foukarakis