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 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 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. */
90 static char *orig_prefix;
91 static size_t orig_prefix_len;
92 /* Current installation prefix. */
93 static char *curr_prefix;
94 static 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 "/"). */
103 static void
set_this_relocation_prefix(const char * orig_prefix_arg,const char * curr_prefix_arg)104 set_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 "/"). */
141 void
set_relocation_prefix(const char * orig_prefix_arg,const char * curr_prefix_arg)142 set_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
166 static
167 #endif
168 const char *
compute_curr_prefix(const char * orig_installprefix,const char * orig_installdir,const char * curr_pathname)169 compute_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. */
282 static 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
288 BOOL WINAPI
DllMain(HINSTANCE module_handle,DWORD event,LPVOID reserved)289 DllMain (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
332 static void
find_shared_library_fullname()333 find_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. */
386 static char *
get_shared_library_fullname()387 get_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. */
404 const char *
relocate(const char * pathname)405 relocate (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