1/* Provide relocatable packages.
2   Copyright (C) 2003-2006 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#include <config.h>
29
30/* Specification.  */
31#include "relocatable.h"
32
33#if ENABLE_RELOCATABLE
34
35#include <stddef.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39
40#ifdef NO_XMALLOC
41# define xmalloc malloc
42#else
43# include "xalloc.h"
44#endif
45
46#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
47# define WIN32_LEAN_AND_MEAN
48# include <windows.h>
49#endif
50
51#if DEPENDS_ON_LIBCHARSET
52# include <libcharset.h>
53#endif
54#if DEPENDS_ON_LIBICONV && HAVE_ICONV
55# include <iconv.h>
56#endif
57#if DEPENDS_ON_LIBINTL && ENABLE_NLS
58# include <libintl.h>
59#endif
60
61/* Faked cheap 'bool'.  */
62#undef bool
63#undef false
64#undef true
65#define bool int
66#define false 0
67#define true 1
68
69/* Pathname support.
70   ISSLASH(C)           tests whether C is a directory separator character.
71   IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
72 */
73#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
74  /* Win32, Cygwin, OS/2, DOS */
75# define ISSLASH(C) ((C) == '/' || (C) == '\\')
76# define HAS_DEVICE(P) \
77    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
78     && (P)[1] == ':')
79# define IS_PATH_WITH_DIR(P) \
80    (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
81# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
82#else
83  /* Unix */
84# define ISSLASH(C) ((C) == '/')
85# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
86# define FILE_SYSTEM_PREFIX_LEN(P) 0
87#endif
88
89/* Original installation prefix.  */
90static char *orig_prefix;
91static size_t orig_prefix_len;
92/* Current installation prefix.  */
93static char *curr_prefix;
94static size_t curr_prefix_len;
95/* These prefixes do not end in a slash.  Anything that will be concatenated
96   to them must start with a slash.  */
97
98/* Sets the original and the current installation prefix of this module.
99   Relocation simply replaces a pathname starting with the original prefix
100   by the corresponding pathname with the current prefix instead.  Both
101   prefixes should be directory names without trailing slash (i.e. use ""
102   instead of "/").  */
103static void
104set_this_relocation_prefix (const char *orig_prefix_arg,
105			    const char *curr_prefix_arg)
106{
107  if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
108      /* Optimization: if orig_prefix and curr_prefix are equal, the
109	 relocation is a nop.  */
110      && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
111    {
112      /* Duplicate the argument strings.  */
113      char *memory;
114
115      orig_prefix_len = strlen (orig_prefix_arg);
116      curr_prefix_len = strlen (curr_prefix_arg);
117      memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
118#ifdef NO_XMALLOC
119      if (memory != NULL)
120#endif
121	{
122	  memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
123	  orig_prefix = memory;
124	  memory += orig_prefix_len + 1;
125	  memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
126	  curr_prefix = memory;
127	  return;
128	}
129    }
130  orig_prefix = NULL;
131  curr_prefix = NULL;
132  /* Don't worry about wasted memory here - this function is usually only
133     called once.  */
134}
135
136/* Sets the original and the current installation prefix of the package.
137   Relocation simply replaces a pathname starting with the original prefix
138   by the corresponding pathname with the current prefix instead.  Both
139   prefixes should be directory names without trailing slash (i.e. use ""
140   instead of "/").  */
141void
142set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
143{
144  set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
145
146  /* Now notify all dependent libraries.  */
147#if DEPENDS_ON_LIBCHARSET
148  libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
149#endif
150#if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
151  libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
152#endif
153#if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
154  libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
155#endif
156}
157
158#if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR)
159
160/* Convenience function:
161   Computes the current installation prefix, based on the original
162   installation prefix, the original installation directory of a particular
163   file, and the current pathname of this file.  Returns NULL upon failure.  */
164#ifdef IN_LIBRARY
165#define compute_curr_prefix local_compute_curr_prefix
166static
167#endif
168const char *
169compute_curr_prefix (const char *orig_installprefix,
170		     const char *orig_installdir,
171		     const char *curr_pathname)
172{
173  const 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 filesystem 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 filesystem */
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      /* Unexpected: The curr_installdir does not end with rel_installdir.  */
258      return NULL;
259
260    {
261      size_t curr_prefix_len = cp - curr_installdir;
262      char *curr_prefix;
263
264      curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
265#ifdef NO_XMALLOC
266      if (curr_prefix == NULL)
267	return NULL;
268#endif
269      memcpy (curr_prefix, curr_installdir, curr_prefix_len);
270      curr_prefix[curr_prefix_len] = '\0';
271
272      return curr_prefix;
273    }
274  }
275}
276
277#endif /* !IN_LIBRARY || PIC */
278
279#if defined PIC && defined INSTALLDIR
280
281/* Full pathname of shared library, or NULL.  */
282static char *shared_library_fullname;
283
284#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
285
286/* Determine the full pathname of the shared library when it is loaded.  */
287
288BOOL WINAPI
289DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
290{
291  (void) reserved;
292
293  if (event == DLL_PROCESS_ATTACH)
294    {
295      /* The DLL is being loaded into an application's address range.  */
296      static char location[MAX_PATH];
297
298      if (!GetModuleFileName (module_handle, location, sizeof (location)))
299	/* Shouldn't happen.  */
300	return FALSE;
301
302      if (!IS_PATH_WITH_DIR (location))
303	/* Shouldn't happen.  */
304	return FALSE;
305
306      {
307#if defined __CYGWIN__
308	/* On Cygwin, we need to convert paths coming from Win32 system calls
309	   to the Unix-like slashified notation.  */
310	static char location_as_posix_path[2 * MAX_PATH];
311	/* There's no error return defined for cygwin_conv_to_posix_path.
312	   See cygwin-api/func-cygwin-conv-to-posix-path.html.
313	   Does it overflow the buffer of expected size MAX_PATH or does it
314	   truncate the path?  I don't know.  Let's catch both.  */
315	cygwin_conv_to_posix_path (location, location_as_posix_path);
316	location_as_posix_path[MAX_PATH - 1] = '\0';
317	if (strlen (location_as_posix_path) >= MAX_PATH - 1)
318	  /* A sign of buffer overflow or path truncation.  */
319	  return FALSE;
320	shared_library_fullname = strdup (location_as_posix_path);
321#else
322	shared_library_fullname = strdup (location);
323#endif
324      }
325    }
326
327  return TRUE;
328}
329
330#else /* Unix except Cygwin */
331
332static void
333find_shared_library_fullname ()
334{
335#if defined __linux__ && __GLIBC__ >= 2
336  /* Linux has /proc/self/maps. glibc 2 has the getline() function.  */
337  FILE *fp;
338
339  /* Open the current process' maps file.  It describes one VMA per line.  */
340  fp = fopen ("/proc/self/maps", "r");
341  if (fp)
342    {
343      unsigned long address = (unsigned long) &find_shared_library_fullname;
344      for (;;)
345	{
346	  unsigned long start, end;
347	  int c;
348
349	  if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
350	    break;
351	  if (address >= start && address <= end - 1)
352	    {
353	      /* Found it.  Now see if this line contains a filename.  */
354	      while (c = getc (fp), c != EOF && c != '\n' && c != '/')
355		continue;
356	      if (c == '/')
357		{
358		  size_t size;
359		  int len;
360
361		  ungetc (c, fp);
362		  shared_library_fullname = NULL; size = 0;
363		  len = getline (&shared_library_fullname, &size, fp);
364		  if (len >= 0)
365		    {
366		      /* Success: filled shared_library_fullname.  */
367		      if (len > 0 && shared_library_fullname[len - 1] == '\n')
368			shared_library_fullname[len - 1] = '\0';
369		    }
370		}
371	      break;
372	    }
373	  while (c = getc (fp), c != EOF && c != '\n')
374	    continue;
375	}
376      fclose (fp);
377    }
378#endif
379}
380
381#endif /* (WIN32 or Cygwin) / (Unix except Cygwin) */
382
383/* Return the full pathname of the current shared library.
384   Return NULL if unknown.
385   Guaranteed to work only on Linux, Cygwin and Woe32.  */
386static char *
387get_shared_library_fullname ()
388{
389#if !(defined _WIN32 || defined __WIN32__ || defined __CYGWIN__)
390  static bool tried_find_shared_library_fullname;
391  if (!tried_find_shared_library_fullname)
392    {
393      find_shared_library_fullname ();
394      tried_find_shared_library_fullname = true;
395    }
396#endif
397  return shared_library_fullname;
398}
399
400#endif /* PIC */
401
402/* Returns the pathname, relocated according to the current installation
403   directory.  */
404const char *
405relocate (const char *pathname)
406{
407#if defined PIC && defined INSTALLDIR
408  static int initialized;
409
410  /* Initialization code for a shared library.  */
411  if (!initialized)
412    {
413      /* At this point, orig_prefix and curr_prefix likely have already been
414	 set through the main program's set_program_name_and_installdir
415	 function.  This is sufficient in the case that the library has
416	 initially been installed in the same orig_prefix.  But we can do
417	 better, to also cover the cases that 1. it has been installed
418	 in a different prefix before being moved to orig_prefix and (later)
419	 to curr_prefix, 2. unlike the program, it has not moved away from
420	 orig_prefix.  */
421      const char *orig_installprefix = INSTALLPREFIX;
422      const char *orig_installdir = INSTALLDIR;
423      const char *curr_prefix_better;
424
425      curr_prefix_better =
426	compute_curr_prefix (orig_installprefix, orig_installdir,
427			     get_shared_library_fullname ());
428      if (curr_prefix_better == NULL)
429	curr_prefix_better = curr_prefix;
430
431      set_relocation_prefix (orig_installprefix, curr_prefix_better);
432
433      initialized = 1;
434    }
435#endif
436
437  /* Note: It is not necessary to perform case insensitive comparison here,
438     even for DOS-like filesystems, because the pathname argument was
439     typically created from the same Makefile variable as orig_prefix came
440     from.  */
441  if (orig_prefix != NULL && curr_prefix != NULL
442      && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
443    {
444      if (pathname[orig_prefix_len] == '\0')
445	/* pathname equals orig_prefix.  */
446	return curr_prefix;
447      if (ISSLASH (pathname[orig_prefix_len]))
448	{
449	  /* pathname starts with orig_prefix.  */
450	  const char *pathname_tail = &pathname[orig_prefix_len];
451	  char *result =
452	    (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
453
454#ifdef NO_XMALLOC
455	  if (result != NULL)
456#endif
457	    {
458	      memcpy (result, curr_prefix, curr_prefix_len);
459	      strcpy (result + curr_prefix_len, pathname_tail);
460	      return result;
461	    }
462	}
463    }
464  /* Nothing to relocate.  */
465  return pathname;
466}
467
468#endif
469