Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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(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
HINSTANCE dlh;
#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");
/* A module handle for the executable. */
lm->dlh = GetModuleHandle(NULL);
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)
#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 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;
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...
*/
PR_SetError(PR_FIND_SYMBOL_ERROR, 0);
return (void*)NULL;
}
#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;
#else
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return NULL;
#endif
}