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