Fix some DLL hell: use dladdr() to find plugin dir
Normally one would dlopen() a shared object's basename, not its absolute path. However, lib/krb5/plugin.c, in an effort to be zero-conf-ish, wants to readdir() to find plugins to load, and in the process it ends up defeating the RTLD's search-the-caller's-rpath. This commit partially addresses this by allowing the use of $ORIGIN in plugin_dir values and using them for the default (except on OS X). This allows multiple Heimdal versions installed on the same host, but with different plugin ABIs, to co-exist. A step forward for doing make check on hosts where Heimdal is installed. For now we hardcode $ORIGIN/../lib/plugin/krb5 (linux, Solaris, *BSD), or $ORIGIN (Windows; for assemblies objects need to be in the same directory) and we eval $ORIGIN by using dladdr() (Linux, Solaris) or GetModuleHandleEx() (Win32, via a dladdr() wrapper in libroken) to find the path to libkrb5 whose dirname to use as $ORIGIN. For Windows, because we need the plugins to be in the same directory as libkrb5, we require a prefix on plugin DLLs ("plugin_krb5_") to distinguish them from other objects. We should add a special token to mean "look in $ORIGIN, sure, but dlopen() the plugin basenames only (so the RTLD can search the rpath)".
This commit is contained in:
@@ -32,6 +32,8 @@
|
||||
#ifndef __dlfcn_h__
|
||||
#define __dlfcn_h__
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef ROKEN_LIB_FUNCTION
|
||||
#ifdef _WIN32
|
||||
#define ROKEN_LIB_FUNCTION
|
||||
@@ -75,6 +77,11 @@ dlopen(const char *, int);
|
||||
ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
|
||||
dlsym(void *, const char *);
|
||||
|
||||
typedef struct Dl_info {
|
||||
char *dli_fname;
|
||||
char _dli_buf[MAX_PATH + 2];
|
||||
} Dl_info, Dl_info_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
@@ -163,3 +163,38 @@ dlsym(void * vhm, const char * func_name)
|
||||
return (DLSYM_RET_TYPE)(ULONG_PTR)GetProcAddress(hm, func_name);
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
dladdr(void *addr, Dl_info *dli)
|
||||
{
|
||||
HMODULE hm;
|
||||
int ret;
|
||||
DWORD nsize;
|
||||
char *p;
|
||||
|
||||
memset(dli, 0, sizeof(*dli));
|
||||
|
||||
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCTSTR)addr, &hm))
|
||||
return -1;
|
||||
|
||||
nsize = GetModuleFileName(hm, dli->_dli_buf, sizeof(dli->_dli_buf));
|
||||
dli->_dli_buf[sizeof(dli->_dli_buf) - 1] = '\0';
|
||||
if (nsize >= sizeof(dli->_dli_buf))
|
||||
return 0; /* truncated? can't be... */
|
||||
|
||||
/*
|
||||
* Normalize path component separators, since our caller may want to
|
||||
* portably take the dirname or basename of dli->dli_fname,
|
||||
* searching for the last '/'.
|
||||
*/
|
||||
for (p = dli->_dli_buf;
|
||||
p < &dli->_dli_buf[sizeof(dli->_dli_buf) - 1] && *p;
|
||||
p++) {
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
}
|
||||
|
||||
dli->dli_fname = dli->_dli_buf;
|
||||
return 1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user