This is the mail archive of the
libc-help@sourceware.org
mailing list for the glibc project.
LD_PRELOAD functions are called before _init
- From: Mark Hills <mark dot hills at framestore dot com>
- To: libc-help at sourceware dot org
- Date: Thu, 18 Sep 2014 11:03:40 +0100 (BST)
- Subject: LD_PRELOAD functions are called before _init
- Authentication-results: sourceware.org; auth=none
I am finding that when LD_PRELOAD is used, calls to symbols are commonly
(incorrectly?) invoked before the _init function.
See the minimal example below; where fopen64() is invoked before the
_init.
This makes it impossible to use _init as a 'constructor'. Am I expecting a
guarantee that does not exist?
In practice, my problem is this completely prevents wrapping of fopen() in
a commercial application:
* this application provides a new malloc() by linking against
libjemalloc.so
* the new malloc() function takes a lock and makes a call to fopen()
* without the constructor, our implementation of fopen() must call
dlsym(), but this recursively calls malloc() and deadlocks
Is there are better practice to 'initialise' this, or is some bug mangling
the expected ordering?
This is on Scientific Linux 6.5 (a derivative of RedHat 6.5, see version
below)
Advice appreciated -- thanks.
--
Mark
/*
* Demonstrate ordering of calls in LD_PRELOAD
*
* gcc -g -o basic.so basic.c -fPIC -Wall -ldl -shared
*/
#define _GNU_SOURCE
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
static FILE* (*real_fopen64)(const char*, const char *) = NULL;
static __attribute__((constructor)) void do_init(void)
{
real_fopen64 = dlsym(RTLD_NEXT, "fopen64");
assert(real_fopen64);
}
FILE *fopen64(const char *pathname, const char *mode)
{
assert(real_fopen64);
return (*real_fopen64)(pathname, mode);
}
$ ldd ./basic.so
linux-vdso.so.1 => (0x00007fff1f367000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f723172e000)
libc.so.6 => /lib64/libc.so.6 (0x00007f723139a000)
/lib64/ld-linux-x86-64.so.2 (0x000000344ee00000)
$ export LD_PRELOAD=./basic.so
$ /bin/ls
ls: basic.c:21: fopen64: Assertion `real_fopen64' failed.
Abort (core dumped)
(gdb) bt
#0 0x000000344f632925 in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x000000344f634105 in abort () at abort.c:92
#2 0x000000344f62ba4e in __assert_fail_base (fmt=<value optimized out>, assertion=0x7f9c378ed6e6 "real_fopen64", file=0x7f9c378ed6de "basic.c", line=<value optimized out>, function=<value optimized out>) at assert.c:96
#3 0x000000344f62bb10 in __assert_fail (assertion=0x7f9c378ed6e6 "real_fopen64", file=0x7f9c378ed6de "basic.c", line=21, function=0x7f9c378ed6f3 "fopen64") at assert.c:105
#4 0x00007f9c378ed66a in fopen64 (pathname=0x3451e162f6 "/proc/filesystems", mode=0x3451e161eb "r") at basic.c:21
#5 0x0000003451e0cd2d in init_selinuxmnt () at init.c:49
#6 init_lib () at init.c:118
#7 0x0000003451e15cb6 in __do_global_ctors_aux () from /lib64/libselinux.so.1
#8 0x0000003451e04fa3 in _init () from /lib64/libselinux.so.1
#9 0x00007f9c378c84c8 in ?? ()
#10 0x000000344ee0e555 in call_init (main_map=0x344f021188, argc=1375849744, argv=0x7fffc3f70418, env=0x7fffc3f70428) at dl-init.c:70
#11 _dl_init (main_map=0x344f021188, argc=1375849744, argv=0x7fffc3f70418, env=0x7fffc3f70428) at dl-init.c:134
#12 0x000000344ee00b3a in _dl_start_user () from /lib64/ld-2.12.so
#13 0x0000000000000001 in ?? ()
#14 0x00007fffc3f7230a in ?? ()
#15 0x0000000000000000 in ?? ()
$ cat /etc/redhat-release
Scientific Linux release 6.5 (Carbon)
$ rpm -q glibc
glibc-2.12-1.132.el6_5.2.x86_64