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