summaryrefslogtreecommitdiffstats
path: root/nsprpub/pr/src/linking
diff options
context:
space:
mode:
Diffstat (limited to 'nsprpub/pr/src/linking')
-rw-r--r--nsprpub/pr/src/linking/Makefile.in31
-rw-r--r--nsprpub/pr/src/linking/prlink.c1142
2 files changed, 1173 insertions, 0 deletions
diff --git a/nsprpub/pr/src/linking/Makefile.in b/nsprpub/pr/src/linking/Makefile.in
new file mode 100644
index 0000000000..9292e9e91f
--- /dev/null
+++ b/nsprpub/pr/src/linking/Makefile.in
@@ -0,0 +1,31 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = \
+ prlink.c \
+ $(NULL)
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/linking/prlink.c b/nsprpub/pr/src/linking/prlink.c
new file mode 100644
index 0000000000..011ff17e6b
--- /dev/null
+++ b/nsprpub/pr/src/linking/prlink.c
@@ -0,0 +1,1142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+
+#ifdef XP_UNIX
+#ifdef USE_DLFCN
+#include <dlfcn.h>
+/* Define these on systems that don't have them. */
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+#ifndef RTLD_LAZY
+#define RTLD_LAZY RTLD_NOW
+#endif
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+#ifndef RTLD_LOCAL
+#define RTLD_LOCAL 0
+#endif
+#ifdef AIX
+#include <sys/ldr.h>
+#ifndef L_IGNOREUNLOAD /* AIX 4.3.3 does not have L_IGNOREUNLOAD. */
+#define L_IGNOREUNLOAD 0x10000000
+#endif
+#endif
+#elif defined(USE_HPSHL)
+#include <dl.h>
+#endif
+#endif /* XP_UNIX */
+
+#define _PR_DEFAULT_LD_FLAGS PR_LD_LAZY
+
+/*
+ * On these platforms, symbols have a leading '_'.
+ */
+#if defined(XP_OS2) \
+ || ((defined(OPENBSD) || defined(NETBSD)) && !defined(__ELF__))
+#define NEED_LEADING_UNDERSCORE
+#endif
+
+#define PR_LD_PATHW 0x8000 /* for PR_LibSpec_PathnameU */
+
+/************************************************************************/
+
+struct PRLibrary {
+ char* name; /* Our own copy of the name string */
+ PRLibrary* next;
+ int refCount;
+ const PRStaticLinkTable* staticTable;
+
+#ifdef XP_PC
+#ifdef XP_OS2
+ HMODULE dlh;
+#else
+ HINSTANCE dlh;
+#endif
+#endif
+
+#ifdef XP_UNIX
+#if defined(USE_HPSHL)
+ shl_t dlh;
+#else
+ void* dlh;
+#endif
+#endif
+
+};
+
+static PRLibrary *pr_loadmap;
+static PRLibrary *pr_exe_loadmap;
+static PRMonitor *pr_linker_lock;
+static char* _pr_currentLibPath = NULL;
+
+static PRLibrary *pr_LoadLibraryByPathname(const char *name, PRIntn flags);
+
+/************************************************************************/
+
+#if !defined(USE_DLFCN) && !defined(HAVE_STRERROR)
+#define ERR_STR_BUF_LENGTH 20
+#endif
+
+static void DLLErrorInternal(PRIntn oserr)
+/*
+** This whole function, and most of the code in this file, are run
+** with a big hairy lock wrapped around it. Not the best of situations,
+** but will eventually come up with the right answer.
+*/
+{
+ const char *error = NULL;
+#ifdef USE_DLFCN
+ error = dlerror(); /* $$$ That'll be wrong some of the time - AOF */
+#elif defined(HAVE_STRERROR)
+ error = strerror(oserr); /* this should be okay */
+#else
+ char errStrBuf[ERR_STR_BUF_LENGTH];
+ PR_snprintf(errStrBuf, sizeof(errStrBuf), "error %d", oserr);
+ error = errStrBuf;
+#endif
+ if (NULL != error) {
+ PR_SetErrorText(strlen(error), error);
+ }
+} /* DLLErrorInternal */
+
+void _PR_InitLinker(void)
+{
+ PRLibrary *lm = NULL;
+#if defined(XP_UNIX)
+ void *h;
+#endif
+
+ if (!pr_linker_lock) {
+ pr_linker_lock = PR_NewNamedMonitor("linker-lock");
+ }
+ PR_EnterMonitor(pr_linker_lock);
+
+#if defined(XP_PC)
+ lm = PR_NEWZAP(PRLibrary);
+ lm->name = strdup("Executable");
+#if defined(XP_OS2)
+ lm->dlh = NULLHANDLE;
+#else
+ /* A module handle for the executable. */
+ lm->dlh = GetModuleHandle(NULL);
+#endif /* ! XP_OS2 */
+
+ lm->refCount = 1;
+ lm->staticTable = NULL;
+ pr_exe_loadmap = lm;
+ pr_loadmap = lm;
+
+#elif defined(XP_UNIX)
+#ifdef HAVE_DLL
+#if defined(USE_DLFCN) && !defined(NO_DLOPEN_NULL)
+ h = dlopen(0, RTLD_LAZY);
+ if (!h) {
+ char *error;
+
+ DLLErrorInternal(_MD_ERRNO());
+ error = (char*)PR_MALLOC(PR_GetErrorTextLength());
+ (void) PR_GetErrorText(error);
+ fprintf(stderr, "failed to initialize shared libraries [%s]\n",
+ error);
+ PR_DELETE(error);
+ abort();/* XXX */
+ }
+#elif defined(USE_HPSHL)
+ h = NULL;
+ /* don't abort with this NULL */
+#elif defined(NO_DLOPEN_NULL)
+ h = NULL; /* XXXX toshok */ /* XXXX vlad */
+#else
+#error no dll strategy
+#endif /* USE_DLFCN */
+
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm) {
+ lm->name = strdup("a.out");
+ lm->refCount = 1;
+ lm->dlh = h;
+ lm->staticTable = NULL;
+ }
+ pr_exe_loadmap = lm;
+ pr_loadmap = lm;
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+
+ if (lm) {
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("Loaded library %s (init)", lm->name));
+ }
+
+ PR_ExitMonitor(pr_linker_lock);
+}
+
+/*
+ * _PR_ShutdownLinker does not unload the dlls loaded by the application
+ * via calls to PR_LoadLibrary. Any dlls that still remain on the
+ * pr_loadmap list when NSPR shuts down are application programming errors.
+ * The only exception is pr_exe_loadmap, which was added to the list by
+ * NSPR and hence should be cleaned up by NSPR.
+ */
+void _PR_ShutdownLinker(void)
+{
+ /* FIXME: pr_exe_loadmap should be destroyed. */
+
+ PR_DestroyMonitor(pr_linker_lock);
+ pr_linker_lock = NULL;
+
+ if (_pr_currentLibPath) {
+ free(_pr_currentLibPath);
+ _pr_currentLibPath = NULL;
+ }
+}
+
+/******************************************************************************/
+
+PR_IMPLEMENT(PRStatus) PR_SetLibraryPath(const char *path)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_EnterMonitor(pr_linker_lock);
+ if (_pr_currentLibPath) {
+ free(_pr_currentLibPath);
+ }
+ if (path) {
+ _pr_currentLibPath = strdup(path);
+ if (!_pr_currentLibPath) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ } else {
+ _pr_currentLibPath = 0;
+ }
+ PR_ExitMonitor(pr_linker_lock);
+ return rv;
+}
+
+/*
+** Return the library path for finding shared libraries.
+*/
+PR_IMPLEMENT(char *)
+PR_GetLibraryPath(void)
+{
+ char *ev;
+ char *copy = NULL; /* a copy of _pr_currentLibPath */
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_EnterMonitor(pr_linker_lock);
+ if (_pr_currentLibPath != NULL) {
+ goto exit;
+ }
+
+ /* initialize pr_currentLibPath */
+
+#ifdef XP_PC
+ ev = getenv("LD_LIBRARY_PATH");
+ if (!ev) {
+ ev = ".;\\lib";
+ }
+ ev = strdup(ev);
+#endif
+
+#if defined(XP_UNIX)
+#if defined(USE_DLFCN)
+ {
+ char *p=NULL;
+ int len;
+
+ ev = getenv("LD_LIBRARY_PATH");
+ if (!ev) {
+ ev = "/usr/lib:/lib";
+ }
+ len = strlen(ev) + 1; /* +1 for the null */
+
+ p = (char*) malloc(len);
+ if (p) {
+ strcpy(p, ev);
+ } /* if (p) */
+ ev = p;
+ PR_LOG(_pr_io_lm, PR_LOG_NOTICE, ("linker path '%s'", ev));
+
+ }
+#else
+ /* AFAIK there isn't a library path with the HP SHL interface --Rob */
+ ev = strdup("");
+#endif
+#endif
+
+ /*
+ * If ev is NULL, we have run out of memory
+ */
+ _pr_currentLibPath = ev;
+
+exit:
+ if (_pr_currentLibPath) {
+ copy = strdup(_pr_currentLibPath);
+ }
+ PR_ExitMonitor(pr_linker_lock);
+ if (!copy) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return copy;
+}
+
+/*
+** Build library name from path, lib and extensions
+*/
+PR_IMPLEMENT(char*)
+PR_GetLibraryName(const char *path, const char *lib)
+{
+ char *fullname;
+
+#ifdef XP_PC
+ if (strstr(lib, PR_DLL_SUFFIX) == NULL)
+ {
+ if (path) {
+ fullname = PR_smprintf("%s\\%s%s", path, lib, PR_DLL_SUFFIX);
+ } else {
+ fullname = PR_smprintf("%s%s", lib, PR_DLL_SUFFIX);
+ }
+ } else {
+ if (path) {
+ fullname = PR_smprintf("%s\\%s", path, lib);
+ } else {
+ fullname = PR_smprintf("%s", lib);
+ }
+ }
+#endif /* XP_PC */
+#if defined(XP_UNIX)
+ if (strstr(lib, PR_DLL_SUFFIX) == NULL)
+ {
+ if (path) {
+ fullname = PR_smprintf("%s/lib%s%s", path, lib, PR_DLL_SUFFIX);
+ } else {
+ fullname = PR_smprintf("lib%s%s", lib, PR_DLL_SUFFIX);
+ }
+ } else {
+ if (path) {
+ fullname = PR_smprintf("%s/%s", path, lib);
+ } else {
+ fullname = PR_smprintf("%s", lib);
+ }
+ }
+#endif /* XP_UNIX */
+ return fullname;
+}
+
+/*
+** Free the memory allocated, for the caller, by PR_GetLibraryName
+*/
+PR_IMPLEMENT(void)
+PR_FreeLibraryName(char *mem)
+{
+ PR_smprintf_free(mem);
+}
+
+static PRLibrary*
+pr_UnlockedFindLibrary(const char *name)
+{
+ PRLibrary* lm = pr_loadmap;
+ const char* np = strrchr(name, PR_DIRECTORY_SEPARATOR);
+ np = np ? np + 1 : name;
+ while (lm) {
+ const char* cp = strrchr(lm->name, PR_DIRECTORY_SEPARATOR);
+ cp = cp ? cp + 1 : lm->name;
+#ifdef WIN32
+ /* Windows DLL names are case insensitive... */
+ if (strcmpi(np, cp) == 0)
+#elif defined(XP_OS2)
+ if (stricmp(np, cp) == 0)
+#else
+ if (strcmp(np, cp) == 0)
+#endif
+ {
+ /* found */
+ lm->refCount++;
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s incr => %d (find lib)",
+ lm->name, lm->refCount));
+ return lm;
+ }
+ lm = lm->next;
+ }
+ return NULL;
+}
+
+PR_IMPLEMENT(PRLibrary*)
+PR_LoadLibraryWithFlags(PRLibSpec libSpec, PRIntn flags)
+{
+ if (flags == 0) {
+ flags = _PR_DEFAULT_LD_FLAGS;
+ }
+ switch (libSpec.type) {
+ case PR_LibSpec_Pathname:
+ return pr_LoadLibraryByPathname(libSpec.value.pathname, flags);
+#ifdef WIN32
+ case PR_LibSpec_PathnameU:
+ /*
+ * cast to |char *| and set PR_LD_PATHW flag so that
+ * it can be cast back to PRUnichar* in the callee.
+ */
+ return pr_LoadLibraryByPathname((const char*)
+ libSpec.value.pathname_u,
+ flags | PR_LD_PATHW);
+#endif
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+}
+
+PR_IMPLEMENT(PRLibrary*)
+PR_LoadLibrary(const char *name)
+{
+ PRLibSpec libSpec;
+
+ libSpec.type = PR_LibSpec_Pathname;
+ libSpec.value.pathname = name;
+ return PR_LoadLibraryWithFlags(libSpec, 0);
+}
+
+/*
+** Dynamically load a library. Only load libraries once, so scan the load
+** map first.
+*/
+static PRLibrary*
+pr_LoadLibraryByPathname(const char *name, PRIntn flags)
+{
+ PRLibrary *lm;
+ PRLibrary* result = NULL;
+ PRInt32 oserr;
+#ifdef WIN32
+ char utf8name_stack[MAX_PATH];
+ char *utf8name_malloc = NULL;
+ char *utf8name = utf8name_stack;
+ PRUnichar wname_stack[MAX_PATH];
+ PRUnichar *wname_malloc = NULL;
+ PRUnichar *wname = wname_stack;
+ int len;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ /* See if library is already loaded */
+ PR_EnterMonitor(pr_linker_lock);
+
+#ifdef WIN32
+ if (flags & PR_LD_PATHW) {
+ /* cast back what's cast to |char *| for the argument passing. */
+ wname = (LPWSTR) name;
+ } else {
+ int wlen = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
+ if (wlen > MAX_PATH) {
+ wname = wname_malloc = PR_Malloc(wlen * sizeof(PRUnichar));
+ }
+ if (wname == NULL ||
+ !MultiByteToWideChar(CP_ACP, 0, name, -1, wname, wlen)) {
+ oserr = _MD_ERRNO();
+ goto unlock;
+ }
+ }
+ len = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL);
+ if (len > MAX_PATH) {
+ utf8name = utf8name_malloc = PR_Malloc(len);
+ }
+ if (utf8name == NULL ||
+ !WideCharToMultiByte(CP_UTF8, 0, wname, -1,
+ utf8name, len, NULL, NULL)) {
+ oserr = _MD_ERRNO();
+ goto unlock;
+ }
+ /* the list of loaded library names are always kept in UTF-8
+ * on Win32 platforms */
+ result = pr_UnlockedFindLibrary(utf8name);
+#else
+ result = pr_UnlockedFindLibrary(name);
+#endif
+
+ if (result != NULL) {
+ goto unlock;
+ }
+
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm == NULL) {
+ oserr = _MD_ERRNO();
+ goto unlock;
+ }
+ lm->staticTable = NULL;
+
+#ifdef XP_OS2 /* Why isn't all this stuff in MD code?! */
+ {
+ HMODULE h;
+ UCHAR pszError[_MAX_PATH];
+ ULONG ulRc = NO_ERROR;
+
+ ulRc = DosLoadModule(pszError, _MAX_PATH, (PSZ) name, &h);
+ if (ulRc != NO_ERROR) {
+ oserr = ulRc;
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#endif /* XP_OS2 */
+
+#ifdef WIN32
+ {
+ HINSTANCE h;
+
+ h = LoadLibraryExW(wname, NULL,
+ (flags & PR_LD_ALT_SEARCH_PATH) ?
+ LOAD_WITH_ALTERED_SEARCH_PATH : 0);
+ if (h == NULL) {
+ oserr = _MD_ERRNO();
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(utf8name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#endif /* WIN32 */
+
+#if defined(XP_UNIX)
+#ifdef HAVE_DLL
+ {
+#if defined(USE_DLFCN)
+#ifdef NTO
+ /* Neutrino needs RTLD_GROUP to load Netscape plugins. (bug 71179) */
+ int dl_flags = RTLD_GROUP;
+#elif defined(AIX)
+ /* AIX needs RTLD_MEMBER to load an archive member. (bug 228899) */
+ int dl_flags = RTLD_MEMBER;
+#else
+ int dl_flags = 0;
+#endif
+ void *h = NULL;
+#if defined(DARWIN)
+ PRBool okToLoad = PR_FALSE;
+#endif
+
+ if (flags & PR_LD_LAZY) {
+ dl_flags |= RTLD_LAZY;
+ }
+ if (flags & PR_LD_NOW) {
+ dl_flags |= RTLD_NOW;
+ }
+ if (flags & PR_LD_GLOBAL) {
+ dl_flags |= RTLD_GLOBAL;
+ }
+ if (flags & PR_LD_LOCAL) {
+ dl_flags |= RTLD_LOCAL;
+ }
+#if defined(DARWIN)
+ /* If the file contains an absolute or relative path (slash)
+ * and the path doesn't look like a System path, then require
+ * the file exists.
+ * The reason is that DARWIN's dlopen ignores the provided path
+ * and checks for the plain filename in DYLD_LIBRARY_PATH,
+ * which could load an unexpected version of a library. */
+ if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) {
+ /* no slash, allow to load from any location */
+ okToLoad = PR_TRUE;
+ } else {
+ const char systemPrefix1[] = "/System/";
+ const size_t systemPrefixLen1 = strlen(systemPrefix1);
+ const char systemPrefix2[] = "/usr/lib/";
+ const size_t systemPrefixLen2 = strlen(systemPrefix2);
+ const size_t name_len = strlen(name);
+ if (((name_len > systemPrefixLen1) &&
+ (strncmp(name, systemPrefix1, systemPrefixLen1) == 0)) ||
+ ((name_len > systemPrefixLen2) &&
+ (strncmp(name, systemPrefix2, systemPrefixLen2) == 0))) {
+ /* found at beginning, it's a system library.
+ * Skip filesystem check (required for macOS 11),
+ * allow loading from any location */
+ okToLoad = PR_TRUE;
+ } else if (PR_Access(name, PR_ACCESS_EXISTS) == PR_SUCCESS) {
+ /* file exists, allow to load */
+ okToLoad = PR_TRUE;
+ }
+ }
+ if (okToLoad) {
+ h = dlopen(name, dl_flags);
+ }
+#else
+ h = dlopen(name, dl_flags);
+#endif
+#elif defined(USE_HPSHL)
+ int shl_flags = 0;
+ shl_t h;
+
+ /*
+ * Use the DYNAMIC_PATH flag only if 'name' is a plain file
+ * name (containing no directory) to match the behavior of
+ * dlopen().
+ */
+ if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) {
+ shl_flags |= DYNAMIC_PATH;
+ }
+ if (flags & PR_LD_LAZY) {
+ shl_flags |= BIND_DEFERRED;
+ }
+ if (flags & PR_LD_NOW) {
+ shl_flags |= BIND_IMMEDIATE;
+ }
+ /* No equivalent of PR_LD_GLOBAL and PR_LD_LOCAL. */
+ h = shl_load(name, shl_flags, 0L);
+#else
+#error Configuration error
+#endif
+ if (!h) {
+ oserr = _MD_ERRNO();
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+
+ lm->refCount = 1;
+
+ result = lm; /* success */
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (load lib)", lm->name));
+
+unlock:
+ if (result == NULL) {
+ PR_SetError(PR_LOAD_LIBRARY_ERROR, oserr);
+ DLLErrorInternal(oserr); /* sets error text */
+ }
+#ifdef WIN32
+ if (utf8name_malloc) {
+ PR_Free(utf8name_malloc);
+ }
+ if (wname_malloc) {
+ PR_Free(wname_malloc);
+ }
+#endif
+ PR_ExitMonitor(pr_linker_lock);
+ return result;
+}
+
+/*
+** Unload a shared library which was loaded via PR_LoadLibrary
+*/
+PR_IMPLEMENT(PRStatus)
+PR_UnloadLibrary(PRLibrary *lib)
+{
+ int result = 0;
+ PRStatus status = PR_SUCCESS;
+
+ if (lib == 0) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ PR_EnterMonitor(pr_linker_lock);
+
+ if (lib->refCount <= 0) {
+ PR_ExitMonitor(pr_linker_lock);
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (--lib->refCount > 0) {
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s decr => %d",
+ lib->name, lib->refCount));
+ goto done;
+ }
+
+#ifdef XP_UNIX
+#ifdef HAVE_DLL
+#ifdef USE_DLFCN
+ result = dlclose(lib->dlh);
+#elif defined(USE_HPSHL)
+ result = shl_unload(lib->dlh);
+#else
+#error Configuration error
+#endif
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+#ifdef XP_PC
+ if (lib->dlh) {
+ FreeLibrary((HINSTANCE)(lib->dlh));
+ lib->dlh = (HINSTANCE)NULL;
+ }
+#endif /* XP_PC */
+
+ /* unlink from library search list */
+ if (pr_loadmap == lib) {
+ pr_loadmap = pr_loadmap->next;
+ }
+ else if (pr_loadmap != NULL) {
+ PRLibrary* prev = pr_loadmap;
+ PRLibrary* next = pr_loadmap->next;
+ while (next != NULL) {
+ if (next == lib) {
+ prev->next = next->next;
+ goto freeLib;
+ }
+ prev = next;
+ next = next->next;
+ }
+ /*
+ * fail (the library is not on the _pr_loadmap list),
+ * but don't wipe out an error from dlclose/shl_unload.
+ */
+ PR_NOT_REACHED("_pr_loadmap and lib->refCount inconsistent");
+ if (result == 0) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ status = PR_FAILURE;
+ }
+ }
+ /*
+ * We free the PRLibrary structure whether dlclose/shl_unload
+ * succeeds or not.
+ */
+
+freeLib:
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Unloaded library %s", lib->name));
+ free(lib->name);
+ lib->name = NULL;
+ PR_DELETE(lib);
+ if (result != 0) {
+ PR_SetError(PR_UNLOAD_LIBRARY_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ status = PR_FAILURE;
+ }
+
+done:
+ PR_ExitMonitor(pr_linker_lock);
+ return status;
+}
+
+static void*
+pr_FindSymbolInLib(PRLibrary *lm, const char *name)
+{
+ void *f = NULL;
+#ifdef XP_OS2
+ int rc;
+#endif
+
+ if (lm->staticTable != NULL) {
+ const PRStaticLinkTable* tp;
+ for (tp = lm->staticTable; tp->name; tp++) {
+ if (strcmp(name, tp->name) == 0) {
+ return (void*) tp->fp;
+ }
+ }
+ /*
+ ** If the symbol was not found in the static table then check if
+ ** the symbol was exported in the DLL... Win16 only!!
+ */
+#if !defined(WIN16)
+ PR_SetError(PR_FIND_SYMBOL_ERROR, 0);
+ return (void*)NULL;
+#endif
+ }
+
+#ifdef XP_OS2
+ rc = DosQueryProcAddr(lm->dlh, 0, (PSZ) name, (PFN *) &f);
+#if defined(NEED_LEADING_UNDERSCORE)
+ /*
+ * Older plugins (not built using GCC) will have symbols that are not
+ * underscore prefixed. We check for that here.
+ */
+ if (rc != NO_ERROR) {
+ name++;
+ DosQueryProcAddr(lm->dlh, 0, (PSZ) name, (PFN *) &f);
+ }
+#endif
+#endif /* XP_OS2 */
+
+#ifdef WIN32
+ f = GetProcAddress(lm->dlh, name);
+#endif /* WIN32 */
+
+#ifdef XP_UNIX
+#ifdef HAVE_DLL
+#ifdef USE_DLFCN
+ f = dlsym(lm->dlh, name);
+#elif defined(USE_HPSHL)
+ if (shl_findsym(&lm->dlh, name, TYPE_PROCEDURE, &f) == -1) {
+ f = NULL;
+ }
+#endif
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+ if (f == NULL) {
+ PR_SetError(PR_FIND_SYMBOL_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ }
+ return f;
+}
+
+/*
+** Called by class loader to resolve missing native's
+*/
+PR_IMPLEMENT(void*)
+PR_FindSymbol(PRLibrary *lib, const char *raw_name)
+{
+ void *f = NULL;
+#if defined(NEED_LEADING_UNDERSCORE)
+ char *name;
+#else
+ const char *name;
+#endif
+ /*
+ ** Mangle the raw symbol name in any way that is platform specific.
+ */
+#if defined(NEED_LEADING_UNDERSCORE)
+ /* Need a leading _ */
+ name = PR_smprintf("_%s", raw_name);
+#elif defined(AIX)
+ /*
+ ** AIX with the normal linker put's a "." in front of the symbol
+ ** name. When use "svcc" and "svld" then the "." disappears. Go
+ ** figure.
+ */
+ name = raw_name;
+#else
+ name = raw_name;
+#endif
+
+ PR_EnterMonitor(pr_linker_lock);
+ PR_ASSERT(lib != NULL);
+ f = pr_FindSymbolInLib(lib, name);
+
+#if defined(NEED_LEADING_UNDERSCORE)
+ PR_smprintf_free(name);
+#endif
+
+ PR_ExitMonitor(pr_linker_lock);
+ return f;
+}
+
+/*
+** Return the address of the function 'raw_name' in the library 'lib'
+*/
+PR_IMPLEMENT(PRFuncPtr)
+PR_FindFunctionSymbol(PRLibrary *lib, const char *raw_name)
+{
+ return ((PRFuncPtr) PR_FindSymbol(lib, raw_name));
+}
+
+PR_IMPLEMENT(void*)
+PR_FindSymbolAndLibrary(const char *raw_name, PRLibrary* *lib)
+{
+ void *f = NULL;
+#if defined(NEED_LEADING_UNDERSCORE)
+ char *name;
+#else
+ const char *name;
+#endif
+ PRLibrary* lm;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ /*
+ ** Mangle the raw symbol name in any way that is platform specific.
+ */
+#if defined(NEED_LEADING_UNDERSCORE)
+ /* Need a leading _ */
+ name = PR_smprintf("_%s", raw_name);
+#elif defined(AIX)
+ /*
+ ** AIX with the normal linker put's a "." in front of the symbol
+ ** name. When use "svcc" and "svld" then the "." disappears. Go
+ ** figure.
+ */
+ name = raw_name;
+#else
+ name = raw_name;
+#endif
+
+ PR_EnterMonitor(pr_linker_lock);
+
+ /* search all libraries */
+ for (lm = pr_loadmap; lm != NULL; lm = lm->next) {
+ f = pr_FindSymbolInLib(lm, name);
+ if (f != NULL) {
+ *lib = lm;
+ lm->refCount++;
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s incr => %d (for %s)",
+ lm->name, lm->refCount, name));
+ break;
+ }
+ }
+#if defined(NEED_LEADING_UNDERSCORE)
+ PR_smprintf_free(name);
+#endif
+
+ PR_ExitMonitor(pr_linker_lock);
+ return f;
+}
+
+PR_IMPLEMENT(PRFuncPtr)
+PR_FindFunctionSymbolAndLibrary(const char *raw_name, PRLibrary* *lib)
+{
+ return ((PRFuncPtr) PR_FindSymbolAndLibrary(raw_name, lib));
+}
+
+/*
+** Add a static library to the list of loaded libraries. If LoadLibrary
+** is called with the name then we will pretend it was already loaded
+*/
+PR_IMPLEMENT(PRLibrary*)
+PR_LoadStaticLibrary(const char *name, const PRStaticLinkTable *slt)
+{
+ PRLibrary *lm=NULL;
+ PRLibrary* result = NULL;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ /* See if library is already loaded */
+ PR_EnterMonitor(pr_linker_lock);
+
+ /* If the lbrary is already loaded, then add the static table information... */
+ result = pr_UnlockedFindLibrary(name);
+ if (result != NULL) {
+ PR_ASSERT( (result->staticTable == NULL) || (result->staticTable == slt) );
+ result->staticTable = slt;
+ goto unlock;
+ }
+
+ /* Add library to list...Mark it static */
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm == NULL) {
+ goto unlock;
+ }
+
+ lm->name = strdup(name);
+ lm->refCount = 1;
+ lm->dlh = pr_exe_loadmap ? pr_exe_loadmap->dlh : 0;
+ lm->staticTable = slt;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+
+ result = lm; /* success */
+ PR_ASSERT(lm->refCount == 1);
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (static lib)", lm->name));
+unlock:
+ PR_ExitMonitor(pr_linker_lock);
+ return result;
+}
+
+PR_IMPLEMENT(char *)
+PR_GetLibraryFilePathname(const char *name, PRFuncPtr addr)
+{
+#if defined(USE_DLFCN) && defined(HAVE_DLADDR)
+ Dl_info dli;
+ char *result;
+
+ if (dladdr((void *)addr, &dli) == 0) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ result = PR_Malloc(strlen(dli.dli_fname)+1);
+ if (result != NULL) {
+ strcpy(result, dli.dli_fname);
+ }
+ return result;
+#elif defined(AIX)
+ char *result;
+#define LD_INFO_INCREMENT 64
+ struct ld_info *info;
+ unsigned int info_length = LD_INFO_INCREMENT * sizeof(struct ld_info);
+ struct ld_info *infop;
+ int loadflags = L_GETINFO | L_IGNOREUNLOAD;
+
+ for (;;) {
+ info = PR_Malloc(info_length);
+ if (info == NULL) {
+ return NULL;
+ }
+ /* If buffer is too small, loadquery fails with ENOMEM. */
+ if (loadquery(loadflags, info, info_length) != -1) {
+ break;
+ }
+ /*
+ * Calling loadquery when compiled for 64-bit with the
+ * L_IGNOREUNLOAD flag can cause an invalid argument error
+ * on AIX 5.1. Detect this error the first time that
+ * loadquery is called, and try calling it again without
+ * this flag set.
+ */
+ if (errno == EINVAL && (loadflags & L_IGNOREUNLOAD)) {
+ loadflags &= ~L_IGNOREUNLOAD;
+ if (loadquery(loadflags, info, info_length) != -1) {
+ break;
+ }
+ }
+ PR_Free(info);
+ if (errno != ENOMEM) {
+ /* should not happen */
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ return NULL;
+ }
+ /* retry with a larger buffer */
+ info_length += LD_INFO_INCREMENT * sizeof(struct ld_info);
+ }
+
+ for (infop = info;
+ ;
+ infop = (struct ld_info *)((char *)infop + infop->ldinfo_next)) {
+ unsigned long start = (unsigned long)infop->ldinfo_dataorg;
+ unsigned long end = start + infop->ldinfo_datasize;
+ if (start <= (unsigned long)addr && end > (unsigned long)addr) {
+ result = PR_Malloc(strlen(infop->ldinfo_filename)+1);
+ if (result != NULL) {
+ strcpy(result, infop->ldinfo_filename);
+ }
+ break;
+ }
+ if (!infop->ldinfo_next) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, 0);
+ result = NULL;
+ break;
+ }
+ }
+ PR_Free(info);
+ return result;
+#elif defined(HPUX) && defined(USE_HPSHL)
+ int index;
+ struct shl_descriptor desc;
+ char *result;
+
+ for (index = 0; shl_get_r(index, &desc) == 0; index++) {
+ if (strstr(desc.filename, name) != NULL) {
+ result = PR_Malloc(strlen(desc.filename)+1);
+ if (result != NULL) {
+ strcpy(result, desc.filename);
+ }
+ return result;
+ }
+ }
+ /*
+ * Since the index value of a library is decremented if
+ * a library preceding it in the shared library search
+ * list was unloaded, it is possible that we missed some
+ * libraries as we went up the list. So we should go
+ * down the list to be sure that we not miss anything.
+ */
+ for (index--; index >= 0; index--) {
+ if ((shl_get_r(index, &desc) == 0)
+ && (strstr(desc.filename, name) != NULL)) {
+ result = PR_Malloc(strlen(desc.filename)+1);
+ if (result != NULL) {
+ strcpy(result, desc.filename);
+ }
+ return result;
+ }
+ }
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, 0);
+ return NULL;
+#elif defined(HPUX) && defined(USE_DLFCN)
+ struct load_module_desc desc;
+ char *result;
+ const char *module_name;
+
+ if (dlmodinfo((unsigned long)addr, &desc, sizeof desc, NULL, 0, 0) == 0) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ module_name = dlgetname(&desc, sizeof desc, NULL, 0, 0);
+ if (module_name == NULL) {
+ /* should not happen */
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ result = PR_Malloc(strlen(module_name)+1);
+ if (result != NULL) {
+ strcpy(result, module_name);
+ }
+ return result;
+#elif defined(WIN32)
+ PRUnichar wname[MAX_PATH];
+ HMODULE handle = NULL;
+ PRUnichar module_name[MAX_PATH];
+ int len;
+ char *result;
+
+ if (MultiByteToWideChar(CP_ACP, 0, name, -1, wname, MAX_PATH)) {
+ handle = GetModuleHandleW(wname);
+ }
+ if (handle == NULL) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ if (GetModuleFileNameW(handle, module_name, MAX_PATH) == 0) {
+ /* should not happen */
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ return NULL;
+ }
+ len = WideCharToMultiByte(CP_ACP, 0, module_name, -1,
+ NULL, 0, NULL, NULL);
+ if (len == 0) {
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ return NULL;
+ }
+ result = PR_Malloc(len * sizeof(PRUnichar));
+ if (result != NULL) {
+ WideCharToMultiByte(CP_ACP, 0, module_name, -1,
+ result, len, NULL, NULL);
+ }
+ return result;
+#elif defined(XP_OS2)
+ HMODULE module = NULL;
+ char module_name[_MAX_PATH];
+ char *result;
+ APIRET ulrc = DosQueryModFromEIP(&module, NULL, 0, NULL, NULL, (ULONG) addr);
+ if ((NO_ERROR != ulrc) || (NULL == module) ) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ ulrc = DosQueryModuleName(module, sizeof module_name, module_name);
+ if (NO_ERROR != ulrc) {
+ /* should not happen */
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ return NULL;
+ }
+ result = PR_Malloc(strlen(module_name)+1);
+ if (result != NULL) {
+ strcpy(result, module_name);
+ }
+ return result;
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+#endif
+}