/src/nspr/pr/src/linking/prlink.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "primpl.h" |
7 | | |
8 | | #include <string.h> |
9 | | |
10 | | #ifdef XP_UNIX |
11 | | # ifdef USE_DLFCN |
12 | | # include <dlfcn.h> |
13 | | /* Define these on systems that don't have them. */ |
14 | | # ifndef RTLD_NOW |
15 | | # define RTLD_NOW 0 |
16 | | # endif |
17 | | # ifndef RTLD_LAZY |
18 | | # define RTLD_LAZY RTLD_NOW |
19 | | # endif |
20 | | # ifndef RTLD_GLOBAL |
21 | | # define RTLD_GLOBAL 0 |
22 | | # endif |
23 | | # ifndef RTLD_LOCAL |
24 | | # define RTLD_LOCAL 0 |
25 | | # endif |
26 | | # ifdef AIX |
27 | | # include <sys/ldr.h> |
28 | | # ifndef L_IGNOREUNLOAD /* AIX 4.3.3 does not have L_IGNOREUNLOAD. */ |
29 | | # define L_IGNOREUNLOAD 0x10000000 |
30 | | # endif |
31 | | # endif |
32 | | # elif defined(USE_HPSHL) |
33 | | # include <dl.h> |
34 | | # endif |
35 | | #endif /* XP_UNIX */ |
36 | | |
37 | 0 | #define _PR_DEFAULT_LD_FLAGS PR_LD_LAZY |
38 | | |
39 | | /* |
40 | | * On these platforms, symbols have a leading '_'. |
41 | | */ |
42 | | #if ((defined(OPENBSD) || defined(NETBSD)) && !defined(__ELF__)) |
43 | | # define NEED_LEADING_UNDERSCORE |
44 | | #endif |
45 | | |
46 | | #define PR_LD_PATHW 0x8000 /* for PR_LibSpec_PathnameU */ |
47 | | |
48 | | /************************************************************************/ |
49 | | |
50 | | struct PRLibrary { |
51 | | char* name; /* Our own copy of the name string */ |
52 | | PRLibrary* next; |
53 | | int refCount; |
54 | | const PRStaticLinkTable* staticTable; |
55 | | |
56 | | #ifdef XP_PC |
57 | | HINSTANCE dlh; |
58 | | #endif |
59 | | |
60 | | #ifdef XP_UNIX |
61 | | # if defined(USE_HPSHL) |
62 | | shl_t dlh; |
63 | | # else |
64 | | void* dlh; |
65 | | # endif |
66 | | #endif |
67 | | }; |
68 | | |
69 | | static PRLibrary* pr_loadmap; |
70 | | static PRLibrary* pr_exe_loadmap; |
71 | | static PRMonitor* pr_linker_lock; |
72 | | static char* _pr_currentLibPath = NULL; |
73 | | |
74 | | static PRLibrary* pr_LoadLibraryByPathname(const char* name, PRIntn flags); |
75 | | |
76 | | /************************************************************************/ |
77 | | |
78 | | #if !defined(USE_DLFCN) && !defined(HAVE_STRERROR) |
79 | | # define ERR_STR_BUF_LENGTH 20 |
80 | | #endif |
81 | | |
82 | | static void DLLErrorInternal(PRIntn oserr) |
83 | | /* |
84 | | ** This whole function, and most of the code in this file, are run |
85 | | ** with a big hairy lock wrapped around it. Not the best of situations, |
86 | | ** but will eventually come up with the right answer. |
87 | | */ |
88 | 0 | { |
89 | 0 | const char* error = NULL; |
90 | 0 | #ifdef USE_DLFCN |
91 | 0 | error = dlerror(); /* $$$ That'll be wrong some of the time - AOF */ |
92 | | #elif defined(HAVE_STRERROR) |
93 | | error = strerror(oserr); /* this should be okay */ |
94 | | #else |
95 | | char errStrBuf[ERR_STR_BUF_LENGTH]; |
96 | | PR_snprintf(errStrBuf, sizeof(errStrBuf), "error %d", oserr); |
97 | | error = errStrBuf; |
98 | | #endif |
99 | 0 | if (NULL != error) { |
100 | 0 | PR_SetErrorText(strlen(error), error); |
101 | 0 | } |
102 | 0 | } /* DLLErrorInternal */ |
103 | | |
104 | 4 | void _PR_InitLinker(void) { |
105 | 4 | PRLibrary* lm = NULL; |
106 | 4 | #if defined(XP_UNIX) |
107 | 4 | void* h; |
108 | 4 | #endif |
109 | | |
110 | 4 | if (!pr_linker_lock) { |
111 | 4 | pr_linker_lock = PR_NewNamedMonitor("linker-lock"); |
112 | 4 | } |
113 | 4 | PR_EnterMonitor(pr_linker_lock); |
114 | | |
115 | | #if defined(XP_PC) |
116 | | lm = PR_NEWZAP(PRLibrary); |
117 | | lm->name = strdup("Executable"); |
118 | | /* A module handle for the executable. */ |
119 | | lm->dlh = GetModuleHandle(NULL); |
120 | | |
121 | | lm->refCount = 1; |
122 | | lm->staticTable = NULL; |
123 | | pr_exe_loadmap = lm; |
124 | | pr_loadmap = lm; |
125 | | |
126 | | #elif defined(XP_UNIX) |
127 | | # ifdef HAVE_DLL |
128 | 4 | # if defined(USE_DLFCN) && !defined(NO_DLOPEN_NULL) |
129 | 4 | h = dlopen(0, RTLD_LAZY); |
130 | 4 | if (!h) { |
131 | 0 | char* error; |
132 | |
|
133 | 0 | DLLErrorInternal(_MD_ERRNO()); |
134 | 0 | error = (char*)PR_MALLOC(PR_GetErrorTextLength()); |
135 | 0 | (void)PR_GetErrorText(error); |
136 | 0 | fprintf(stderr, "failed to initialize shared libraries [%s]\n", error); |
137 | 0 | PR_DELETE(error); |
138 | 0 | abort(); /* XXX */ |
139 | 0 | } |
140 | | # elif defined(USE_HPSHL) |
141 | | h = NULL; |
142 | | /* don't abort with this NULL */ |
143 | | # elif defined(NO_DLOPEN_NULL) |
144 | | h = NULL; /* XXXX toshok */ /* XXXX vlad */ |
145 | | # else |
146 | | # error no dll strategy |
147 | | # endif /* USE_DLFCN */ |
148 | | |
149 | 4 | lm = PR_NEWZAP(PRLibrary); |
150 | 4 | if (lm) { |
151 | 4 | lm->name = strdup("a.out"); |
152 | 4 | lm->refCount = 1; |
153 | 4 | lm->dlh = h; |
154 | 4 | lm->staticTable = NULL; |
155 | 4 | } |
156 | 4 | pr_exe_loadmap = lm; |
157 | 4 | pr_loadmap = lm; |
158 | 4 | # endif /* HAVE_DLL */ |
159 | 4 | #endif /* XP_UNIX */ |
160 | | |
161 | 4 | if (lm) { |
162 | 4 | PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (init)", lm->name)); |
163 | 4 | } |
164 | | |
165 | 4 | PR_ExitMonitor(pr_linker_lock); |
166 | 4 | } |
167 | | |
168 | | /* |
169 | | * _PR_ShutdownLinker does not unload the dlls loaded by the application |
170 | | * via calls to PR_LoadLibrary. Any dlls that still remain on the |
171 | | * pr_loadmap list when NSPR shuts down are application programming errors. |
172 | | * The only exception is pr_exe_loadmap, which was added to the list by |
173 | | * NSPR and hence should be cleaned up by NSPR. |
174 | | */ |
175 | 0 | void _PR_ShutdownLinker(void) { |
176 | | /* FIXME: pr_exe_loadmap should be destroyed. */ |
177 | |
|
178 | 0 | PR_DestroyMonitor(pr_linker_lock); |
179 | 0 | pr_linker_lock = NULL; |
180 | |
|
181 | 0 | if (_pr_currentLibPath) { |
182 | 0 | free(_pr_currentLibPath); |
183 | 0 | _pr_currentLibPath = NULL; |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | /******************************************************************************/ |
188 | | |
189 | 0 | PR_IMPLEMENT(PRStatus) PR_SetLibraryPath(const char* path) { |
190 | 0 | PRStatus rv = PR_SUCCESS; |
191 | |
|
192 | 0 | if (!_pr_initialized) { |
193 | 0 | _PR_ImplicitInitialization(); |
194 | 0 | } |
195 | 0 | PR_EnterMonitor(pr_linker_lock); |
196 | 0 | if (_pr_currentLibPath) { |
197 | 0 | free(_pr_currentLibPath); |
198 | 0 | } |
199 | 0 | if (path) { |
200 | 0 | _pr_currentLibPath = strdup(path); |
201 | 0 | if (!_pr_currentLibPath) { |
202 | 0 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
203 | 0 | rv = PR_FAILURE; |
204 | 0 | } |
205 | 0 | } else { |
206 | 0 | _pr_currentLibPath = 0; |
207 | 0 | } |
208 | 0 | PR_ExitMonitor(pr_linker_lock); |
209 | 0 | return rv; |
210 | 0 | } |
211 | | |
212 | | /* |
213 | | ** Return the library path for finding shared libraries. |
214 | | */ |
215 | | PR_IMPLEMENT(char*) |
216 | 0 | PR_GetLibraryPath(void) { |
217 | 0 | char* ev; |
218 | 0 | char* copy = NULL; /* a copy of _pr_currentLibPath */ |
219 | |
|
220 | 0 | if (!_pr_initialized) { |
221 | 0 | _PR_ImplicitInitialization(); |
222 | 0 | } |
223 | 0 | PR_EnterMonitor(pr_linker_lock); |
224 | 0 | if (_pr_currentLibPath != NULL) { |
225 | 0 | goto exit; |
226 | 0 | } |
227 | | |
228 | | /* initialize pr_currentLibPath */ |
229 | | |
230 | | #ifdef XP_PC |
231 | | ev = getenv("LD_LIBRARY_PATH"); |
232 | | if (!ev) { |
233 | | ev = ".;\\lib"; |
234 | | } |
235 | | ev = strdup(ev); |
236 | | #endif |
237 | | |
238 | 0 | #if defined(XP_UNIX) |
239 | 0 | # if defined(USE_DLFCN) |
240 | 0 | { |
241 | 0 | char* p = NULL; |
242 | 0 | int len; |
243 | |
|
244 | 0 | ev = getenv("LD_LIBRARY_PATH"); |
245 | 0 | if (!ev) { |
246 | 0 | ev = "/usr/lib:/lib"; |
247 | 0 | } |
248 | 0 | len = strlen(ev) + 1; /* +1 for the null */ |
249 | |
|
250 | 0 | p = (char*)malloc(len); |
251 | 0 | if (p) { |
252 | 0 | strcpy(p, ev); |
253 | 0 | } /* if (p) */ |
254 | 0 | ev = p; |
255 | 0 | PR_LOG(_pr_io_lm, PR_LOG_NOTICE, ("linker path '%s'", ev)); |
256 | 0 | } |
257 | | # else |
258 | | /* AFAIK there isn't a library path with the HP SHL interface --Rob */ |
259 | | ev = strdup(""); |
260 | | # endif |
261 | 0 | #endif |
262 | | |
263 | | /* |
264 | | * If ev is NULL, we have run out of memory |
265 | | */ |
266 | 0 | _pr_currentLibPath = ev; |
267 | |
|
268 | 0 | exit: |
269 | 0 | if (_pr_currentLibPath) { |
270 | 0 | copy = strdup(_pr_currentLibPath); |
271 | 0 | } |
272 | 0 | PR_ExitMonitor(pr_linker_lock); |
273 | 0 | if (!copy) { |
274 | 0 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
275 | 0 | } |
276 | 0 | return copy; |
277 | 0 | } |
278 | | |
279 | | /* |
280 | | ** Build library name from path, lib and extensions |
281 | | */ |
282 | | PR_IMPLEMENT(char*) |
283 | 0 | PR_GetLibraryName(const char* path, const char* lib) { |
284 | 0 | char* fullname; |
285 | |
|
286 | | #ifdef XP_PC |
287 | | if (strstr(lib, PR_DLL_SUFFIX) == NULL) { |
288 | | if (path) { |
289 | | fullname = PR_smprintf("%s\\%s%s", path, lib, PR_DLL_SUFFIX); |
290 | | } else { |
291 | | fullname = PR_smprintf("%s%s", lib, PR_DLL_SUFFIX); |
292 | | } |
293 | | } else { |
294 | | if (path) { |
295 | | fullname = PR_smprintf("%s\\%s", path, lib); |
296 | | } else { |
297 | | fullname = PR_smprintf("%s", lib); |
298 | | } |
299 | | } |
300 | | #endif /* XP_PC */ |
301 | 0 | #if defined(XP_UNIX) |
302 | 0 | if (strstr(lib, PR_DLL_SUFFIX) == NULL) { |
303 | 0 | if (path) { |
304 | 0 | fullname = PR_smprintf("%s/lib%s%s", path, lib, PR_DLL_SUFFIX); |
305 | 0 | } else { |
306 | 0 | fullname = PR_smprintf("lib%s%s", lib, PR_DLL_SUFFIX); |
307 | 0 | } |
308 | 0 | } else { |
309 | 0 | if (path) { |
310 | 0 | fullname = PR_smprintf("%s/%s", path, lib); |
311 | 0 | } else { |
312 | 0 | fullname = PR_smprintf("%s", lib); |
313 | 0 | } |
314 | 0 | } |
315 | 0 | #endif /* XP_UNIX */ |
316 | 0 | return fullname; |
317 | 0 | } |
318 | | |
319 | | /* |
320 | | ** Free the memory allocated, for the caller, by PR_GetLibraryName |
321 | | */ |
322 | | PR_IMPLEMENT(void) |
323 | 0 | PR_FreeLibraryName(char* mem) { PR_smprintf_free(mem); } |
324 | | |
325 | 0 | static PRLibrary* pr_UnlockedFindLibrary(const char* name) { |
326 | 0 | PRLibrary* lm = pr_loadmap; |
327 | 0 | const char* np = strrchr(name, PR_DIRECTORY_SEPARATOR); |
328 | 0 | np = np ? np + 1 : name; |
329 | 0 | while (lm) { |
330 | 0 | const char* cp = strrchr(lm->name, PR_DIRECTORY_SEPARATOR); |
331 | 0 | cp = cp ? cp + 1 : lm->name; |
332 | | #ifdef WIN32 |
333 | | /* Windows DLL names are case insensitive... */ |
334 | | if (strcmpi(np, cp) == 0) |
335 | | #else |
336 | 0 | if (strcmp(np, cp) == 0) |
337 | 0 | #endif |
338 | 0 | { |
339 | | /* found */ |
340 | 0 | lm->refCount++; |
341 | 0 | PR_LOG(_pr_linker_lm, PR_LOG_MIN, |
342 | 0 | ("%s incr => %d (find lib)", lm->name, lm->refCount)); |
343 | 0 | return lm; |
344 | 0 | } |
345 | 0 | lm = lm->next; |
346 | 0 | } |
347 | 0 | return NULL; |
348 | 0 | } |
349 | | |
350 | | PR_IMPLEMENT(PRLibrary*) |
351 | 0 | PR_LoadLibraryWithFlags(PRLibSpec libSpec, PRIntn flags) { |
352 | 0 | if (flags == 0) { |
353 | 0 | flags = _PR_DEFAULT_LD_FLAGS; |
354 | 0 | } |
355 | 0 | switch (libSpec.type) { |
356 | 0 | case PR_LibSpec_Pathname: |
357 | 0 | return pr_LoadLibraryByPathname(libSpec.value.pathname, flags); |
358 | | #ifdef WIN32 |
359 | | case PR_LibSpec_PathnameU: |
360 | | /* |
361 | | * cast to |char *| and set PR_LD_PATHW flag so that |
362 | | * it can be cast back to PRUnichar* in the callee. |
363 | | */ |
364 | | return pr_LoadLibraryByPathname((const char*)libSpec.value.pathname_u, |
365 | | flags | PR_LD_PATHW); |
366 | | #endif |
367 | 0 | default: |
368 | 0 | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
369 | 0 | return NULL; |
370 | 0 | } |
371 | 0 | } |
372 | | |
373 | | PR_IMPLEMENT(PRLibrary*) |
374 | 0 | PR_LoadLibrary(const char* name) { |
375 | 0 | PRLibSpec libSpec; |
376 | |
|
377 | 0 | libSpec.type = PR_LibSpec_Pathname; |
378 | 0 | libSpec.value.pathname = name; |
379 | 0 | return PR_LoadLibraryWithFlags(libSpec, 0); |
380 | 0 | } |
381 | | |
382 | | /* |
383 | | ** Dynamically load a library. Only load libraries once, so scan the load |
384 | | ** map first. |
385 | | */ |
386 | 0 | static PRLibrary* pr_LoadLibraryByPathname(const char* name, PRIntn flags) { |
387 | 0 | PRLibrary* lm; |
388 | 0 | PRLibrary* result = NULL; |
389 | 0 | PRInt32 oserr; |
390 | | #ifdef WIN32 |
391 | | char utf8name_stack[MAX_PATH]; |
392 | | char* utf8name_malloc = NULL; |
393 | | char* utf8name = utf8name_stack; |
394 | | PRUnichar wname_stack[MAX_PATH]; |
395 | | PRUnichar* wname_malloc = NULL; |
396 | | PRUnichar* wname = wname_stack; |
397 | | int len; |
398 | | #endif |
399 | |
|
400 | 0 | if (!_pr_initialized) { |
401 | 0 | _PR_ImplicitInitialization(); |
402 | 0 | } |
403 | | |
404 | | /* See if library is already loaded */ |
405 | 0 | PR_EnterMonitor(pr_linker_lock); |
406 | |
|
407 | | #ifdef WIN32 |
408 | | if (flags & PR_LD_PATHW) { |
409 | | /* cast back what's cast to |char *| for the argument passing. */ |
410 | | wname = (LPWSTR)name; |
411 | | } else { |
412 | | int wlen = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0); |
413 | | if (wlen > MAX_PATH) { |
414 | | wname = wname_malloc = PR_Malloc(wlen * sizeof(PRUnichar)); |
415 | | } |
416 | | if (wname == NULL || |
417 | | !MultiByteToWideChar(CP_ACP, 0, name, -1, wname, wlen)) { |
418 | | oserr = _MD_ERRNO(); |
419 | | goto unlock; |
420 | | } |
421 | | } |
422 | | len = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL); |
423 | | if (len > MAX_PATH) { |
424 | | utf8name = utf8name_malloc = PR_Malloc(len); |
425 | | } |
426 | | if (utf8name == NULL || |
427 | | !WideCharToMultiByte(CP_UTF8, 0, wname, -1, utf8name, len, NULL, NULL)) { |
428 | | oserr = _MD_ERRNO(); |
429 | | goto unlock; |
430 | | } |
431 | | /* the list of loaded library names are always kept in UTF-8 |
432 | | * on Win32 platforms */ |
433 | | result = pr_UnlockedFindLibrary(utf8name); |
434 | | #else |
435 | 0 | result = pr_UnlockedFindLibrary(name); |
436 | 0 | #endif |
437 | |
|
438 | 0 | if (result != NULL) { |
439 | 0 | goto unlock; |
440 | 0 | } |
441 | | |
442 | 0 | lm = PR_NEWZAP(PRLibrary); |
443 | 0 | if (lm == NULL) { |
444 | 0 | oserr = _MD_ERRNO(); |
445 | 0 | goto unlock; |
446 | 0 | } |
447 | 0 | lm->staticTable = NULL; |
448 | |
|
449 | | #ifdef WIN32 |
450 | | { |
451 | | HINSTANCE h; |
452 | | |
453 | | h = LoadLibraryExW( |
454 | | wname, NULL, |
455 | | (flags & PR_LD_ALT_SEARCH_PATH) ? LOAD_WITH_ALTERED_SEARCH_PATH : 0); |
456 | | if (h == NULL) { |
457 | | oserr = _MD_ERRNO(); |
458 | | PR_DELETE(lm); |
459 | | goto unlock; |
460 | | } |
461 | | lm->name = strdup(utf8name); |
462 | | lm->dlh = h; |
463 | | lm->next = pr_loadmap; |
464 | | pr_loadmap = lm; |
465 | | } |
466 | | #endif /* WIN32 */ |
467 | |
|
468 | 0 | #if defined(XP_UNIX) |
469 | 0 | # ifdef HAVE_DLL |
470 | 0 | { |
471 | 0 | # if defined(USE_DLFCN) |
472 | | # ifdef NTO |
473 | | /* Neutrino needs RTLD_GROUP to load Netscape plugins. (bug 71179) */ |
474 | | int dl_flags = RTLD_GROUP; |
475 | | # elif defined(AIX) |
476 | | /* AIX needs RTLD_MEMBER to load an archive member. (bug 228899) */ |
477 | | int dl_flags = RTLD_MEMBER; |
478 | | # else |
479 | 0 | int dl_flags = 0; |
480 | 0 | # endif |
481 | 0 | void* h = NULL; |
482 | | # if defined(DARWIN) |
483 | | PRBool okToLoad = PR_FALSE; |
484 | | # endif |
485 | |
|
486 | 0 | if (flags & PR_LD_LAZY) { |
487 | 0 | dl_flags |= RTLD_LAZY; |
488 | 0 | } |
489 | 0 | if (flags & PR_LD_NOW) { |
490 | 0 | dl_flags |= RTLD_NOW; |
491 | 0 | } |
492 | 0 | if (flags & PR_LD_GLOBAL) { |
493 | 0 | dl_flags |= RTLD_GLOBAL; |
494 | 0 | } |
495 | 0 | if (flags & PR_LD_LOCAL) { |
496 | 0 | dl_flags |= RTLD_LOCAL; |
497 | 0 | } |
498 | | # if defined(DARWIN) |
499 | | /* If the file contains an absolute or relative path (slash) |
500 | | * and the path doesn't look like a System path, then require |
501 | | * the file exists. |
502 | | * The reason is that DARWIN's dlopen ignores the provided path |
503 | | * and checks for the plain filename in DYLD_LIBRARY_PATH, |
504 | | * which could load an unexpected version of a library. */ |
505 | | if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) { |
506 | | /* no slash, allow to load from any location */ |
507 | | okToLoad = PR_TRUE; |
508 | | } else { |
509 | | const char systemPrefix1[] = "/System/"; |
510 | | const size_t systemPrefixLen1 = strlen(systemPrefix1); |
511 | | const char systemPrefix2[] = "/usr/lib/"; |
512 | | const size_t systemPrefixLen2 = strlen(systemPrefix2); |
513 | | const size_t name_len = strlen(name); |
514 | | if (((name_len > systemPrefixLen1) && |
515 | | (strncmp(name, systemPrefix1, systemPrefixLen1) == 0)) || |
516 | | ((name_len > systemPrefixLen2) && |
517 | | (strncmp(name, systemPrefix2, systemPrefixLen2) == 0))) { |
518 | | /* found at beginning, it's a system library. |
519 | | * Skip filesystem check (required for macOS 11), |
520 | | * allow loading from any location */ |
521 | | okToLoad = PR_TRUE; |
522 | | } else if (PR_Access(name, PR_ACCESS_EXISTS) == PR_SUCCESS) { |
523 | | /* file exists, allow to load */ |
524 | | okToLoad = PR_TRUE; |
525 | | } |
526 | | } |
527 | | if (okToLoad) { |
528 | | h = dlopen(name, dl_flags); |
529 | | } |
530 | | # else |
531 | 0 | h = dlopen(name, dl_flags); |
532 | 0 | # endif |
533 | | # elif defined(USE_HPSHL) |
534 | | int shl_flags = 0; |
535 | | shl_t h; |
536 | | |
537 | | /* |
538 | | * Use the DYNAMIC_PATH flag only if 'name' is a plain file |
539 | | * name (containing no directory) to match the behavior of |
540 | | * dlopen(). |
541 | | */ |
542 | | if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) { |
543 | | shl_flags |= DYNAMIC_PATH; |
544 | | } |
545 | | if (flags & PR_LD_LAZY) { |
546 | | shl_flags |= BIND_DEFERRED; |
547 | | } |
548 | | if (flags & PR_LD_NOW) { |
549 | | shl_flags |= BIND_IMMEDIATE; |
550 | | } |
551 | | /* No equivalent of PR_LD_GLOBAL and PR_LD_LOCAL. */ |
552 | | h = shl_load(name, shl_flags, 0L); |
553 | | # else |
554 | | # error Configuration error |
555 | | # endif |
556 | 0 | if (!h) { |
557 | 0 | oserr = _MD_ERRNO(); |
558 | 0 | PR_DELETE(lm); |
559 | 0 | goto unlock; |
560 | 0 | } |
561 | 0 | lm->name = strdup(name); |
562 | 0 | lm->dlh = h; |
563 | 0 | lm->next = pr_loadmap; |
564 | 0 | pr_loadmap = lm; |
565 | 0 | } |
566 | 0 | # endif /* HAVE_DLL */ |
567 | 0 | #endif /* XP_UNIX */ |
568 | | |
569 | 0 | lm->refCount = 1; |
570 | |
|
571 | 0 | result = lm; /* success */ |
572 | 0 | PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (load lib)", lm->name)); |
573 | |
|
574 | 0 | unlock: |
575 | 0 | if (result == NULL) { |
576 | 0 | PR_SetError(PR_LOAD_LIBRARY_ERROR, oserr); |
577 | 0 | DLLErrorInternal(oserr); /* sets error text */ |
578 | 0 | } |
579 | | #ifdef WIN32 |
580 | | if (utf8name_malloc) { |
581 | | PR_Free(utf8name_malloc); |
582 | | } |
583 | | if (wname_malloc) { |
584 | | PR_Free(wname_malloc); |
585 | | } |
586 | | #endif |
587 | 0 | PR_ExitMonitor(pr_linker_lock); |
588 | 0 | return result; |
589 | 0 | } |
590 | | |
591 | | /* |
592 | | ** Unload a shared library which was loaded via PR_LoadLibrary |
593 | | */ |
594 | | PR_IMPLEMENT(PRStatus) |
595 | 0 | PR_UnloadLibrary(PRLibrary* lib) { |
596 | 0 | int result = 0; |
597 | 0 | PRStatus status = PR_SUCCESS; |
598 | |
|
599 | 0 | if (lib == 0) { |
600 | 0 | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
601 | 0 | return PR_FAILURE; |
602 | 0 | } |
603 | | |
604 | 0 | PR_EnterMonitor(pr_linker_lock); |
605 | |
|
606 | 0 | if (lib->refCount <= 0) { |
607 | 0 | PR_ExitMonitor(pr_linker_lock); |
608 | 0 | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
609 | 0 | return PR_FAILURE; |
610 | 0 | } |
611 | | |
612 | 0 | if (--lib->refCount > 0) { |
613 | 0 | PR_LOG(_pr_linker_lm, PR_LOG_MIN, |
614 | 0 | ("%s decr => %d", lib->name, lib->refCount)); |
615 | 0 | goto done; |
616 | 0 | } |
617 | | |
618 | 0 | #ifdef XP_UNIX |
619 | 0 | # ifdef HAVE_DLL |
620 | 0 | # ifdef USE_DLFCN |
621 | 0 | result = dlclose(lib->dlh); |
622 | | # elif defined(USE_HPSHL) |
623 | | result = shl_unload(lib->dlh); |
624 | | # else |
625 | | # error Configuration error |
626 | | # endif |
627 | 0 | # endif /* HAVE_DLL */ |
628 | 0 | #endif /* XP_UNIX */ |
629 | | #ifdef XP_PC |
630 | | if (lib->dlh) { |
631 | | FreeLibrary((HINSTANCE)(lib->dlh)); |
632 | | lib->dlh = (HINSTANCE)NULL; |
633 | | } |
634 | | #endif /* XP_PC */ |
635 | | |
636 | | /* unlink from library search list */ |
637 | 0 | if (pr_loadmap == lib) { |
638 | 0 | pr_loadmap = pr_loadmap->next; |
639 | 0 | } else if (pr_loadmap != NULL) { |
640 | 0 | PRLibrary* prev = pr_loadmap; |
641 | 0 | PRLibrary* next = pr_loadmap->next; |
642 | 0 | while (next != NULL) { |
643 | 0 | if (next == lib) { |
644 | 0 | prev->next = next->next; |
645 | 0 | goto freeLib; |
646 | 0 | } |
647 | 0 | prev = next; |
648 | 0 | next = next->next; |
649 | 0 | } |
650 | | /* |
651 | | * fail (the library is not on the _pr_loadmap list), |
652 | | * but don't wipe out an error from dlclose/shl_unload. |
653 | | */ |
654 | 0 | PR_NOT_REACHED("_pr_loadmap and lib->refCount inconsistent"); |
655 | 0 | if (result == 0) { |
656 | 0 | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
657 | 0 | status = PR_FAILURE; |
658 | 0 | } |
659 | 0 | } |
660 | | /* |
661 | | * We free the PRLibrary structure whether dlclose/shl_unload |
662 | | * succeeds or not. |
663 | | */ |
664 | | |
665 | 0 | freeLib: |
666 | 0 | PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Unloaded library %s", lib->name)); |
667 | 0 | free(lib->name); |
668 | 0 | lib->name = NULL; |
669 | 0 | PR_DELETE(lib); |
670 | 0 | if (result != 0) { |
671 | 0 | PR_SetError(PR_UNLOAD_LIBRARY_ERROR, _MD_ERRNO()); |
672 | 0 | DLLErrorInternal(_MD_ERRNO()); |
673 | 0 | status = PR_FAILURE; |
674 | 0 | } |
675 | |
|
676 | 0 | done: |
677 | 0 | PR_ExitMonitor(pr_linker_lock); |
678 | 0 | return status; |
679 | 0 | } |
680 | | |
681 | 0 | static void* pr_FindSymbolInLib(PRLibrary* lm, const char* name) { |
682 | 0 | void* f = NULL; |
683 | |
|
684 | 0 | if (lm->staticTable != NULL) { |
685 | 0 | const PRStaticLinkTable* tp; |
686 | 0 | for (tp = lm->staticTable; tp->name; tp++) { |
687 | 0 | if (strcmp(name, tp->name) == 0) { |
688 | 0 | return (void*)tp->fp; |
689 | 0 | } |
690 | 0 | } |
691 | | /* |
692 | | ** If the symbol was not found in the static table then check if |
693 | | ** the symbol was exported in the DLL... |
694 | | */ |
695 | 0 | PR_SetError(PR_FIND_SYMBOL_ERROR, 0); |
696 | 0 | return (void*)NULL; |
697 | 0 | } |
698 | | |
699 | | #ifdef WIN32 |
700 | | f = GetProcAddress(lm->dlh, name); |
701 | | #endif /* WIN32 */ |
702 | | |
703 | 0 | #ifdef XP_UNIX |
704 | 0 | # ifdef HAVE_DLL |
705 | 0 | # ifdef USE_DLFCN |
706 | 0 | f = dlsym(lm->dlh, name); |
707 | | # elif defined(USE_HPSHL) |
708 | | if (shl_findsym(&lm->dlh, name, TYPE_PROCEDURE, &f) == -1) { |
709 | | f = NULL; |
710 | | } |
711 | | # endif |
712 | 0 | # endif /* HAVE_DLL */ |
713 | 0 | #endif /* XP_UNIX */ |
714 | 0 | if (f == NULL) { |
715 | 0 | PR_SetError(PR_FIND_SYMBOL_ERROR, _MD_ERRNO()); |
716 | 0 | DLLErrorInternal(_MD_ERRNO()); |
717 | 0 | } |
718 | 0 | return f; |
719 | 0 | } |
720 | | |
721 | | /* |
722 | | ** Called by class loader to resolve missing native's |
723 | | */ |
724 | | PR_IMPLEMENT(void*) |
725 | 0 | PR_FindSymbol(PRLibrary* lib, const char* raw_name) { |
726 | 0 | void* f = NULL; |
727 | | #if defined(NEED_LEADING_UNDERSCORE) |
728 | | char* name; |
729 | | #else |
730 | 0 | const char* name; |
731 | 0 | #endif |
732 | | /* |
733 | | ** Mangle the raw symbol name in any way that is platform specific. |
734 | | */ |
735 | | #if defined(NEED_LEADING_UNDERSCORE) |
736 | | /* Need a leading _ */ |
737 | | name = PR_smprintf("_%s", raw_name); |
738 | | #elif defined(AIX) |
739 | | /* |
740 | | ** AIX with the normal linker put's a "." in front of the symbol |
741 | | ** name. When use "svcc" and "svld" then the "." disappears. Go |
742 | | ** figure. |
743 | | */ |
744 | | name = raw_name; |
745 | | #else |
746 | 0 | name = raw_name; |
747 | 0 | #endif |
748 | |
|
749 | 0 | PR_EnterMonitor(pr_linker_lock); |
750 | 0 | PR_ASSERT(lib != NULL); |
751 | 0 | f = pr_FindSymbolInLib(lib, name); |
752 | |
|
753 | | #if defined(NEED_LEADING_UNDERSCORE) |
754 | | PR_smprintf_free(name); |
755 | | #endif |
756 | |
|
757 | 0 | PR_ExitMonitor(pr_linker_lock); |
758 | 0 | return f; |
759 | 0 | } |
760 | | |
761 | | /* |
762 | | ** Return the address of the function 'raw_name' in the library 'lib' |
763 | | */ |
764 | | PR_IMPLEMENT(PRFuncPtr) |
765 | 0 | PR_FindFunctionSymbol(PRLibrary* lib, const char* raw_name) { |
766 | 0 | return ((PRFuncPtr)PR_FindSymbol(lib, raw_name)); |
767 | 0 | } |
768 | | |
769 | | PR_IMPLEMENT(void*) |
770 | 0 | PR_FindSymbolAndLibrary(const char* raw_name, PRLibrary** lib) { |
771 | 0 | void* f = NULL; |
772 | | #if defined(NEED_LEADING_UNDERSCORE) |
773 | | char* name; |
774 | | #else |
775 | 0 | const char* name; |
776 | 0 | #endif |
777 | 0 | PRLibrary* lm; |
778 | |
|
779 | 0 | if (!_pr_initialized) { |
780 | 0 | _PR_ImplicitInitialization(); |
781 | 0 | } |
782 | | /* |
783 | | ** Mangle the raw symbol name in any way that is platform specific. |
784 | | */ |
785 | | #if defined(NEED_LEADING_UNDERSCORE) |
786 | | /* Need a leading _ */ |
787 | | name = PR_smprintf("_%s", raw_name); |
788 | | #elif defined(AIX) |
789 | | /* |
790 | | ** AIX with the normal linker put's a "." in front of the symbol |
791 | | ** name. When use "svcc" and "svld" then the "." disappears. Go |
792 | | ** figure. |
793 | | */ |
794 | | name = raw_name; |
795 | | #else |
796 | 0 | name = raw_name; |
797 | 0 | #endif |
798 | |
|
799 | 0 | PR_EnterMonitor(pr_linker_lock); |
800 | | |
801 | | /* search all libraries */ |
802 | 0 | for (lm = pr_loadmap; lm != NULL; lm = lm->next) { |
803 | 0 | f = pr_FindSymbolInLib(lm, name); |
804 | 0 | if (f != NULL) { |
805 | 0 | *lib = lm; |
806 | 0 | lm->refCount++; |
807 | 0 | PR_LOG(_pr_linker_lm, PR_LOG_MIN, |
808 | 0 | ("%s incr => %d (for %s)", lm->name, lm->refCount, name)); |
809 | 0 | break; |
810 | 0 | } |
811 | 0 | } |
812 | | #if defined(NEED_LEADING_UNDERSCORE) |
813 | | PR_smprintf_free(name); |
814 | | #endif |
815 | |
|
816 | 0 | PR_ExitMonitor(pr_linker_lock); |
817 | 0 | return f; |
818 | 0 | } |
819 | | |
820 | | PR_IMPLEMENT(PRFuncPtr) |
821 | 0 | PR_FindFunctionSymbolAndLibrary(const char* raw_name, PRLibrary** lib) { |
822 | 0 | return ((PRFuncPtr)PR_FindSymbolAndLibrary(raw_name, lib)); |
823 | 0 | } |
824 | | |
825 | | /* |
826 | | ** Add a static library to the list of loaded libraries. If LoadLibrary |
827 | | ** is called with the name then we will pretend it was already loaded |
828 | | */ |
829 | | PR_IMPLEMENT(PRLibrary*) |
830 | 0 | PR_LoadStaticLibrary(const char* name, const PRStaticLinkTable* slt) { |
831 | 0 | PRLibrary* lm = NULL; |
832 | 0 | PRLibrary* result = NULL; |
833 | |
|
834 | 0 | if (!_pr_initialized) { |
835 | 0 | _PR_ImplicitInitialization(); |
836 | 0 | } |
837 | | |
838 | | /* See if library is already loaded */ |
839 | 0 | PR_EnterMonitor(pr_linker_lock); |
840 | | |
841 | | /* If the lbrary is already loaded, then add the static table information... |
842 | | */ |
843 | 0 | result = pr_UnlockedFindLibrary(name); |
844 | 0 | if (result != NULL) { |
845 | 0 | PR_ASSERT((result->staticTable == NULL) || (result->staticTable == slt)); |
846 | 0 | result->staticTable = slt; |
847 | 0 | goto unlock; |
848 | 0 | } |
849 | | |
850 | | /* Add library to list...Mark it static */ |
851 | 0 | lm = PR_NEWZAP(PRLibrary); |
852 | 0 | if (lm == NULL) { |
853 | 0 | goto unlock; |
854 | 0 | } |
855 | | |
856 | 0 | lm->name = strdup(name); |
857 | 0 | lm->refCount = 1; |
858 | 0 | lm->dlh = pr_exe_loadmap ? pr_exe_loadmap->dlh : 0; |
859 | 0 | lm->staticTable = slt; |
860 | 0 | lm->next = pr_loadmap; |
861 | 0 | pr_loadmap = lm; |
862 | |
|
863 | 0 | result = lm; /* success */ |
864 | 0 | PR_ASSERT(lm->refCount == 1); |
865 | 0 | PR_LOG(_pr_linker_lm, PR_LOG_MIN, |
866 | 0 | ("Loaded library %s (static lib)", lm->name)); |
867 | 0 | unlock: |
868 | 0 | PR_ExitMonitor(pr_linker_lock); |
869 | 0 | return result; |
870 | 0 | } |
871 | | |
872 | | PR_IMPLEMENT(char*) |
873 | 0 | PR_GetLibraryFilePathname(const char* name, PRFuncPtr addr) { |
874 | 0 | #if defined(USE_DLFCN) && defined(HAVE_DLADDR) |
875 | 0 | Dl_info dli; |
876 | 0 | char* result; |
877 | |
|
878 | 0 | if (dladdr((void*)addr, &dli) == 0) { |
879 | 0 | PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO()); |
880 | 0 | DLLErrorInternal(_MD_ERRNO()); |
881 | 0 | return NULL; |
882 | 0 | } |
883 | 0 | result = PR_Malloc(strlen(dli.dli_fname) + 1); |
884 | 0 | if (result != NULL) { |
885 | 0 | strcpy(result, dli.dli_fname); |
886 | 0 | } |
887 | 0 | return result; |
888 | | #elif defined(AIX) |
889 | | char* result; |
890 | | # define LD_INFO_INCREMENT 64 |
891 | | struct ld_info* info; |
892 | | unsigned int info_length = LD_INFO_INCREMENT * sizeof(struct ld_info); |
893 | | struct ld_info* infop; |
894 | | int loadflags = L_GETINFO | L_IGNOREUNLOAD; |
895 | | |
896 | | for (;;) { |
897 | | info = PR_Malloc(info_length); |
898 | | if (info == NULL) { |
899 | | return NULL; |
900 | | } |
901 | | /* If buffer is too small, loadquery fails with ENOMEM. */ |
902 | | if (loadquery(loadflags, info, info_length) != -1) { |
903 | | break; |
904 | | } |
905 | | /* |
906 | | * Calling loadquery when compiled for 64-bit with the |
907 | | * L_IGNOREUNLOAD flag can cause an invalid argument error |
908 | | * on AIX 5.1. Detect this error the first time that |
909 | | * loadquery is called, and try calling it again without |
910 | | * this flag set. |
911 | | */ |
912 | | if (errno == EINVAL && (loadflags & L_IGNOREUNLOAD)) { |
913 | | loadflags &= ~L_IGNOREUNLOAD; |
914 | | if (loadquery(loadflags, info, info_length) != -1) { |
915 | | break; |
916 | | } |
917 | | } |
918 | | PR_Free(info); |
919 | | if (errno != ENOMEM) { |
920 | | /* should not happen */ |
921 | | _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); |
922 | | return NULL; |
923 | | } |
924 | | /* retry with a larger buffer */ |
925 | | info_length += LD_INFO_INCREMENT * sizeof(struct ld_info); |
926 | | } |
927 | | |
928 | | for (infop = info;; |
929 | | infop = (struct ld_info*)((char*)infop + infop->ldinfo_next)) { |
930 | | unsigned long start = (unsigned long)infop->ldinfo_dataorg; |
931 | | unsigned long end = start + infop->ldinfo_datasize; |
932 | | if (start <= (unsigned long)addr && end > (unsigned long)addr) { |
933 | | result = PR_Malloc(strlen(infop->ldinfo_filename) + 1); |
934 | | if (result != NULL) { |
935 | | strcpy(result, infop->ldinfo_filename); |
936 | | } |
937 | | break; |
938 | | } |
939 | | if (!infop->ldinfo_next) { |
940 | | PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, 0); |
941 | | result = NULL; |
942 | | break; |
943 | | } |
944 | | } |
945 | | PR_Free(info); |
946 | | return result; |
947 | | #elif defined(HPUX) && defined(USE_HPSHL) |
948 | | int index; |
949 | | struct shl_descriptor desc; |
950 | | char* result; |
951 | | |
952 | | for (index = 0; shl_get_r(index, &desc) == 0; index++) { |
953 | | if (strstr(desc.filename, name) != NULL) { |
954 | | result = PR_Malloc(strlen(desc.filename) + 1); |
955 | | if (result != NULL) { |
956 | | strcpy(result, desc.filename); |
957 | | } |
958 | | return result; |
959 | | } |
960 | | } |
961 | | /* |
962 | | * Since the index value of a library is decremented if |
963 | | * a library preceding it in the shared library search |
964 | | * list was unloaded, it is possible that we missed some |
965 | | * libraries as we went up the list. So we should go |
966 | | * down the list to be sure that we not miss anything. |
967 | | */ |
968 | | for (index--; index >= 0; index--) { |
969 | | if ((shl_get_r(index, &desc) == 0) && |
970 | | (strstr(desc.filename, name) != NULL)) { |
971 | | result = PR_Malloc(strlen(desc.filename) + 1); |
972 | | if (result != NULL) { |
973 | | strcpy(result, desc.filename); |
974 | | } |
975 | | return result; |
976 | | } |
977 | | } |
978 | | PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, 0); |
979 | | return NULL; |
980 | | #elif defined(HPUX) && defined(USE_DLFCN) |
981 | | struct load_module_desc desc; |
982 | | char* result; |
983 | | const char* module_name; |
984 | | |
985 | | if (dlmodinfo((unsigned long)addr, &desc, sizeof desc, NULL, 0, 0) == 0) { |
986 | | PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO()); |
987 | | DLLErrorInternal(_MD_ERRNO()); |
988 | | return NULL; |
989 | | } |
990 | | module_name = dlgetname(&desc, sizeof desc, NULL, 0, 0); |
991 | | if (module_name == NULL) { |
992 | | /* should not happen */ |
993 | | _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); |
994 | | DLLErrorInternal(_MD_ERRNO()); |
995 | | return NULL; |
996 | | } |
997 | | result = PR_Malloc(strlen(module_name) + 1); |
998 | | if (result != NULL) { |
999 | | strcpy(result, module_name); |
1000 | | } |
1001 | | return result; |
1002 | | #elif defined(WIN32) |
1003 | | PRUnichar wname[MAX_PATH]; |
1004 | | HMODULE handle = NULL; |
1005 | | PRUnichar module_name[MAX_PATH]; |
1006 | | int len; |
1007 | | char* result; |
1008 | | |
1009 | | if (MultiByteToWideChar(CP_ACP, 0, name, -1, wname, MAX_PATH)) { |
1010 | | handle = GetModuleHandleW(wname); |
1011 | | } |
1012 | | if (handle == NULL) { |
1013 | | PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO()); |
1014 | | DLLErrorInternal(_MD_ERRNO()); |
1015 | | return NULL; |
1016 | | } |
1017 | | if (GetModuleFileNameW(handle, module_name, MAX_PATH) == 0) { |
1018 | | /* should not happen */ |
1019 | | _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); |
1020 | | return NULL; |
1021 | | } |
1022 | | len = WideCharToMultiByte(CP_ACP, 0, module_name, -1, NULL, 0, NULL, NULL); |
1023 | | if (len == 0) { |
1024 | | _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); |
1025 | | return NULL; |
1026 | | } |
1027 | | result = PR_Malloc(len * sizeof(PRUnichar)); |
1028 | | if (result != NULL) { |
1029 | | WideCharToMultiByte(CP_ACP, 0, module_name, -1, result, len, NULL, NULL); |
1030 | | } |
1031 | | return result; |
1032 | | #else |
1033 | | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
1034 | | return NULL; |
1035 | | #endif |
1036 | 0 | } |