michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "secport.h" michael@0: #include "nspr.h" michael@0: michael@0: #ifdef XP_UNIX michael@0: #include michael@0: #define BL_MAXSYMLINKS 20 michael@0: michael@0: /* michael@0: * If 'link' is a symbolic link, this function follows the symbolic links michael@0: * and returns the pathname of the ultimate source of the symbolic links. michael@0: * If 'link' is not a symbolic link, this function returns NULL. michael@0: * The caller should call PR_Free to free the string returned by this michael@0: * function. michael@0: */ michael@0: static char* loader_GetOriginalPathname(const char* link) michael@0: { michael@0: char* resolved = NULL; michael@0: char* input = NULL; michael@0: PRUint32 iterations = 0; michael@0: PRInt32 len = 0, retlen = 0; michael@0: if (!link) { michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); michael@0: return NULL; michael@0: } michael@0: len = PR_MAX(1024, strlen(link) + 1); michael@0: resolved = PR_Malloc(len); michael@0: input = PR_Malloc(len); michael@0: if (!resolved || !input) { michael@0: if (resolved) { michael@0: PR_Free(resolved); michael@0: } michael@0: if (input) { michael@0: PR_Free(input); michael@0: } michael@0: return NULL; michael@0: } michael@0: strcpy(input, link); michael@0: while ( (iterations++ < BL_MAXSYMLINKS) && michael@0: ( (retlen = readlink(input, resolved, len - 1)) > 0) ) { michael@0: char* tmp = input; michael@0: resolved[retlen] = '\0'; /* NULL termination */ michael@0: input = resolved; michael@0: resolved = tmp; michael@0: } michael@0: PR_Free(resolved); michael@0: if (iterations == 1 && retlen < 0) { michael@0: PR_Free(input); michael@0: input = NULL; michael@0: } michael@0: return input; michael@0: } michael@0: #endif /* XP_UNIX */ michael@0: michael@0: /* michael@0: * Load the library with the file name 'name' residing in the same michael@0: * directory as the reference library, whose pathname is 'referencePath'. michael@0: */ michael@0: static PRLibrary * michael@0: loader_LoadLibInReferenceDir(const char *referencePath, const char *name) michael@0: { michael@0: PRLibrary *dlh = NULL; michael@0: char *fullName = NULL; michael@0: char* c; michael@0: PRLibSpec libSpec; michael@0: michael@0: /* Remove the trailing filename from referencePath and add the new one */ michael@0: c = strrchr(referencePath, PR_GetDirectorySeparator()); michael@0: if (c) { michael@0: size_t referencePathSize = 1 + c - referencePath; michael@0: fullName = (char*) PORT_Alloc(strlen(name) + referencePathSize + 1); michael@0: if (fullName) { michael@0: memcpy(fullName, referencePath, referencePathSize); michael@0: strcpy(fullName + referencePathSize, name); michael@0: #ifdef DEBUG_LOADER michael@0: PR_fprintf(PR_STDOUT, "\nAttempting to load fully-qualified %s\n", michael@0: fullName); michael@0: #endif michael@0: libSpec.type = PR_LibSpec_Pathname; michael@0: libSpec.value.pathname = fullName; michael@0: dlh = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL michael@0: #ifdef PR_LD_ALT_SEARCH_PATH michael@0: /* allow library's dependencies to be found in the same directory michael@0: * on Windows even if PATH is not set. Requires NSPR 4.8.1 . */ michael@0: | PR_LD_ALT_SEARCH_PATH michael@0: #endif michael@0: ); michael@0: PORT_Free(fullName); michael@0: } michael@0: } michael@0: return dlh; michael@0: } michael@0: michael@0: /* michael@0: * Load a shared library called "newShLibName" in the same directory as michael@0: * a shared library that is already loaded, called existingShLibName. michael@0: * A pointer to a static function in that shared library, michael@0: * staticShLibFunc, is required. michael@0: * michael@0: * existingShLibName: michael@0: * The file name of the shared library that shall be used as the michael@0: * "reference library". The loader will attempt to load the requested michael@0: * library from the same directory as the reference library. michael@0: * michael@0: * staticShLibFunc: michael@0: * Pointer to a static function in the "reference library". michael@0: * michael@0: * newShLibName: michael@0: * The simple file name of the new shared library to be loaded. michael@0: * michael@0: * We use PR_GetLibraryFilePathname to get the pathname of the loaded michael@0: * shared lib that contains this function, and then do a michael@0: * PR_LoadLibraryWithFlags with an absolute pathname for the shared michael@0: * library to be loaded. michael@0: * michael@0: * On Windows, the "alternate search path" strategy is employed, if available. michael@0: * On Unix, if existingShLibName is a symbolic link, and no link exists for the michael@0: * new library, the original link will be resolved, and the new library loaded michael@0: * from the resolved location. michael@0: * michael@0: * If the new shared library is not found in the same location as the reference michael@0: * library, it will then be loaded from the normal system library path. michael@0: * michael@0: */ michael@0: michael@0: PRLibrary * michael@0: PORT_LoadLibraryFromOrigin(const char* existingShLibName, michael@0: PRFuncPtr staticShLibFunc, michael@0: const char *newShLibName) michael@0: { michael@0: PRLibrary *lib = NULL; michael@0: char* fullPath = NULL; michael@0: PRLibSpec libSpec; michael@0: michael@0: /* Get the pathname for existingShLibName, e.g. /usr/lib/libnss3.so michael@0: * PR_GetLibraryFilePathname works with either the base library name or a michael@0: * function pointer, depending on the platform. michael@0: * We require the address of a function in the "reference library", michael@0: * provided by the caller. To avoid getting the address of the stub/thunk michael@0: * of an exported function by accident, use the address of a static michael@0: * function rather than an exported function. michael@0: */ michael@0: fullPath = PR_GetLibraryFilePathname(existingShLibName, michael@0: staticShLibFunc); michael@0: michael@0: if (fullPath) { michael@0: lib = loader_LoadLibInReferenceDir(fullPath, newShLibName); michael@0: #ifdef XP_UNIX michael@0: if (!lib) { michael@0: /* michael@0: * If fullPath is a symbolic link, resolve the symbolic michael@0: * link and try again. michael@0: */ michael@0: char* originalfullPath = loader_GetOriginalPathname(fullPath); michael@0: if (originalfullPath) { michael@0: PR_Free(fullPath); michael@0: fullPath = originalfullPath; michael@0: lib = loader_LoadLibInReferenceDir(fullPath, newShLibName); michael@0: } michael@0: } michael@0: #endif michael@0: PR_Free(fullPath); michael@0: } michael@0: if (!lib) { michael@0: #ifdef DEBUG_LOADER michael@0: PR_fprintf(PR_STDOUT, "\nAttempting to load %s\n", newShLibName); michael@0: #endif michael@0: libSpec.type = PR_LibSpec_Pathname; michael@0: libSpec.value.pathname = newShLibName; michael@0: lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL); michael@0: } michael@0: if (NULL == lib) { michael@0: #ifdef DEBUG_LOADER michael@0: PR_fprintf(PR_STDOUT, "\nLoading failed : %s.\n", newShLibName); michael@0: #endif michael@0: } michael@0: return lib; michael@0: } michael@0: