Why Ld_Preload Doesn't Work for One of Loaded Shared Libraries

Why LD_PRELOAD doesn't work for one of loaded shared libraries?

It seeems that http.so is loaded with the RTLD_DEEPBIND flag and that is why LD_PRELOAD is ignored for one of shared libraries.

This is from http://linux.die.net/man/3/dlopen:

RTLD_DEEPBIND (since glibc 2.3.4)
Place the lookup scope of the symbols in this library ahead of the global scope. This means that a self-contained library will use its
own symbols in preference to global symbols with the same name
contained in libraries that have already been loaded. This flag is not
specified in POSIX.1-2001.

I wrote a test shared library:

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

static void initialize_my_dlopen(void) __attribute__((constructor));

void* (*real_dlopen)(const char *, int flag);
static int unset_RTLD_DEEPBIND=0;
static int _initialized = 0;

static void initialize_my_dlopen(void)
{
if (_initialized)
return;
real_dlopen = (void *(*)(const char *,int))dlsym(RTLD_NEXT, "dlopen");
unset_RTLD_DEEPBIND = atoi(getenv("UNSET_RTLD_DEEPBIND") ? getenv("UNSET_RTLD_DEEPBIND") : "0");
printf("unset_RTLD_DEEPBIND: %d\n", unset_RTLD_DEEPBIND);
_initialized = 1;
}

extern "C" {

void *dlopen(const char *filename, int flag)
{
int new_flag = unset_RTLD_DEEPBIND == 0 ? flag : flag & (~RTLD_DEEPBIND);
return (*real_dlopen)(filename, new_flag);
}
}

And built it:

  gcc -shared -fPIC -g -m64 my_dlopen.cpp -o libmy_dlopen.so -ldl

When I set UNSET_RTLD_DEEPBIND to 0 and run httpd the program coredumps.

export UNSET_RTLD_DEEPBIND=0
LD_PRELOAD=./libmy_dlopen.so:./ps/lib/libmem_consumption.so ./bin/httpd -f config

When I set UNSET_RTLD_DEEPBIND to 1 and run httpd everything is OK.

export UNSET_RTLD_DEEPBIND=1
LD_PRELOAD=./libmy_dlopen.so:./ps/lib/libmem_consumption.so ./bin/httpd -f config

And this is the output of LD_DEBUG=all for the UNSET_RTLD_DEEPBIND to 1:

 10678: symbol=free;  lookup in file=/apache2/bin/httpd [0]
10678: symbol=free; lookup in file=/apache2/libmy_dlopen.so [0]
10678: symbol=free; lookup in file=/apache2/ps/lib/libmem_consumption.so [0]
10678: binding file /apache2/ext/http.so [0] to /apache2/ps/lib/libmem_consumption.so [0]: normal symbol `free'

Not able to use function from my dynamic library loaded via LD_PRELOAD

sscanf() may be a macro referencing an internal function. Have a look at <stdio.h>. For example, on my system, I have:

extern int __isoc99_sscanf (const char *__restrict __s,
const char *__restrict __format, ...) __THROW;
# define fscanf __isoc99_fscanf
# define scanf __isoc99_scanf
# define sscanf __isoc99_sscanf

Hence, sscanf() is actually a macro referencing __isoc99_sscanf(). So, if you redefine the latter, you get what you expect.

#include <stdarg.h>
#include <stdio.h>

//__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
int __isoc99_sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}

After rebuild, you get:

$ gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1
$ LD_PRELOAD=`pwd`/preload.so ./foo
test
42

Error when trying to link to a shared library I created using LD_PRELOAD

I do not know how to add compiler flag, but you can make it works as follows.

Compile test as usual:

g++ -Wall -g test.cpp -o test -std=c++11 

Invoke it like this:

LD_PRELOAD=/home/nullmalloc/nullmalloc.so ./test

What is the LD_PRELOAD trick?

If you set LD_PRELOAD to the path of a shared object, that file will be loaded before any other library (including the C runtime, libc.so). So to run ls with your special malloc() implementation, do this:

$ LD_PRELOAD=/path/to/my/malloc.so /bin/ls

Change library load order at run time (like LD_PRELOAD but during execution)

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.

Why does `LD_DEBUG=libs` fail to display a library that loaded in an application?

It's because the qtcreator process does not load the libqbscore.so. The qbs child process loads it.

Because Qt Creator and Qbs are open source projects, their interactions can be analyzed by analyzing the source codes.

why is library loaded via LD_PRELOAD operating before initialization?

Obviously, fopen is called before initialization of comm.so. It is interesting to place a breakpoint in fopen() in order to understand (check this link in order to get debug symbols of various packages). I get this backtrace:

(gdb) bt
#0 fopen (filename=0x7ffff79cd2e7 "/proc/filesystems", mode=0x7ffff79cd159 "r") at comm.c:28
#1 0x00007ffff79bdb0e in selinuxfs_exists_internal () at init.c:64
#2 0x00007ffff79b5d98 in init_selinuxmnt () at init.c:99
#3 init_lib () at init.c:154
#4 0x00007ffff7de88aa in call_init (l=<optimized out>, argc=argc@entry=1, argv=argv@entry=0x7fffffffdf58, env=env@entry=0x7fffffffdf68) at dl-init.c:72
#5 0x00007ffff7de89bb in call_init (env=0x7fffffffdf68, argv=0x7fffffffdf58, argc=1, l=<optimized out>) at dl-init.c:30
#6 _dl_init (main_map=0x7ffff7ffe170, argc=1, argv=0x7fffffffdf58, env=0x7fffffffdf68) at dl-init.c:120
#7 0x00007ffff7dd9c5a in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
#8 0x0000000000000001 in ?? ()
#9 0x00007fffffffe337 in ?? ()
#10 0x0000000000000000 in ?? ()

It is obvious, comm.so depends on other libraries (libdl.so that requires libselinux.so). And comm.so is not the only library that declare an init function. libdl.so and libselinux.so also declare ones.

So, comm.so is the first library to be loaded (because it is declared with LD_PRELOAD) but, comm.so depends on libdl.so (because of -ldl during compilation) and libdl.so depends on libselinux.so. So, in order to load comm.so, init functions from libdl.so and libselinux.so are called before. And finally, init function from libselinux.so call fopen()

Personally, I usually resolve dynamic symbols during first call to the symbol. Like this:

FILE *fopen(const char *filename, const char *mode) {
static FILE *(*real_fopen)(const char *filename, const char *mode) = NULL;

if (!real_fopen)
real_fopen = dlsym(RTLD_NEXT, "fopen");

return real_fopen(filename, mode);
}


Related Topics



Leave a reply



Submit