#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "prmem.h"
#include "prmon.h"
#include "prlog.h"
#include "prlink.h"
#include "prprf.h"
#ifdef SW_THREADS
#include "swkern.h"
#else
#include "hwkern.h"
#endif
#ifdef XP_MAC
#include <ConditionalMacros.h>
#include <CodeFragments.h>
#include <TextUtils.h>
#include <Types.h>
#include <Strings.h>
#include "prmacos.h"
#endif
#ifdef XP_UNIX
#ifdef USE_DLFCN
#include <dlfcn.h>
#elif defined(USE_HPSHL)
#include <dl.h>
#endif
#undef HAVE_DLL /* jwz -- kludge */
/* Define this on systems which don't have it (AIX) */
#ifndef RTLD_LAZY
#define RTLD_LAZY 1
#endif
#endif /* XP_UNIX */
#ifdef XP_PC
typedef PRStaticLinkTable *NODL_PROC(void);
#endif
struct PRLibraryStr {
const char* name;
PRLibrary* next;
int refCount;
#ifdef XP_PC
HINSTANCE dlh;
#endif
#ifdef XP_MAC
CFragConnectionID dlh;
#endif
#ifdef XP_UNIX
#if defined(USE_HPSHL)
shl_t dlh;
#else
void* dlh;
#endif
#endif
const PRStaticLinkTable* staticTable;
};
PR_LOG_DEFINE(Linker);
static PRLibrary *pr_loadmap;
static PRLibrary *pr_exe_loadmap;
static PRMonitor *pr_linker_lock;
static char* pr_current_lib_path = NULL;
static char needInit = 1;
/************************************************************************/
#if ( !defined(USE_DLFCN) && !defined(HAVE_STRERROR) ) || defined(HPUX) || defined(BSDI) || defined(LINUX)
static char* errStrBuf = NULL;
#define ERR_STR_BUF_LENGTH 20
static char*
errno_string()
{
if (errStrBuf == NULL) {
errStrBuf = malloc(ERR_STR_BUF_LENGTH);
if (errStrBuf == NULL) return NULL;
}
PR_snprintf(errStrBuf, ERR_STR_BUF_LENGTH, "error %d", errno);
return errStrBuf;
}
#endif
#ifdef HAVE_STRERROR
#define STRERROR(err) strerror(err)
#else
#define STRERROR(err) errno_string()
#endif
static char*
pr_DLLError(void)
{
/*
** We have to be in the pr_linker_lock in this routine because
** pr_DLLError is not thread-safe.
*/
PR_ASSERT(PR_InMonitor(pr_linker_lock));
#ifdef HAVE_DLL
# ifdef USE_DLFCN
return dlerror();
# elif defined(USE_HPSHL)
return STRERROR(errno);
# else
return STRERROR(errno);
# endif
#elif (defined(XP_MAC) && !GENERATINGCFM)
return NULL;
#else
return STRERROR(errno);
#endif /* HAVE_DLL */
}
static void
InitLinker(void)
{
PRLibrary *lm;
#if defined(XP_UNIX)
void *h;
#endif
if (!pr_linker_lock) {
pr_linker_lock = PR_NewNamedMonitor(1, "linker-lock");
} else
PR_EnterMonitor(pr_linker_lock);
if (!pr_loadmap) {
#if defined(XP_PC)
lm = (PRLibrary*) calloc(1, sizeof(PRLibrary));
lm->name = "Executable";
/*
** In WIN32, GetProcAddress(...) expects a module handle in order to
** get exported symbols from the executable...
**
** However, in WIN16 this is accomplished by passing NULL to
** GetProcAddress(...)
*/
#if defined(_WIN32)
lm->dlh = GetModuleHandle(NULL);
#else
lm->dlh = (HINSTANCE)NULL;
#endif /* ! _WIN32 */
lm->refCount = 1;
lm->staticTable = NULL;
pr_exe_loadmap = lm;
pr_loadmap = lm;
#elif defined(XP_MAC)
#pragma unused lm
#pragma unused h
// DebugStr((StringPtr)"\pwrite me");
// FIX ME
#elif defined(XP_UNIX)
#ifdef HAVE_DLL
#ifdef USE_DLFCN
h = dlopen(0, RTLD_LAZY);
if (!h) {
fprintf(stderr, "failed to initialize shared libraries [%s]\n",
pr_DLLError());
abort();/* XXX */
}
#elif defined(USE_HPSHL)
#if 0 /* XXXrobm HP man pages don't say you can send a NULL path */
h = shl_load(NULL, BIND_DEFERRED, NULL);
#else
h = NULL;
#endif
/* don't abort with this NULL */
#else
#error no dll strategy
#endif /* USE_DLFCN */
lm = (PRLibrary*) calloc(1, sizeof(PRLibrary));
lm->name = "a.out";
lm->refCount = 1;
lm->dlh = h;
lm->staticTable = NULL;
pr_exe_loadmap = lm;
pr_loadmap = lm;
#endif /* HAVE_DLL */
#endif /* XP_UNIX */
}
needInit = 0;
PR_LOG(Linker, debug, ("Loaded library %s (init)", lm->name));
PR_ExitMonitor(pr_linker_lock);
}
void _PR_ShutdownLinker(void)
{
if (needInit)
return;
PR_EnterMonitor(pr_linker_lock);
while (pr_loadmap) {
PR_UnloadLibrary(pr_loadmap);
}
PR_ExitMonitor(pr_linker_lock);
PR_DestroyMonitor(pr_linker_lock);
pr_linker_lock = NULL;
}
/******************************************************************************/
PR_PUBLIC_API(void)
PR_SetLibraryPath(const char *path)
{
pr_current_lib_path = (char*)path;
}
/*
** Return the library path for finding shared libraries.
*/
PR_PUBLIC_API(const char *)
PR_GetLibraryPath(void)
{
char *ev;
if (pr_current_lib_path != NULL)
return pr_current_lib_path;
/* else initialize pr_current_lib_path: */
#ifdef XP_PC
ev = getenv("LD_LIBRARY_PATH");
if (!ev) {
ev = ".;\\lib";
}
#endif
#ifdef XP_MAC
ev = "";
#endif
#ifdef XP_UNIX
#if defined USE_DLFCN
{
char *home;
char *local;
char *p;
int len;
ev = getenv("LD_LIBRARY_PATH");
if (!ev) {
ev = "/usr/lib:/lib";
}
home = getenv("HOME");
/*
** Augment the path automatically by adding in ~/.netscape and
** /usr/local/netscape
*/
len = strlen(ev) + 1; /* +1 for the null */
if (home && home[0]) {
len += strlen(home) + 1; /* +1 for the colon */
}
local = ":/usr/local/netscape/lib/" PR_LINKER_ARCH;
len += strlen(local); /* already got the : */
p = (char*) malloc(len+50);
strcpy(p, ev);
if (home) {
strcat(p, ":");
strcat(p, home);
}
strcat(p, local);
ev = p;
PR_LOG(IO, warn, ("linker path '%s'", ev));
}
#else
/* AFAIK there isn't a library path with the HP SHL interface --Rob */
ev = "";
#endif
#endif
pr_current_lib_path = ev;
return ev;
}
/*
** Build library name from path, lib and extensions
*/
PR_PUBLIC_API(char*)
PR_GetLibName(const char *path, const char *lib)
{
char *fullname;
#ifdef XP_PC
fullname = PR_smprintf("%s\\%s.dll", path, lib);
#endif
#ifdef XP_MAC
fullname = PR_smprintf("%s%s", path, lib);
#endif
#ifdef XP_UNIX
fullname = PR_smprintf("%s/lib%s.so", path, lib);
#endif
return fullname;
}
static PRLibrary*
pr_UnlockedFindLibrary(const char *name)
{
PRLibrary* lm = pr_loadmap;
const char* np = strrchr(name, DIRECTORY_SEPARATOR);
np = np ? np + 1 : name;
while (lm) {
const char* cp = strrchr(lm->name, DIRECTORY_SEPARATOR);
cp = cp ? cp + 1 : lm->name;
#ifdef XP_PC
/* Windows DLL names are case insensitive... */
if (strcmpi(np, cp) == 0) {
#else
if (strcmp(np, cp) == 0) {
#endif
/* found */
lm->refCount++;
PR_LOG(Linker, debug,
("%s incr => %d (find lib)",
lm->name, lm->refCount));
return lm;
}
lm = lm->next;
}
return NULL;
}
/*
** Dynamically load a library. Only load libraries once, so scan the load
** map first.
*/
PR_PUBLIC_API(PRLibrary*)
PR_LoadLibrary(const char *name)
{
PRLibrary *lm;
PRLibrary* result;
/* Initialize the linker if necessary */
if (needInit) InitLinker();
/* See if library is already loaded */
PR_EnterMonitor(pr_linker_lock);
result = pr_UnlockedFindLibrary(name);
if (result != NULL) goto unlock;
lm = (PRLibrary*) calloc(1, sizeof(PRLibrary));
if (lm == NULL) goto unlock;
lm->staticTable = NULL;
#ifdef XP_PC
{
NODL_PROC *pfn;
HINSTANCE h;
h = LoadLibrary(name);
if (h < (HINSTANCE)HINSTANCE_ERROR) {
free(lm);
goto unlock;
}
lm->name = strdup(name);
lm->dlh = h;
lm->next = pr_loadmap;
pr_loadmap = lm;
/*
** Try to load a table of "static functions" provided by the DLL
*/
pfn = (NODL_PROC *)GetProcAddress(h, "NODL_TABLE");
if (pfn != NULL) {
lm->staticTable = (*pfn)();
}
}
#endif
#if defined(XP_MAC) && GENERATINGCFM
{
OSErr err;
Ptr main;
CFragConnectionID connectionID;
Str255 errName;
Str255 pName;
char cName[64];
/*
* Algorithm: The "name" passed in could be either a shared
* library name that we should look for in the normal library
* search paths, or a full path name to a specific library on
* disk. Since the full path will always contain a ":"
* (shortest possible path is "Volume:File"), and since a
* library name can not contain a ":", we can test for the
* presence of a ":" to see which type of library we should load.
*/
if (strchr(name, MAC_PATH_SEPARATOR) == NULL)
{
/*
* The name did not contain a ":", so it must be a
* library name. Convert the name to a Pascal string
* and try to find the library.
*/
OSType archType;
#ifdef GENERATINGPOWERPC
archType = kPowerPCCFragArch;
#else
archType = kMotorola68KCFragArch;
#endif
strcpy((char *)&pName, name);
c2pstr((char *)&pName);
/* Try to find the specified library */
err = GetSharedLibrary(pName, archType, kLoadCFrag, &connectionID,
&main, errName);
if (err != noErr)
goto unlock;
strcpy(cName, name);
}
else
{
/*
* The name did contain a ":", so it must be a full path name.
* Now we have to do a lot of work to convert the path name to
* an FSSpec (silly, since we were probably just called from the
* MacFE plug-in code that already knew the FSSpec and converted
* it to a full path just to pass to us). First we copy out the
* volume name (the text leading up to the first ":"); then we
* separate the file name (the text following the last ":") from
* rest of the path. After converting the strings to Pascal
* format we can call GetCatInfo to get the parent directory ID
* of the file, and then (finally) make an FSSpec and call
* GetDiskFragment.
*/
char* cMacPath = NULL;
char* cFileName = NULL;
char* position = NULL;
CInfoPBRec pb;
FSSpec fileSpec;
uint32 index;
/* Copy the name: we'll change it */
cMacPath = strdup(name);
if (cMacPath == NULL)
goto unlock;
/* First, get the vRefNum */
position = strchr(cMacPath, MAC_PATH_SEPARATOR);
if ((position == cMacPath) || (position == NULL))
fileSpec.vRefNum = 0; /* Use application relative searching */
else
{
char cVolName[32];
memset(cVolName, 0, sizeof(cVolName));
strncpy(cVolName, cMacPath, position-cMacPath);
fileSpec.vRefNum = GetVolumeRefNumFromName(cVolName);
}
/* Next, break the path and file name apart */
index = 0;
while (cMacPath[index] != 0)
index++;
while (cMacPath[index] != MAC_PATH_SEPARATOR && index > 0)
index--;
if (index == 0 || index == strlen(cMacPath))
{
free(cMacPath);
goto unlock;
}
cMacPath[index] = 0;
cFileName = &(cMacPath[index + 1]);
/* Convert the path and name into Pascal strings */
strcpy((char*) &pName, cMacPath);
c2pstr((char*) &pName);
strcpy((char*) &fileSpec.name, cFileName);
c2pstr((char*) &fileSpec.name);
strcpy(cName, cFileName);
free(cMacPath);
cMacPath = NULL;
/* Now we can look up the path on the volume */
pb.dirInfo.ioNamePtr = pName;
pb.dirInfo.ioVRefNum = fileSpec.vRefNum;
pb.dirInfo.ioDrDirID = 0;
pb.dirInfo.ioFDirIndex = 0;
err = PBGetCatInfoSync(&pb);
if (err != noErr)
goto unlock;
fileSpec.parID = pb.dirInfo.ioDrDirID;
/* Finally, try to load the library */
err = GetDiskFragment(&fileSpec, 0, kCFragGoesToEOF, fileSpec.name,
kLoadCFrag, &connectionID, &main, errName);
if (err != noErr)
goto unlock;
}
lm->name = strdup(cName);
lm->dlh = connectionID;
lm->next = pr_loadmap;
pr_loadmap = lm;
}
#elif defined(XP_MAC) && !GENERATINGCFM
{
}
#endif
#ifdef XP_UNIX
#ifdef HAVE_DLL
{
#ifdef USE_DLFCN
void *h;
h = dlopen(name, RTLD_LAZY);
#else /* USE_HPSHL */
shl_t h = shl_load(name, BIND_DEFERRED, NULL);
#endif
if (!h) {
free(lm);
goto unlock;
}
lm->name = strdup(name);
lm->dlh = h;
lm->next = pr_loadmap;
pr_loadmap = lm;
#if 0
fprintf(stderr, "nspr: loaded library %s\n", name);
#endif
}
#endif /* HAVE_DLL */
#endif /* XP_UNIX */
lm->refCount = 1;
result = lm; /* success */
PR_ASSERT(lm->refCount == 1);
PR_LOG(Linker, debug, ("Loaded library %s (load lib)", lm->name));
unlock:
if (result == NULL)
_pr_current_thread->errstr = pr_DLLError();
PR_ExitMonitor(pr_linker_lock);
return result;
}
PR_PUBLIC_API(PRLibrary*)
PR_FindLibrary(const char *name)
{
PRLibrary* result;
/* Initialize the linker if necessary */
if (needInit) InitLinker();
PR_EnterMonitor(pr_linker_lock);
result = pr_UnlockedFindLibrary(name);
PR_ExitMonitor(pr_linker_lock);
return result;
}
/*
** Unload a shared library which was loaded via PR_LoadLibrary
*/
PR_PUBLIC_API(int)
PR_UnloadLibrary(PRLibrary *lib)
{
int result = 0;
if (lib == 0) return 0;
/* Initialize the linker if necessary */
if (needInit) InitLinker();
PR_EnterMonitor(pr_linker_lock);
if (--lib->refCount > 0) {
PR_LOG(Linker, debug,
("%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);
#endif
#endif /* HAVE_DLL */
#endif /* XP_UNIX */
#ifdef XP_PC
if (lib->dlh) {
FreeLibrary((HINSTANCE)(lib->dlh));
lib->dlh = (HINSTANCE)NULL;
}
#endif /* XP_PC */
#if defined(XP_MAC) && GENERATINGCFM
/* Close the connection */
CloseConnection(&(lib->dlh));
#endif
/* 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 (but don't wipe out an error from dlclose/shl_unload) */
if (result == 0) {
result = -1;
}
}
freeLib:
PR_LOG(Linker, debug, ("Unloaded library %s", lib->name));
free(lib);
done:
if (result == -1)
_pr_current_thread->errstr = pr_DLLError();
PR_ExitMonitor(pr_linker_lock);
return result;
}
static FARPROC
pr_FindSymbolInLib(PRLibrary *lm, const char *name)
{
FARPROC f=0;
if (lm->staticTable != NULL) {
const PRStaticLinkTable* tp;
for (tp = lm->staticTable; tp->name; tp++) {
if (strcmp(name, tp->name) == 0) {
return (FARPROC) 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(XP_PC) || defined(_WIN32)
return (FARPROC)NULL;
#endif
}
#ifdef XP_PC
f = GetProcAddress(lm->dlh, name);
#endif /* XP_PC */
#ifdef XP_MAC
{
OSErr err;
Ptr symAddr;
CFragSymbolClass symClass;
Str255 pName;
strcpy((char *)&pName, name);
c2pstr((char *)&pName);
err = FindSymbol(lm->dlh, pName, &symAddr, &symClass);
f = (err == noErr ? (FARPROC)symAddr : NULL);
}
#endif /* XP_MAC */
#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_current_thread->errstr = pr_DLLError();
return f;
}
/*
** Called by class loader to resolve missing native's
*/
PR_PUBLIC_API(FARPROC)
PR_FindSymbol(const char *raw_name, PRLibrary *lib)
{
FARPROC f = NULL;
const char *name;
if (needInit) InitLinker();
/*
** Mangle the raw symbol name in any way that is platform specific.
*/
#if defined(SUNOS4)
/* 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(SUNOS4)
free((void*)name);
#endif
PR_ExitMonitor(pr_linker_lock);
return f;
}
PR_PUBLIC_API(FARPROC)
PR_FindSymbolAndLibrary(const char *raw_name, PRLibrary* *lib)
{
FARPROC f = NULL;
const char *name;
PRLibrary* lm;
if (needInit) InitLinker();
/*
** Mangle the raw symbol name in any way that is platform specific.
*/
#if defined(SUNOS4)
/* 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(Linker, debug,
("%s incr => %d (for %s)",
lm->name, lm->refCount, name));
break;
}
}
#if defined(SUNOS4)
free((void*)name);
#endif
PR_ExitMonitor(pr_linker_lock);
return f;
}
/*
** 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_PUBLIC_API(PRLibrary*)
PR_LoadStaticLibrary(const char *name, const PRStaticLinkTable *slt)
{
PRLibrary *lm;
PRLibrary* result = NULL;
/* Initialize the linker if necessary */
if (needInit) InitLinker();
/* 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 = (PRLibrary*) calloc(1, sizeof(PRLibrary));
if (lm == NULL) goto unlock;
lm->name = 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);
unlock:
PR_LOG(Linker, debug, ("Loaded library %s (static lib)", lm->name));
PR_ExitMonitor(pr_linker_lock);
return result;
}