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:
Nicolas Williams
2013-09-04 22:23:13 -05:00
parent 3e0fd6449e
commit 3e74e2e3bb
7 changed files with 110 additions and 4 deletions

View File

@@ -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

View File

@@ -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;
}