Coverage Report

Created: 2026-05-19 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}