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