Back to index

cell-binutils  2.17cvs20070401
relocatable.c
Go to the documentation of this file.
00001 /* Provide relocatable packages.
00002    Copyright (C) 2003 Free Software Foundation, Inc.
00003    Written by Bruno Haible <bruno@clisp.org>, 2003.
00004 
00005    This program is free software; you can redistribute it and/or modify it
00006    under the terms of the GNU Library General Public License as published
00007    by the Free Software Foundation; either version 2, or (at your option)
00008    any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public
00016    License along with this program; if not, write to the Free Software
00017    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
00018    USA.  */
00019 
00020 
00021 /* Tell glibc's <stdio.h> to provide a prototype for getline().
00022    This must come before <config.h> because <config.h> may include
00023    <features.h>, and once <features.h> has been included, it's too late.  */
00024 #ifndef _GNU_SOURCE
00025 # define _GNU_SOURCE 1
00026 #endif
00027 
00028 #ifdef HAVE_CONFIG_H
00029 # include "config.h"
00030 #endif
00031 
00032 /* Specification.  */
00033 #include "relocatable.h"
00034 
00035 #if ENABLE_RELOCATABLE
00036 
00037 #include <stddef.h>
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 
00042 #ifdef NO_XMALLOC
00043 # define xmalloc malloc
00044 #else
00045 # include "xmalloc.h"
00046 #endif
00047 
00048 #if DEPENDS_ON_LIBCHARSET
00049 # include <libcharset.h>
00050 #endif
00051 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
00052 # include <iconv.h>
00053 #endif
00054 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
00055 # include <libintl.h>
00056 #endif
00057 
00058 /* Faked cheap 'bool'.  */
00059 #undef bool
00060 #undef false
00061 #undef true
00062 #define bool int
00063 #define false 0
00064 #define true 1
00065 
00066 /* Pathname support.
00067    ISSLASH(C)           tests whether C is a directory separator character.
00068    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
00069  */
00070 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
00071   /* Win32, OS/2, DOS */
00072 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
00073 # define HAS_DEVICE(P) \
00074     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
00075      && (P)[1] == ':')
00076 # define IS_PATH_WITH_DIR(P) \
00077     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
00078 # define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
00079 #else
00080   /* Unix */
00081 # define ISSLASH(C) ((C) == '/')
00082 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
00083 # define FILESYSTEM_PREFIX_LEN(P) 0
00084 #endif
00085 
00086 /* Original installation prefix.  */
00087 static char *orig_prefix;
00088 static size_t orig_prefix_len;
00089 /* Current installation prefix.  */
00090 static char *curr_prefix;
00091 static size_t curr_prefix_len;
00092 /* These prefixes do not end in a slash.  Anything that will be concatenated
00093    to them must start with a slash.  */
00094 
00095 /* Sets the original and the current installation prefix of this module.
00096    Relocation simply replaces a pathname starting with the original prefix
00097    by the corresponding pathname with the current prefix instead.  Both
00098    prefixes should be directory names without trailing slash (i.e. use ""
00099    instead of "/").  */
00100 static void
00101 set_this_relocation_prefix (const char *orig_prefix_arg,
00102                          const char *curr_prefix_arg)
00103 {
00104   if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
00105       /* Optimization: if orig_prefix and curr_prefix are equal, the
00106         relocation is a nop.  */
00107       && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
00108     {
00109       /* Duplicate the argument strings.  */
00110       char *memory;
00111 
00112       orig_prefix_len = strlen (orig_prefix_arg);
00113       curr_prefix_len = strlen (curr_prefix_arg);
00114       memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
00115 #ifdef NO_XMALLOC
00116       if (memory != NULL)
00117 #endif
00118        {
00119          memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
00120          orig_prefix = memory;
00121          memory += orig_prefix_len + 1;
00122          memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
00123          curr_prefix = memory;
00124          return;
00125        }
00126     }
00127   orig_prefix = NULL;
00128   curr_prefix = NULL;
00129   /* Don't worry about wasted memory here - this function is usually only
00130      called once.  */
00131 }
00132 
00133 /* Sets the original and the current installation prefix of the package.
00134    Relocation simply replaces a pathname starting with the original prefix
00135    by the corresponding pathname with the current prefix instead.  Both
00136    prefixes should be directory names without trailing slash (i.e. use ""
00137    instead of "/").  */
00138 void
00139 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
00140 {
00141   set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00142 
00143   /* Now notify all dependent libraries.  */
00144 #if DEPENDS_ON_LIBCHARSET
00145   libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00146 #endif
00147 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
00148   libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00149 #endif
00150 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
00151   libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00152 #endif
00153 }
00154 
00155 /* Convenience function:
00156    Computes the current installation prefix, based on the original
00157    installation prefix, the original installation directory of a particular
00158    file, and the current pathname of this file.  Returns NULL upon failure.  */
00159 #ifdef IN_LIBRARY
00160 #define compute_curr_prefix local_compute_curr_prefix
00161 static
00162 #endif
00163 const char *
00164 compute_curr_prefix (const char *orig_installprefix,
00165                    const char *orig_installdir,
00166                    const char *curr_pathname)
00167 {
00168   const char *curr_installdir;
00169   const char *rel_installdir;
00170 
00171   if (curr_pathname == NULL)
00172     return NULL;
00173 
00174   /* Determine the relative installation directory, relative to the prefix.
00175      This is simply the difference between orig_installprefix and
00176      orig_installdir.  */
00177   if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
00178       != 0)
00179     /* Shouldn't happen - nothing should be installed outside $(prefix).  */
00180     return NULL;
00181   rel_installdir = orig_installdir + strlen (orig_installprefix);
00182 
00183   /* Determine the current installation directory.  */
00184   {
00185     const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
00186     const char *p = curr_pathname + strlen (curr_pathname);
00187     char *q;
00188 
00189     while (p > p_base)
00190       {
00191        p--;
00192        if (ISSLASH (*p))
00193          break;
00194       }
00195 
00196     q = (char *) xmalloc (p - curr_pathname + 1);
00197 #ifdef NO_XMALLOC
00198     if (q == NULL)
00199       return NULL;
00200 #endif
00201     memcpy (q, curr_pathname, p - curr_pathname);
00202     q[p - curr_pathname] = '\0';
00203     curr_installdir = q;
00204   }
00205 
00206   /* Compute the current installation prefix by removing the trailing
00207      rel_installdir from it.  */
00208   {
00209     const char *rp = rel_installdir + strlen (rel_installdir);
00210     const char *cp = curr_installdir + strlen (curr_installdir);
00211     const char *cp_base =
00212       curr_installdir + FILESYSTEM_PREFIX_LEN (curr_installdir);
00213 
00214     while (rp > rel_installdir && cp > cp_base)
00215       {
00216        bool same = false;
00217        const char *rpi = rp;
00218        const char *cpi = cp;
00219 
00220        while (rpi > rel_installdir && cpi > cp_base)
00221          {
00222            rpi--;
00223            cpi--;
00224            if (ISSLASH (*rpi) || ISSLASH (*cpi))
00225              {
00226               if (ISSLASH (*rpi) && ISSLASH (*cpi))
00227                 same = true;
00228               break;
00229              }
00230 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
00231            /* Win32, OS/2, DOS - case insignificant filesystem */
00232            if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
00233               != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
00234              break;
00235 #else
00236            if (*rpi != *cpi)
00237              break;
00238 #endif
00239          }
00240        if (!same)
00241          break;
00242        /* The last pathname component was the same.  opi and cpi now point
00243           to the slash before it.  */
00244        rp = rpi;
00245        cp = cpi;
00246       }
00247 
00248     if (rp > rel_installdir)
00249       /* Unexpected: The curr_installdir does not end with rel_installdir.  */
00250       return NULL;
00251 
00252     {
00253       size_t curr_prefix_len = cp - curr_installdir;
00254       char *curr_prefix;
00255 
00256       curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
00257 #ifdef NO_XMALLOC
00258       if (curr_prefix == NULL)
00259        return NULL;
00260 #endif
00261       memcpy (curr_prefix, curr_installdir, curr_prefix_len);
00262       curr_prefix[curr_prefix_len] = '\0';
00263 
00264       return curr_prefix;
00265     }
00266   }
00267 }
00268 
00269 #if defined PIC && defined INSTALLDIR
00270 
00271 /* Full pathname of shared library, or NULL.  */
00272 static char *shared_library_fullname;
00273 
00274 #if defined _WIN32 || defined __WIN32__
00275 
00276 /* Determine the full pathname of the shared library when it is loaded.  */
00277 
00278 BOOL WINAPI
00279 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
00280 {
00281   (void) reserved;
00282 
00283   if (event == DLL_PROCESS_ATTACH)
00284     {
00285       /* The DLL is being loaded into an application's address range.  */
00286       static char location[MAX_PATH];
00287 
00288       if (!GetModuleFileName (module_handle, location, sizeof (location)))
00289        /* Shouldn't happen.  */
00290        return FALSE;
00291 
00292       if (!IS_PATH_WITH_DIR (location))
00293        /* Shouldn't happen.  */
00294        return FALSE;
00295 
00296       shared_library_fullname = strdup (location);
00297     }
00298 
00299   return TRUE;
00300 }
00301 
00302 #else /* Unix */
00303 
00304 static void
00305 find_shared_library_fullname ()
00306 {
00307 #ifdef __linux__
00308   FILE *fp;
00309 
00310   /* Open the current process' maps file.  It describes one VMA per line.  */
00311   fp = fopen ("/proc/self/maps", "r");
00312   if (fp)
00313     {
00314       unsigned long address = (unsigned long) &find_shared_library_fullname;
00315       for (;;)
00316        {
00317          unsigned long start, end;
00318          int c;
00319 
00320          if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
00321            break;
00322          if (address >= start && address <= end - 1)
00323            {
00324              /* Found it.  Now see if this line contains a filename.  */
00325              while (c = getc (fp), c != EOF && c != '\n' && c != '/')
00326               continue;
00327              if (c == '/')
00328               {
00329                 size_t size;
00330                 int len;
00331 
00332                 ungetc (c, fp);
00333                 shared_library_fullname = NULL; size = 0;
00334                 len = getline (&shared_library_fullname, &size, fp);
00335                 if (len >= 0)
00336                   {
00337                     /* Success: filled shared_library_fullname.  */
00338                     if (len > 0 && shared_library_fullname[len - 1] == '\n')
00339                      shared_library_fullname[len - 1] = '\0';
00340                   }
00341               }
00342              break;
00343            }
00344          while (c = getc (fp), c != EOF && c != '\n')
00345            continue;
00346        }
00347       fclose (fp);
00348     }
00349 #endif
00350 }
00351 
00352 #endif /* WIN32 / Unix */
00353 
00354 /* Return the full pathname of the current shared library.
00355    Return NULL if unknown.
00356    Guaranteed to work only on Linux and Woe32.  */
00357 static char *
00358 get_shared_library_fullname ()
00359 {
00360 #if !(defined _WIN32 || defined __WIN32__)
00361   static bool tried_find_shared_library_fullname;
00362   if (!tried_find_shared_library_fullname)
00363     {
00364       find_shared_library_fullname ();
00365       tried_find_shared_library_fullname = true;
00366     }
00367 #endif
00368   return shared_library_fullname;
00369 }
00370 
00371 #endif /* PIC */
00372 
00373 /* Returns the pathname, relocated according to the current installation
00374    directory.  */
00375 const char *
00376 relocate (const char *pathname)
00377 {
00378 #if defined PIC && defined INSTALLDIR
00379   static int initialized;
00380 
00381   /* Initialization code for a shared library.  */
00382   if (!initialized)
00383     {
00384       /* At this point, orig_prefix and curr_prefix likely have already been
00385         set through the main program's set_program_name_and_installdir
00386         function.  This is sufficient in the case that the library has
00387         initially been installed in the same orig_prefix.  But we can do
00388         better, to also cover the cases that 1. it has been installed
00389         in a different prefix before being moved to orig_prefix and (later)
00390         to curr_prefix, 2. unlike the program, it has not moved away from
00391         orig_prefix.  */
00392       const char *orig_installprefix = INSTALLPREFIX;
00393       const char *orig_installdir = INSTALLDIR;
00394       const char *curr_prefix_better;
00395 
00396       curr_prefix_better =
00397        compute_curr_prefix (orig_installprefix, orig_installdir,
00398                           get_shared_library_fullname ());
00399       if (curr_prefix_better == NULL)
00400        curr_prefix_better = curr_prefix;
00401 
00402       set_relocation_prefix (orig_installprefix, curr_prefix_better);
00403 
00404       initialized = 1;
00405     }
00406 #endif
00407 
00408   /* Note: It is not necessary to perform case insensitive comparison here,
00409      even for DOS-like filesystems, because the pathname argument was
00410      typically created from the same Makefile variable as orig_prefix came
00411      from.  */
00412   if (orig_prefix != NULL && curr_prefix != NULL
00413       && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
00414     {
00415       if (pathname[orig_prefix_len] == '\0')
00416        /* pathname equals orig_prefix.  */
00417        return curr_prefix;
00418       if (ISSLASH (pathname[orig_prefix_len]))
00419        {
00420          /* pathname starts with orig_prefix.  */
00421          const char *pathname_tail = &pathname[orig_prefix_len];
00422          char *result =
00423            (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
00424 
00425 #ifdef NO_XMALLOC
00426          if (result != NULL)
00427 #endif
00428            {
00429              memcpy (result, curr_prefix, curr_prefix_len);
00430              strcpy (result + curr_prefix_len, pathname_tail);
00431              return result;
00432            }
00433        }
00434     }
00435   /* Nothing to relocate.  */
00436   return pathname;
00437 }
00438 
00439 #endif