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