secload.c (6278B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "secport.h" 6 #include "nspr.h" 7 8 #ifdef XP_UNIX 9 #include <unistd.h> 10 #define BL_MAXSYMLINKS 20 11 12 /* 13 * If 'link' is a symbolic link, this function follows the symbolic links 14 * and returns the pathname of the ultimate source of the symbolic links. 15 * If 'link' is not a symbolic link, this function returns NULL. 16 * The caller should call PR_Free to free the string returned by this 17 * function. 18 */ 19 static char* 20 loader_GetOriginalPathname(const char* link) 21 { 22 char* resolved = NULL; 23 char* input = NULL; 24 PRUint32 iterations = 0; 25 PRInt32 len = 0, retlen = 0; 26 if (!link) { 27 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 28 return NULL; 29 } 30 len = PR_MAX(1024, strlen(link) + 1); 31 resolved = PR_Malloc(len); 32 input = PR_Malloc(len); 33 if (!resolved || !input) { 34 if (resolved) { 35 PR_Free(resolved); 36 } 37 if (input) { 38 PR_Free(input); 39 } 40 return NULL; 41 } 42 strcpy(input, link); 43 while ((iterations++ < BL_MAXSYMLINKS) && 44 ((retlen = readlink(input, resolved, len - 1)) > 0)) { 45 char* tmp = input; 46 resolved[retlen] = '\0'; /* NULL termination */ 47 input = resolved; 48 resolved = tmp; 49 } 50 PR_Free(resolved); 51 if (iterations == 1 && retlen < 0) { 52 PR_Free(input); 53 input = NULL; 54 } 55 return input; 56 } 57 #endif /* XP_UNIX */ 58 59 /* 60 * Load the library with the file name 'name' residing in the same 61 * directory as the reference library, whose pathname is 'referencePath'. 62 */ 63 static PRLibrary* 64 loader_LoadLibInReferenceDir(const char* referencePath, const char* name) 65 { 66 PRLibrary* dlh = NULL; 67 char* fullName = NULL; 68 char* c; 69 PRLibSpec libSpec; 70 71 /* Remove the trailing filename from referencePath and add the new one */ 72 c = strrchr(referencePath, PR_GetDirectorySeparator()); 73 if (c) { 74 size_t referencePathSize = 1 + c - referencePath; 75 fullName = (char*)PORT_Alloc(strlen(name) + referencePathSize + 1); 76 if (fullName) { 77 memcpy(fullName, referencePath, referencePathSize); 78 strcpy(fullName + referencePathSize, name); 79 #ifdef DEBUG_LOADER 80 PR_fprintf(PR_STDOUT, "\nAttempting to load fully-qualified %s\n", 81 fullName); 82 #endif 83 libSpec.type = PR_LibSpec_Pathname; 84 libSpec.value.pathname = fullName; 85 dlh = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL 86 #ifdef PR_LD_ALT_SEARCH_PATH 87 /* allow library's dependencies to be found in the same directory 88 * on Windows even if PATH is not set. Requires NSPR 4.8.1 . */ 89 | PR_LD_ALT_SEARCH_PATH 90 #endif 91 ); 92 PORT_Free(fullName); 93 } 94 } 95 return dlh; 96 } 97 98 /* 99 * Load a shared library called "newShLibName" in the same directory as 100 * a shared library that is already loaded, called existingShLibName. 101 * A pointer to a static function in that shared library, 102 * staticShLibFunc, is required. 103 * 104 * existingShLibName: 105 * The file name of the shared library that shall be used as the 106 * "reference library". The loader will attempt to load the requested 107 * library from the same directory as the reference library. 108 * 109 * staticShLibFunc: 110 * Pointer to a static function in the "reference library". 111 * 112 * newShLibName: 113 * The simple file name of the new shared library to be loaded. 114 * 115 * We use PR_GetLibraryFilePathname to get the pathname of the loaded 116 * shared lib that contains this function, and then do a 117 * PR_LoadLibraryWithFlags with an absolute pathname for the shared 118 * library to be loaded. 119 * 120 * On Windows, the "alternate search path" strategy is employed, if available. 121 * On Unix, if existingShLibName is a symbolic link, and no link exists for the 122 * new library, the original link will be resolved, and the new library loaded 123 * from the resolved location. 124 * 125 * If the new shared library is not found in the same location as the reference 126 * library, it will then be loaded from the normal system library path. 127 * 128 */ 129 130 PRLibrary* 131 PORT_LoadLibraryFromOrigin(const char* existingShLibName, 132 PRFuncPtr staticShLibFunc, 133 const char* newShLibName) 134 { 135 PRLibrary* lib = NULL; 136 char* fullPath = NULL; 137 PRLibSpec libSpec; 138 139 /* Get the pathname for existingShLibName, e.g. /usr/lib/libnss3.so 140 * PR_GetLibraryFilePathname works with either the base library name or a 141 * function pointer, depending on the platform. 142 * We require the address of a function in the "reference library", 143 * provided by the caller. To avoid getting the address of the stub/thunk 144 * of an exported function by accident, use the address of a static 145 * function rather than an exported function. 146 */ 147 fullPath = PR_GetLibraryFilePathname(existingShLibName, 148 staticShLibFunc); 149 150 if (fullPath) { 151 lib = loader_LoadLibInReferenceDir(fullPath, newShLibName); 152 #ifdef XP_UNIX 153 if (!lib) { 154 /* 155 * If fullPath is a symbolic link, resolve the symbolic 156 * link and try again. 157 */ 158 char* originalfullPath = loader_GetOriginalPathname(fullPath); 159 if (originalfullPath) { 160 PR_Free(fullPath); 161 fullPath = originalfullPath; 162 lib = loader_LoadLibInReferenceDir(fullPath, newShLibName); 163 } 164 } 165 #endif 166 PR_Free(fullPath); 167 } 168 if (!lib) { 169 #ifdef DEBUG_LOADER 170 PR_fprintf(PR_STDOUT, "\nAttempting to load %s\n", newShLibName); 171 #endif 172 libSpec.type = PR_LibSpec_Pathname; 173 libSpec.value.pathname = newShLibName; 174 lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL); 175 } 176 if (NULL == lib) { 177 #ifdef DEBUG_LOADER 178 PR_fprintf(PR_STDOUT, "\nLoading failed : %s.\n", newShLibName); 179 #endif 180 } 181 return lib; 182 }