tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prlink.c (24534B)


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