Home | History | Annotate | Line # | Download | only in intl
relocatable.c revision 1.1.1.1
      1 /*	$NetBSD: relocatable.c,v 1.1.1.1 2016/01/14 00:11:28 christos Exp $	*/
      2 
      3 /* Provide relocatable packages.
      4    Copyright (C) 2003 Free Software Foundation, Inc.
      5    Written by Bruno Haible <bruno (at) clisp.org>, 2003.
      6 
      7    This program is free software; you can redistribute it and/or modify it
      8    under the terms of the GNU Library General Public License as published
      9    by the Free Software Foundation; either version 2, or (at your option)
     10    any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15    Library General Public License for more details.
     16 
     17    You should have received a copy of the GNU Library General Public
     18    License along with this program; if not, write to the Free Software
     19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     20    USA.  */
     21 
     22 
     23 /* Tell glibc's <stdio.h> to provide a prototype for getline().
     24    This must come before <config.h> because <config.h> may include
     25    <features.h>, and once <features.h> has been included, it's too late.  */
     26 #ifndef _GNU_SOURCE
     27 # define _GNU_SOURCE	1
     28 #endif
     29 
     30 #ifdef HAVE_CONFIG_H
     31 # include "config.h"
     32 #endif
     33 
     34 /* Specification.  */
     35 #include "relocatable.h"
     36 
     37 #if ENABLE_RELOCATABLE
     38 
     39 #include <stddef.h>
     40 #include <stdio.h>
     41 #include <stdlib.h>
     42 #include <string.h>
     43 
     44 #ifdef NO_XMALLOC
     45 # define xmalloc malloc
     46 #else
     47 # include "xalloc.h"
     48 #endif
     49 
     50 #if defined _WIN32 || defined __WIN32__
     51 # define WIN32_LEAN_AND_MEAN
     52 # include <windows.h>
     53 #endif
     54 
     55 #if DEPENDS_ON_LIBCHARSET
     56 # include <libcharset.h>
     57 #endif
     58 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
     59 # include <iconv.h>
     60 #endif
     61 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
     62 # include <libintl.h>
     63 #endif
     64 
     65 /* Faked cheap 'bool'.  */
     66 #undef bool
     67 #undef false
     68 #undef true
     69 #define bool int
     70 #define false 0
     71 #define true 1
     72 
     73 /* Pathname support.
     74    ISSLASH(C)           tests whether C is a directory separator character.
     75    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
     76  */
     77 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
     78   /* Win32, OS/2, DOS */
     79 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
     80 # define HAS_DEVICE(P) \
     81     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
     82      && (P)[1] == ':')
     83 # define IS_PATH_WITH_DIR(P) \
     84     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
     85 # define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
     86 #else
     87   /* Unix */
     88 # define ISSLASH(C) ((C) == '/')
     89 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
     90 # define FILESYSTEM_PREFIX_LEN(P) 0
     91 #endif
     92 
     93 /* Original installation prefix.  */
     94 static char *orig_prefix;
     95 static size_t orig_prefix_len;
     96 /* Current installation prefix.  */
     97 static char *curr_prefix;
     98 static size_t curr_prefix_len;
     99 /* These prefixes do not end in a slash.  Anything that will be concatenated
    100    to them must start with a slash.  */
    101 
    102 /* Sets the original and the current installation prefix of this module.
    103    Relocation simply replaces a pathname starting with the original prefix
    104    by the corresponding pathname with the current prefix instead.  Both
    105    prefixes should be directory names without trailing slash (i.e. use ""
    106    instead of "/").  */
    107 static void
    108 set_this_relocation_prefix (const char *orig_prefix_arg,
    109 			    const char *curr_prefix_arg)
    110 {
    111   if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
    112       /* Optimization: if orig_prefix and curr_prefix are equal, the
    113 	 relocation is a nop.  */
    114       && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
    115     {
    116       /* Duplicate the argument strings.  */
    117       char *memory;
    118 
    119       orig_prefix_len = strlen (orig_prefix_arg);
    120       curr_prefix_len = strlen (curr_prefix_arg);
    121       memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
    122 #ifdef NO_XMALLOC
    123       if (memory != NULL)
    124 #endif
    125 	{
    126 	  memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
    127 	  orig_prefix = memory;
    128 	  memory += orig_prefix_len + 1;
    129 	  memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
    130 	  curr_prefix = memory;
    131 	  return;
    132 	}
    133     }
    134   orig_prefix = NULL;
    135   curr_prefix = NULL;
    136   /* Don't worry about wasted memory here - this function is usually only
    137      called once.  */
    138 }
    139 
    140 /* Sets the original and the current installation prefix of the package.
    141    Relocation simply replaces a pathname starting with the original prefix
    142    by the corresponding pathname with the current prefix instead.  Both
    143    prefixes should be directory names without trailing slash (i.e. use ""
    144    instead of "/").  */
    145 void
    146 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
    147 {
    148   set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
    149 
    150   /* Now notify all dependent libraries.  */
    151 #if DEPENDS_ON_LIBCHARSET
    152   libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
    153 #endif
    154 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
    155   libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
    156 #endif
    157 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
    158   libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
    159 #endif
    160 }
    161 
    162 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR)
    163 
    164 /* Convenience function:
    165    Computes the current installation prefix, based on the original
    166    installation prefix, the original installation directory of a particular
    167    file, and the current pathname of this file.  Returns NULL upon failure.  */
    168 #ifdef IN_LIBRARY
    169 #define compute_curr_prefix local_compute_curr_prefix
    170 static
    171 #endif
    172 const char *
    173 compute_curr_prefix (const char *orig_installprefix,
    174 		     const char *orig_installdir,
    175 		     const char *curr_pathname)
    176 {
    177   const char *curr_installdir;
    178   const char *rel_installdir;
    179 
    180   if (curr_pathname == NULL)
    181     return NULL;
    182 
    183   /* Determine the relative installation directory, relative to the prefix.
    184      This is simply the difference between orig_installprefix and
    185      orig_installdir.  */
    186   if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
    187       != 0)
    188     /* Shouldn't happen - nothing should be installed outside $(prefix).  */
    189     return NULL;
    190   rel_installdir = orig_installdir + strlen (orig_installprefix);
    191 
    192   /* Determine the current installation directory.  */
    193   {
    194     const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
    195     const char *p = curr_pathname + strlen (curr_pathname);
    196     char *q;
    197 
    198     while (p > p_base)
    199       {
    200 	p--;
    201 	if (ISSLASH (*p))
    202 	  break;
    203       }
    204 
    205     q = (char *) xmalloc (p - curr_pathname + 1);
    206 #ifdef NO_XMALLOC
    207     if (q == NULL)
    208       return NULL;
    209 #endif
    210     memcpy (q, curr_pathname, p - curr_pathname);
    211     q[p - curr_pathname] = '\0';
    212     curr_installdir = q;
    213   }
    214 
    215   /* Compute the current installation prefix by removing the trailing
    216      rel_installdir from it.  */
    217   {
    218     const char *rp = rel_installdir + strlen (rel_installdir);
    219     const char *cp = curr_installdir + strlen (curr_installdir);
    220     const char *cp_base =
    221       curr_installdir + FILESYSTEM_PREFIX_LEN (curr_installdir);
    222 
    223     while (rp > rel_installdir && cp > cp_base)
    224       {
    225 	bool same = false;
    226 	const char *rpi = rp;
    227 	const char *cpi = cp;
    228 
    229 	while (rpi > rel_installdir && cpi > cp_base)
    230 	  {
    231 	    rpi--;
    232 	    cpi--;
    233 	    if (ISSLASH (*rpi) || ISSLASH (*cpi))
    234 	      {
    235 		if (ISSLASH (*rpi) && ISSLASH (*cpi))
    236 		  same = true;
    237 		break;
    238 	      }
    239 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
    240 	    /* Win32, OS/2, DOS - case insignificant filesystem */
    241 	    if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
    242 		!= (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
    243 	      break;
    244 #else
    245 	    if (*rpi != *cpi)
    246 	      break;
    247 #endif
    248 	  }
    249 	if (!same)
    250 	  break;
    251 	/* The last pathname component was the same.  opi and cpi now point
    252 	   to the slash before it.  */
    253 	rp = rpi;
    254 	cp = cpi;
    255       }
    256 
    257     if (rp > rel_installdir)
    258       /* Unexpected: The curr_installdir does not end with rel_installdir.  */
    259       return NULL;
    260 
    261     {
    262       size_t curr_prefix_len = cp - curr_installdir;
    263       char *curr_prefix;
    264 
    265       curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
    266 #ifdef NO_XMALLOC
    267       if (curr_prefix == NULL)
    268 	return NULL;
    269 #endif
    270       memcpy (curr_prefix, curr_installdir, curr_prefix_len);
    271       curr_prefix[curr_prefix_len] = '\0';
    272 
    273       return curr_prefix;
    274     }
    275   }
    276 }
    277 
    278 #endif /* !IN_LIBRARY || PIC */
    279 
    280 #if defined PIC && defined INSTALLDIR
    281 
    282 /* Full pathname of shared library, or NULL.  */
    283 static char *shared_library_fullname;
    284 
    285 #if defined _WIN32 || defined __WIN32__
    286 
    287 /* Determine the full pathname of the shared library when it is loaded.  */
    288 
    289 BOOL WINAPI
    290 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
    291 {
    292   (void) reserved;
    293 
    294   if (event == DLL_PROCESS_ATTACH)
    295     {
    296       /* The DLL is being loaded into an application's address range.  */
    297       static char location[MAX_PATH];
    298 
    299       if (!GetModuleFileName (module_handle, location, sizeof (location)))
    300 	/* Shouldn't happen.  */
    301 	return FALSE;
    302 
    303       if (!IS_PATH_WITH_DIR (location))
    304 	/* Shouldn't happen.  */
    305 	return FALSE;
    306 
    307       shared_library_fullname = strdup (location);
    308     }
    309 
    310   return TRUE;
    311 }
    312 
    313 #else /* Unix */
    314 
    315 static void
    316 find_shared_library_fullname ()
    317 {
    318 #if defined __linux__ && __GLIBC__ >= 2
    319   /* Linux has /proc/self/maps. glibc 2 has the getline() function.  */
    320   FILE *fp;
    321 
    322   /* Open the current process' maps file.  It describes one VMA per line.  */
    323   fp = fopen ("/proc/self/maps", "r");
    324   if (fp)
    325     {
    326       unsigned long address = (unsigned long) &find_shared_library_fullname;
    327       for (;;)
    328 	{
    329 	  unsigned long start, end;
    330 	  int c;
    331 
    332 	  if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
    333 	    break;
    334 	  if (address >= start && address <= end - 1)
    335 	    {
    336 	      /* Found it.  Now see if this line contains a filename.  */
    337 	      while (c = getc (fp), c != EOF && c != '\n' && c != '/')
    338 		continue;
    339 	      if (c == '/')
    340 		{
    341 		  size_t size;
    342 		  int len;
    343 
    344 		  ungetc (c, fp);
    345 		  shared_library_fullname = NULL; size = 0;
    346 		  len = getline (&shared_library_fullname, &size, fp);
    347 		  if (len >= 0)
    348 		    {
    349 		      /* Success: filled shared_library_fullname.  */
    350 		      if (len > 0 && shared_library_fullname[len - 1] == '\n')
    351 			shared_library_fullname[len - 1] = '\0';
    352 		    }
    353 		}
    354 	      break;
    355 	    }
    356 	  while (c = getc (fp), c != EOF && c != '\n')
    357 	    continue;
    358 	}
    359       fclose (fp);
    360     }
    361 #endif
    362 }
    363 
    364 #endif /* WIN32 / Unix */
    365 
    366 /* Return the full pathname of the current shared library.
    367    Return NULL if unknown.
    368    Guaranteed to work only on Linux and Woe32.  */
    369 static char *
    370 get_shared_library_fullname ()
    371 {
    372 #if !(defined _WIN32 || defined __WIN32__)
    373   static bool tried_find_shared_library_fullname;
    374   if (!tried_find_shared_library_fullname)
    375     {
    376       find_shared_library_fullname ();
    377       tried_find_shared_library_fullname = true;
    378     }
    379 #endif
    380   return shared_library_fullname;
    381 }
    382 
    383 #endif /* PIC */
    384 
    385 /* Returns the pathname, relocated according to the current installation
    386    directory.  */
    387 const char *
    388 relocate (const char *pathname)
    389 {
    390 #if defined PIC && defined INSTALLDIR
    391   static int initialized;
    392 
    393   /* Initialization code for a shared library.  */
    394   if (!initialized)
    395     {
    396       /* At this point, orig_prefix and curr_prefix likely have already been
    397 	 set through the main program's set_program_name_and_installdir
    398 	 function.  This is sufficient in the case that the library has
    399 	 initially been installed in the same orig_prefix.  But we can do
    400 	 better, to also cover the cases that 1. it has been installed
    401 	 in a different prefix before being moved to orig_prefix and (later)
    402 	 to curr_prefix, 2. unlike the program, it has not moved away from
    403 	 orig_prefix.  */
    404       const char *orig_installprefix = INSTALLPREFIX;
    405       const char *orig_installdir = INSTALLDIR;
    406       const char *curr_prefix_better;
    407 
    408       curr_prefix_better =
    409 	compute_curr_prefix (orig_installprefix, orig_installdir,
    410 			     get_shared_library_fullname ());
    411       if (curr_prefix_better == NULL)
    412 	curr_prefix_better = curr_prefix;
    413 
    414       set_relocation_prefix (orig_installprefix, curr_prefix_better);
    415 
    416       initialized = 1;
    417     }
    418 #endif
    419 
    420   /* Note: It is not necessary to perform case insensitive comparison here,
    421      even for DOS-like filesystems, because the pathname argument was
    422      typically created from the same Makefile variable as orig_prefix came
    423      from.  */
    424   if (orig_prefix != NULL && curr_prefix != NULL
    425       && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
    426     {
    427       if (pathname[orig_prefix_len] == '\0')
    428 	/* pathname equals orig_prefix.  */
    429 	return curr_prefix;
    430       if (ISSLASH (pathname[orig_prefix_len]))
    431 	{
    432 	  /* pathname starts with orig_prefix.  */
    433 	  const char *pathname_tail = &pathname[orig_prefix_len];
    434 	  char *result =
    435 	    (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
    436 
    437 #ifdef NO_XMALLOC
    438 	  if (result != NULL)
    439 #endif
    440 	    {
    441 	      memcpy (result, curr_prefix, curr_prefix_len);
    442 	      strcpy (result + curr_prefix_len, pathname_tail);
    443 	      return result;
    444 	    }
    445 	}
    446     }
    447   /* Nothing to relocate.  */
    448   return pathname;
    449 }
    450 
    451 #endif
    452