This is the mail archive of the libc-help@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

LD_PRELOAD and attribute constructor


Hello,

I'm not sure if this is the correct list for my question - if not,
kindly redirect me. I'm trying to do libc function call wrapping using
LD_PRELOAD, with the "next" function pointers initialized in a
function declared with __attribute__((constructor)). This worked fine
for a while, but I hit a snag when I came across shared libraries that
have their own constructor functions. Specifically, it seems that my
wrapper functions are loaded into the program, and can be called
before my initializer. This is somewhat counter-intuitive to me, since
I would think that any function declared with the constructor
attribute would be called before any of the other functions in that
specific translation unit.

For a small concrete example, the following files show my issue (which
has this behavior on at least Gentoo with glibc 2.8, and Ubuntu 7.10
(not sure how to check the libc version there)). The wrap.c file
becomes the .so that I load with LD_PRELOAD. The prog.c file is the
main program, which links in lib.so generated from lib.c. Both prog.c
and lib.c execute an fopen() call. I would expect either: 1) Both
fopen calls get wrapped, or 2) only the prog.c fopen call is wrapped.
In either case, I expect the wrap.c constructor to be called before a
wrapped fopen() call. What happens is both calls are wrapped, but the
fopen call in the library happens before my constructor.

## wrap.c:
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>

static void wrap_init(void) __attribute__((constructor));
static FILE *(*s_fopen)(const char *, const char *);

FILE *fopen(const char *path, const char *mode)
{
        printf("Wrap fopen: %p\n", s_fopen);
        if(!s_fopen) {
                printf("Abort! wrapped function is NULL\n");
                return NULL;
        }
        return s_fopen(path, mode);
}

static void wrap_init(void)
{
        s_fopen = dlsym(RTLD_NEXT, "fopen");
        printf("Init: %p\n", s_fopen);
}
## End wrap.c

## lib.c
#include <stdio.h>
static void lib_init(void) __attribute__((constructor));
static void lib_init(void) {fopen("foo.txt", "r");}
## End lib.c

## prog.c
#include <stdio.h>
int main(void) {fopen("main.txt", "r"); return 0;}
## End prog.c

## Makefile
all: wrap.so lib.so prog ; LD_PRELOAD=./wrap.so LD_LIBRARY_PATH=. ./prog
wrap.so: wrap.c ; gcc -shared -fPIC wrap.c -ldl -o wrap.so
lib.so: lib.c ; gcc -shared -fPIC lib.c -o lib.so
prog: prog.c lib.so ; gcc prog.c lib.so -o prog
## End Makefile

Running the Makefile produces this output:
LD_PRELOAD=./wrap.so LD_LIBRARY_PATH=. ./prog
Wrap fopen: (nil)
Abort! wrapped function is NULL
Init: 0x40133b20
Wrap fopen: 0x40133b20

But if I comment out the fopen in the lib.c constructor, I get the
desired wrap function behavior (s_fopen is never NULL in fopen):
LD_PRELOAD=./wrap.so LD_LIBRARY_PATH=. ./prog
Init: 0x40133b20
Wrap fopen: 0x40133b20

This issue came up in the real world because apparently
libselinux.so.1 does an fopen in a constructor function, and this is
linked in by a program that I was trying to wrap (specifically, 'cp').

Was it invalid for me to assume that wrap_init() would be called
before any other function in wrap.c? If so, is there a particular
reason why it was implemented this way?

I was able to work around the issue by just removing the constructor
in wrap.c, and instead doing the dlsym initialization inside the
if(!s_fopen) block. Still, I'm curious to know why this behaves the
way it does.

Thanks!
-Mike


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]