root/maint/gnulib/lib/relocatable.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_this_relocation_prefix
  2. set_relocation_prefix
  3. compute_curr_prefix
  4. DllMain
  5. _DLL_InitTerm
  6. find_shared_library_fullname
  7. get_shared_library_fullname
  8. relocate
  9. relocate2

   1 /* Provide relocatable packages.
   2    Copyright (C) 2003-2006, 2008-2021 Free Software Foundation, Inc.
   3    Written by Bruno Haible <bruno@clisp.org>, 2003.
   4 
   5    This file is free software: you can redistribute it and/or modify
   6    it under the terms of the GNU Lesser General Public License as
   7    published by the Free Software Foundation; either version 2.1 of the
   8    License, or (at your option) any later version.
   9 
  10    This file is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13    GNU Lesser General Public License for more details.
  14 
  15    You should have received a copy of the GNU Lesser General Public License
  16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  17 
  18 
  19 /* Tell glibc's <stdio.h> to provide a prototype for getline().
  20    This must come before <config.h> because <config.h> may include
  21    <features.h>, and once <features.h> has been included, it's too late.  */
  22 #ifndef _GNU_SOURCE
  23 # define _GNU_SOURCE 1
  24 #endif
  25 
  26 #define _GL_USE_STDLIB_ALLOC 1
  27 #include <config.h>
  28 
  29 /* Specification.  */
  30 #include "relocatable.h"
  31 
  32 #if ENABLE_RELOCATABLE
  33 
  34 #include <stddef.h>
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <string.h>
  38 
  39 #ifdef NO_XMALLOC
  40 # define xmalloc malloc
  41 #else
  42 # include "xalloc.h"
  43 #endif
  44 
  45 #if defined _WIN32 && !defined __CYGWIN__
  46 # define WIN32_LEAN_AND_MEAN
  47 # include <windows.h>
  48 #endif
  49 
  50 #ifdef __EMX__
  51 # define INCL_DOS
  52 # include <os2.h>
  53 
  54 # define strcmp  stricmp
  55 # define strncmp strnicmp
  56 #endif
  57 
  58 #if DEPENDS_ON_LIBCHARSET
  59 # include <libcharset.h>
  60 #endif
  61 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
  62 # include <iconv.h>
  63 #endif
  64 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
  65 # include <libintl.h>
  66 #endif
  67 
  68 #if defined _WIN32 && !defined __CYGWIN__
  69 /* Don't assume that UNICODE is not defined.  */
  70 # undef GetModuleFileName
  71 # define GetModuleFileName GetModuleFileNameA
  72 #endif
  73 
  74 /* Faked cheap 'bool'.  */
  75 #undef bool
  76 #undef false
  77 #undef true
  78 #define bool int
  79 #define false 0
  80 #define true 1
  81 
  82 /* Pathname support.
  83    ISSLASH(C)                tests whether C is a directory separator character.
  84    IS_FILE_NAME_WITH_DIR(P)  tests whether P contains a directory specification.
  85  */
  86 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
  87   /* Native Windows, OS/2, DOS */
  88 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
  89 # define HAS_DEVICE(P) \
  90     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
  91      && (P)[1] == ':')
  92 # define IS_FILE_NAME_WITH_DIR(P) \
  93     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
  94 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
  95 #else
  96   /* Unix */
  97 # define ISSLASH(C) ((C) == '/')
  98 # define IS_FILE_NAME_WITH_DIR(P) (strchr (P, '/') != NULL)
  99 # define FILE_SYSTEM_PREFIX_LEN(P) 0
 100 #endif
 101 
 102 /* Whether to enable the more costly support for relocatable libraries.
 103    It allows libraries to be have been installed with a different original
 104    prefix than the program.  But it is quite costly, especially on Cygwin
 105    platforms, see below.  Therefore we enable it by default only on native
 106    Windows platforms.  */
 107 #ifndef ENABLE_COSTLY_RELOCATABLE
 108 # if defined _WIN32 && !defined __CYGWIN__
 109 #  define ENABLE_COSTLY_RELOCATABLE 1
 110 # else
 111 #  define ENABLE_COSTLY_RELOCATABLE 0
 112 # endif
 113 #endif
 114 
 115 /* Original installation prefix.  */
 116 static char *orig_prefix;
 117 static size_t orig_prefix_len;
 118 /* Current installation prefix.  */
 119 static char *curr_prefix;
 120 static size_t curr_prefix_len;
 121 /* These prefixes do not end in a slash.  Anything that will be concatenated
 122    to them must start with a slash.  */
 123 
 124 /* Sets the original and the current installation prefix of this module.
 125    Relocation simply replaces a pathname starting with the original prefix
 126    by the corresponding pathname with the current prefix instead.  Both
 127    prefixes should be directory names without trailing slash (i.e. use ""
 128    instead of "/").  */
 129 static void
 130 set_this_relocation_prefix (const char *orig_prefix_arg,
     /* [previous][next][first][last][top][bottom][index][help] */
 131                             const char *curr_prefix_arg)
 132 {
 133   if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
 134       /* Optimization: if orig_prefix and curr_prefix are equal, the
 135          relocation is a nop.  */
 136       && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
 137     {
 138       /* Duplicate the argument strings.  */
 139       char *memory;
 140 
 141       orig_prefix_len = strlen (orig_prefix_arg);
 142       curr_prefix_len = strlen (curr_prefix_arg);
 143       memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
 144 #ifdef NO_XMALLOC
 145       if (memory != NULL)
 146 #endif
 147         {
 148           memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
 149           orig_prefix = memory;
 150           memory += orig_prefix_len + 1;
 151           memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
 152           curr_prefix = memory;
 153           return;
 154         }
 155     }
 156   orig_prefix = NULL;
 157   curr_prefix = NULL;
 158   /* Don't worry about wasted memory here - this function is usually only
 159      called once.  */
 160 }
 161 
 162 /* Sets the original and the current installation prefix of the package.
 163    Relocation simply replaces a pathname starting with the original prefix
 164    by the corresponding pathname with the current prefix instead.  Both
 165    prefixes should be directory names without trailing slash (i.e. use ""
 166    instead of "/").  */
 167 void
 168 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 169 {
 170   set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
 171 
 172   /* Now notify all dependent libraries.  */
 173 #if DEPENDS_ON_LIBCHARSET
 174   libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
 175 #endif
 176 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
 177   libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
 178 #endif
 179 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
 180   libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
 181 #endif
 182 }
 183 
 184 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE)
 185 
 186 /* Convenience function:
 187    Computes the current installation prefix, based on the original
 188    installation prefix, the original installation directory of a particular
 189    file, and the current pathname of this file.
 190    Returns it, freshly allocated.  Returns NULL upon failure.  */
 191 #ifdef IN_LIBRARY
 192 #define compute_curr_prefix local_compute_curr_prefix
 193 static
 194 #endif
 195 char *
 196 compute_curr_prefix (const char *orig_installprefix,
     /* [previous][next][first][last][top][bottom][index][help] */
 197                      const char *orig_installdir,
 198                      const char *curr_pathname)
 199 {
 200   char *curr_installdir;
 201   const char *rel_installdir;
 202 
 203   if (curr_pathname == NULL)
 204     return NULL;
 205 
 206   /* Determine the relative installation directory, relative to the prefix.
 207      This is simply the difference between orig_installprefix and
 208      orig_installdir.  */
 209   if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
 210       != 0)
 211     /* Shouldn't happen - nothing should be installed outside $(prefix).  */
 212     return NULL;
 213   rel_installdir = orig_installdir + strlen (orig_installprefix);
 214 
 215   /* Determine the current installation directory.  */
 216   {
 217     const char *p_base = curr_pathname + FILE_SYSTEM_PREFIX_LEN (curr_pathname);
 218     const char *p = curr_pathname + strlen (curr_pathname);
 219     char *q;
 220 
 221     while (p > p_base)
 222       {
 223         p--;
 224         if (ISSLASH (*p))
 225           break;
 226       }
 227 
 228     q = (char *) xmalloc (p - curr_pathname + 1);
 229 #ifdef NO_XMALLOC
 230     if (q == NULL)
 231       return NULL;
 232 #endif
 233     memcpy (q, curr_pathname, p - curr_pathname);
 234     q[p - curr_pathname] = '\0';
 235     curr_installdir = q;
 236   }
 237 
 238   /* Compute the current installation prefix by removing the trailing
 239      rel_installdir from it.  */
 240   {
 241     const char *rp = rel_installdir + strlen (rel_installdir);
 242     const char *cp = curr_installdir + strlen (curr_installdir);
 243     const char *cp_base =
 244       curr_installdir + FILE_SYSTEM_PREFIX_LEN (curr_installdir);
 245 
 246     while (rp > rel_installdir && cp > cp_base)
 247       {
 248         bool same = false;
 249         const char *rpi = rp;
 250         const char *cpi = cp;
 251 
 252         while (rpi > rel_installdir && cpi > cp_base)
 253           {
 254             rpi--;
 255             cpi--;
 256             if (ISSLASH (*rpi) || ISSLASH (*cpi))
 257               {
 258                 if (ISSLASH (*rpi) && ISSLASH (*cpi))
 259                   same = true;
 260                 break;
 261               }
 262             /* Do case-insensitive comparison if the file system is always or
 263                often case-insensitive.  It's better to accept the comparison
 264                if the difference is only in case, rather than to fail.  */
 265 #if defined _WIN32 || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
 266             /* Native Windows, Cygwin, OS/2, DOS - case insignificant file system */
 267             if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
 268                 != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
 269               break;
 270 #else
 271             if (*rpi != *cpi)
 272               break;
 273 #endif
 274           }
 275         if (!same)
 276           break;
 277         /* The last pathname component was the same.  rpi and cpi now point
 278            to the slash before it.  */
 279         rp = rpi;
 280         cp = cpi;
 281       }
 282 
 283     if (rp > rel_installdir)
 284       {
 285         /* Unexpected: The curr_installdir does not end with rel_installdir.  */
 286         free (curr_installdir);
 287         return NULL;
 288       }
 289 
 290     {
 291       size_t computed_curr_prefix_len = cp - curr_installdir;
 292       char *computed_curr_prefix;
 293 
 294       computed_curr_prefix = (char *) xmalloc (computed_curr_prefix_len + 1);
 295 #ifdef NO_XMALLOC
 296       if (computed_curr_prefix == NULL)
 297         {
 298           free (curr_installdir);
 299           return NULL;
 300         }
 301 #endif
 302       memcpy (computed_curr_prefix, curr_installdir, computed_curr_prefix_len);
 303       computed_curr_prefix[computed_curr_prefix_len] = '\0';
 304 
 305       free (curr_installdir);
 306 
 307       return computed_curr_prefix;
 308     }
 309   }
 310 }
 311 
 312 #endif /* !IN_LIBRARY || PIC */
 313 
 314 #if defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE
 315 
 316 /* Full pathname of shared library, or NULL.  */
 317 static char *shared_library_fullname;
 318 
 319 #if defined _WIN32 && !defined __CYGWIN__
 320 /* Native Windows only.
 321    On Cygwin, it is better to use the Cygwin provided /proc interface, than
 322    to use native Windows API and cygwin_conv_to_posix_path, because it
 323    supports longer file names
 324    (see <https://cygwin.com/ml/cygwin/2011-01/msg00410.html>).  */
 325 
 326 /* Determine the full pathname of the shared library when it is loaded.
 327 
 328    Documentation:
 329    <https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain>  */
 330 
 331 BOOL WINAPI
 332 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
     /* [previous][next][first][last][top][bottom][index][help] */
 333 {
 334   (void) reserved;
 335 
 336   if (event == DLL_PROCESS_ATTACH)
 337     {
 338       /* The DLL is being loaded into an application's address range.  */
 339       static char location[MAX_PATH];
 340 
 341       if (!GetModuleFileName (module_handle, location, sizeof (location)))
 342         /* Shouldn't happen.  */
 343         return FALSE;
 344 
 345       if (!IS_FILE_NAME_WITH_DIR (location))
 346         /* Shouldn't happen.  */
 347         return FALSE;
 348 
 349       /* Avoid a memory leak when the same DLL get attached, detached,
 350          attached, detached, and so on.  This happens e.g. when a spell
 351          checker DLL is used repeatedly by a mail program.  */
 352       if (!(shared_library_fullname != NULL
 353             && strcmp (shared_library_fullname, location) == 0))
 354         /* Remember the full pathname of the shared library.  */
 355         shared_library_fullname = strdup (location);
 356     }
 357 
 358   return TRUE;
 359 }
 360 
 361 #elif defined __EMX__
 362 
 363 extern int  _CRT_init (void);
 364 extern void _CRT_term (void);
 365 extern void __ctordtorInit (void);
 366 extern void __ctordtorTerm (void);
 367 
 368 unsigned long _System
 369 _DLL_InitTerm (unsigned long hModule, unsigned long ulFlag)
     /* [previous][next][first][last][top][bottom][index][help] */
 370 {
 371   static char location[CCHMAXPATH];
 372 
 373   switch (ulFlag)
 374     {
 375       case 0:
 376         if (_CRT_init () == -1)
 377           return 0;
 378 
 379         __ctordtorInit();
 380 
 381         /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
 382            for specification of DosQueryModuleName(). */
 383         if (DosQueryModuleName (hModule, sizeof (location), location))
 384           return 0;
 385 
 386         _fnslashify (location);
 387         shared_library_fullname = strdup (location);
 388         break;
 389 
 390       case 1:
 391         __ctordtorTerm();
 392 
 393         _CRT_term ();
 394         break;
 395     }
 396 
 397   return 1;
 398 }
 399 
 400 #else /* Unix */
 401 
 402 static void
 403 find_shared_library_fullname ()
     /* [previous][next][first][last][top][bottom][index][help] */
 404 {
 405 #if (defined __linux__ && (__GLIBC__ >= 2 || defined __UCLIBC__)) || defined __CYGWIN__
 406   /* Linux has /proc/self/maps. glibc 2 and uClibc have the getline()
 407      function.
 408      Cygwin >= 1.5 has /proc/self/maps and the getline() function too.
 409      But it is costly: ca. 0.3 ms on Linux, 3 ms on Cygwin 1.5, and 5 ms on
 410      Cygwin 1.7.  */
 411   FILE *fp;
 412 
 413   /* Open the current process' maps file.  It describes one VMA per line.  */
 414   fp = fopen ("/proc/self/maps", "r");
 415   if (fp)
 416     {
 417       unsigned long address = (unsigned long) &find_shared_library_fullname;
 418       for (;;)
 419         {
 420           unsigned long start, end;
 421           int c;
 422 
 423           if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
 424             break;
 425           if (address >= start && address <= end - 1)
 426             {
 427               /* Found it.  Now see if this line contains a filename.  */
 428               while (c = getc (fp), c != EOF && c != '\n' && c != '/')
 429                 continue;
 430               if (c == '/')
 431                 {
 432                   size_t size;
 433                   int len;
 434 
 435                   ungetc (c, fp);
 436                   shared_library_fullname = NULL; size = 0;
 437                   len = getline (&shared_library_fullname, &size, fp);
 438                   if (len >= 0)
 439                     {
 440                       /* Success: filled shared_library_fullname.  */
 441                       if (len > 0 && shared_library_fullname[len - 1] == '\n')
 442                         shared_library_fullname[len - 1] = '\0';
 443                     }
 444                 }
 445               break;
 446             }
 447           while (c = getc (fp), c != EOF && c != '\n')
 448             continue;
 449         }
 450       fclose (fp);
 451     }
 452 #endif
 453 }
 454 
 455 #endif /* Native Windows / EMX / Unix */
 456 
 457 /* Return the full pathname of the current shared library.
 458    Return NULL if unknown.
 459    Guaranteed to work only on Linux, EMX, Cygwin, and native Windows.  */
 460 static char *
 461 get_shared_library_fullname ()
     /* [previous][next][first][last][top][bottom][index][help] */
 462 {
 463 #if !(defined _WIN32 && !defined __CYGWIN__) && !defined __EMX__
 464   static bool tried_find_shared_library_fullname;
 465   if (!tried_find_shared_library_fullname)
 466     {
 467       find_shared_library_fullname ();
 468       tried_find_shared_library_fullname = true;
 469     }
 470 #endif
 471   return shared_library_fullname;
 472 }
 473 
 474 #endif /* PIC */
 475 
 476 /* Returns the pathname, relocated according to the current installation
 477    directory.
 478    The returned string is either PATHNAME unmodified or a freshly allocated
 479    string that you can free with free() after casting it to 'char *'.  */
 480 const char *
 481 relocate (const char *pathname)
     /* [previous][next][first][last][top][bottom][index][help] */
 482 {
 483 #if defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE
 484   static int initialized;
 485 
 486   /* Initialization code for a shared library.  */
 487   if (!initialized)
 488     {
 489       /* At this point, orig_prefix and curr_prefix likely have already been
 490          set through the main program's set_program_name_and_installdir
 491          function.  This is sufficient in the case that the library has
 492          initially been installed in the same orig_prefix.  But we can do
 493          better, to also cover the cases that 1. it has been installed
 494          in a different prefix before being moved to orig_prefix and (later)
 495          to curr_prefix, 2. unlike the program, it has not moved away from
 496          orig_prefix.  */
 497       const char *orig_installprefix = INSTALLPREFIX;
 498       const char *orig_installdir = INSTALLDIR;
 499       char *curr_prefix_better;
 500 
 501       curr_prefix_better =
 502         compute_curr_prefix (orig_installprefix, orig_installdir,
 503                              get_shared_library_fullname ());
 504 
 505       set_relocation_prefix (orig_installprefix,
 506                              curr_prefix_better != NULL
 507                              ? curr_prefix_better
 508                              : curr_prefix);
 509 
 510       if (curr_prefix_better != NULL)
 511         free (curr_prefix_better);
 512 
 513       initialized = 1;
 514     }
 515 #endif
 516 
 517   /* Note: It is not necessary to perform case insensitive comparison here,
 518      even for DOS-like file systems, because the pathname argument was
 519      typically created from the same Makefile variable as orig_prefix came
 520      from.  */
 521   if (orig_prefix != NULL && curr_prefix != NULL
 522       && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
 523     {
 524       if (pathname[orig_prefix_len] == '\0')
 525         {
 526           /* pathname equals orig_prefix.  */
 527           char *result = (char *) xmalloc (strlen (curr_prefix) + 1);
 528 
 529 #ifdef NO_XMALLOC
 530           if (result != NULL)
 531 #endif
 532             {
 533               strcpy (result, curr_prefix);
 534               return result;
 535             }
 536         }
 537       else if (ISSLASH (pathname[orig_prefix_len]))
 538         {
 539           /* pathname starts with orig_prefix.  */
 540           const char *pathname_tail = &pathname[orig_prefix_len];
 541           char *result =
 542             (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
 543 
 544 #ifdef NO_XMALLOC
 545           if (result != NULL)
 546 #endif
 547             {
 548               memcpy (result, curr_prefix, curr_prefix_len);
 549               strcpy (result + curr_prefix_len, pathname_tail);
 550               return result;
 551             }
 552         }
 553     }
 554 
 555 #ifdef __EMX__
 556 # ifdef __KLIBC__
 557 #  undef strncmp
 558 
 559   if (strncmp (pathname, "/@unixroot", 10) == 0
 560       && (pathname[10] == '\0' || ISSLASH (pathname[10])))
 561     {
 562       /* kLIBC itself processes /@unixroot prefix */
 563       return pathname;
 564     }
 565   else
 566 # endif
 567   if (ISSLASH (pathname[0]))
 568     {
 569       const char *unixroot = getenv ("UNIXROOT");
 570 
 571       if (unixroot && HAS_DEVICE (unixroot) && unixroot[2] == '\0')
 572         {
 573           char *result = (char *) xmalloc (2 + strlen (pathname) + 1);
 574 #ifdef NO_XMALLOC
 575           if (result != NULL)
 576 #endif
 577             {
 578               memcpy (result, unixroot, 2);
 579               strcpy (result + 2, pathname);
 580               return result;
 581             }
 582         }
 583     }
 584 #endif
 585 
 586   /* Nothing to relocate.  */
 587   return pathname;
 588 }
 589 
 590 /* Returns the pathname, relocated according to the current installation
 591    directory.
 592    This function sets *ALLOCATEDP to the allocated memory, or to NULL if
 593    no memory allocation occurs.  So that, after you're done with the return
 594    value, to reclaim allocated memory, you can do: free (*ALLOCATEDP).  */
 595 const char *
 596 relocate2 (const char *pathname, char **allocatedp)
     /* [previous][next][first][last][top][bottom][index][help] */
 597 {
 598   const char *result = relocate (pathname);
 599   *allocatedp = (result != pathname ? (char *) result : NULL);
 600   return result;
 601 }
 602 
 603 #endif

/* [previous][next][first][last][top][bottom][index][help] */