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