I came across the same problem with hdante's answer as the commenter: calling __libc_dlsym() crashes with segfault. After reading some glibc sources, I applied the following hack as a workaround:
extern void *_dl_sym(void *, const char *, void *); extern void *dlsym(void *handle, const char *name) { if (!strcmp(name,"dlsym")) return (void*)dlsym; return _dl_sym(handle, name, dlsym); }
SPECIFY two things with this “solution”:
- This code bypasses the lock that runs inside
(__libc_)dlsym() , so to make this thread safe, you need to add some lock. - The test argument
_dl_sym() is the address of the caller, glibc seems to recover this value by expanding the stack, but I just use the address of the function itself. The caller’s address is used internally to find the reference map the caller is in to get things like RTLD_NEXT to the right (and using NULL as the thrid argument will fail the call when using RTLD_NEXT ). However, I did not look at glibc unindind functionality, so I am not 100% sure that the above code will work correctly, and it may happen that it works only by accident ...
The solution presented so far has some significant drawbacks: _dl_sym() acts in contrast to the intended dlsym() in some situations. For example, trying to resolve a character that does not exist, exit the program instead of just returning NULL. To get around this, you can use _dl_sym() to just get the pointer to the original dlsym() and use it for everything else (for example, in the "standard" LD_PRELOAD hook connection without any dlsym binding at all):
extern void *_dl_sym(void *, const char *, void *); extern void *dlsym(void *handle, const char *name) { static void * (*real_dlsym)(void *, const char *)=NULL; if (real_dlsym == NULL) real_dlsym=_dl_sym(RTLD_NEXT, "dlsym", dlsym); if (!strcmp(name,"dlsym")) return (void*)dlsym; return real_dlsym(handle,name); }
source share