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@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.  */
94static char *orig_prefix;
95static size_t orig_prefix_len;
96/* Current installation prefix.  */
97static char *curr_prefix;
98static 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 "/").  */
107static void
108set_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 "/").  */
145void
146set_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
170static
171#endif
172const char *
173compute_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.  */
283static 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
289BOOL WINAPI
290DllMain (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
315static void
316find_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.  */
369static char *
370get_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.  */
387const char *
388relocate (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