1 /* Provide relocatable packages.
2 Copyright (C) 2003-2005 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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)
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.
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,
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. */
25 # define _GNU_SOURCE 1
33 #include "relocatable.h"
35 #if ENABLE_RELOCATABLE
43 # define xmalloc malloc
48 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
49 # define WIN32_LEAN_AND_MEAN
53 #if DEPENDS_ON_LIBCHARSET
54 # include <libcharset.h>
56 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
59 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
63 /* Faked cheap 'bool'. */
72 ISSLASH(C) tests whether C is a directory separator character.
73 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
75 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
76 /* Win32, Cygwin, OS/2, DOS */
77 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
78 # define HAS_DEVICE(P) \
79 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
81 # define IS_PATH_WITH_DIR(P) \
82 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
83 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
86 # define ISSLASH(C) ((C) == '/')
87 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
88 # define FILE_SYSTEM_PREFIX_LEN(P) 0
91 /* Original installation prefix. */
92 static char *orig_prefix;
93 static size_t orig_prefix_len;
94 /* Current installation prefix. */
95 static char *curr_prefix;
96 static size_t curr_prefix_len;
97 /* These prefixes do not end in a slash. Anything that will be concatenated
98 to them must start with a slash. */
100 /* Sets the original and the current installation prefix of this module.
101 Relocation simply replaces a pathname starting with the original prefix
102 by the corresponding pathname with the current prefix instead. Both
103 prefixes should be directory names without trailing slash (i.e. use ""
106 set_this_relocation_prefix (const char *orig_prefix_arg,
107 const char *curr_prefix_arg)
109 if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
110 /* Optimization: if orig_prefix and curr_prefix are equal, the
111 relocation is a nop. */
112 && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
114 /* Duplicate the argument strings. */
117 orig_prefix_len = strlen (orig_prefix_arg);
118 curr_prefix_len = strlen (curr_prefix_arg);
119 memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
124 memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
125 orig_prefix = memory;
126 memory += orig_prefix_len + 1;
127 memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
128 curr_prefix = memory;
134 /* Don't worry about wasted memory here - this function is usually only
138 /* Sets the original and the current installation prefix of the package.
139 Relocation simply replaces a pathname starting with the original prefix
140 by the corresponding pathname with the current prefix instead. Both
141 prefixes should be directory names without trailing slash (i.e. use ""
144 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
146 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
148 /* Now notify all dependent libraries. */
149 #if DEPENDS_ON_LIBCHARSET
150 libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
152 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
153 libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
155 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
156 libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
160 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR)
162 /* Convenience function:
163 Computes the current installation prefix, based on the original
164 installation prefix, the original installation directory of a particular
165 file, and the current pathname of this file. Returns NULL upon failure. */
167 #define compute_curr_prefix local_compute_curr_prefix
171 compute_curr_prefix (const char *orig_installprefix,
172 const char *orig_installdir,
173 const char *curr_pathname)
175 const char *curr_installdir;
176 const char *rel_installdir;
178 if (curr_pathname == NULL)
181 /* Determine the relative installation directory, relative to the prefix.
182 This is simply the difference between orig_installprefix and
184 if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
186 /* Shouldn't happen - nothing should be installed outside $(prefix). */
188 rel_installdir = orig_installdir + strlen (orig_installprefix);
190 /* Determine the current installation directory. */
192 const char *p_base = curr_pathname + FILE_SYSTEM_PREFIX_LEN (curr_pathname);
193 const char *p = curr_pathname + strlen (curr_pathname);
203 q = (char *) xmalloc (p - curr_pathname + 1);
208 memcpy (q, curr_pathname, p - curr_pathname);
209 q[p - curr_pathname] = '\0';
213 /* Compute the current installation prefix by removing the trailing
214 rel_installdir from it. */
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);
221 while (rp > rel_installdir && cp > cp_base)
224 const char *rpi = rp;
225 const char *cpi = cp;
227 while (rpi > rel_installdir && cpi > cp_base)
231 if (ISSLASH (*rpi) || ISSLASH (*cpi))
233 if (ISSLASH (*rpi) && ISSLASH (*cpi))
237 /* Do case-insensitive comparison if the filesystem 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 filesystem */
242 if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
243 != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
252 /* The last pathname component was the same. opi and cpi now point
253 to the slash before it. */
258 if (rp > rel_installdir)
259 /* Unexpected: The curr_installdir does not end with rel_installdir. */
263 size_t curr_prefix_len = cp - curr_installdir;
266 curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
268 if (curr_prefix == NULL)
271 memcpy (curr_prefix, curr_installdir, curr_prefix_len);
272 curr_prefix[curr_prefix_len] = '\0';
279 #endif /* !IN_LIBRARY || PIC */
281 #if defined PIC && defined INSTALLDIR
283 /* Full pathname of shared library, or NULL. */
284 static char *shared_library_fullname;
286 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
288 /* Determine the full pathname of the shared library when it is loaded. */
291 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
295 if (event == DLL_PROCESS_ATTACH)
297 /* The DLL is being loaded into an application's address range. */
298 static char location[MAX_PATH];
300 if (!GetModuleFileName (module_handle, location, sizeof (location)))
301 /* Shouldn't happen. */
304 if (!IS_PATH_WITH_DIR (location))
305 /* Shouldn't happen. */
309 #if defined __CYGWIN__
310 /* On Cygwin, we need to convert paths coming from Win32 system calls
311 to the Unix-like slashified notation. */
312 static char location_as_posix_path[2 * MAX_PATH];
313 /* There's no error return defined for cygwin_conv_to_posix_path.
314 See cygwin-api/func-cygwin-conv-to-posix-path.html.
315 Does it overflow the buffer of expected size MAX_PATH or does it
316 truncate the path? I don't know. Let's catch both. */
317 cygwin_conv_to_posix_path (location, location_as_posix_path);
318 location_as_posix_path[MAX_PATH - 1] = '\0';
319 if (strlen (location_as_posix_path) >= MAX_PATH - 1)
320 /* A sign of buffer overflow or path truncation. */
322 shared_library_fullname = strdup (location_as_posix_path);
324 shared_library_fullname = strdup (location);
332 #else /* Unix except Cygwin */
335 find_shared_library_fullname ()
337 #if defined __linux__ && __GLIBC__ >= 2
338 /* Linux has /proc/self/maps. glibc 2 has the getline() function. */
341 /* Open the current process' maps file. It describes one VMA per line. */
342 fp = fopen ("/proc/self/maps", "r");
345 unsigned long address = (unsigned long) &find_shared_library_fullname;
348 unsigned long start, end;
351 if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
353 if (address >= start && address <= end - 1)
355 /* Found it. Now see if this line contains a filename. */
356 while (c = getc (fp), c != EOF && c != '\n' && c != '/')
364 shared_library_fullname = NULL; size = 0;
365 len = getline (&shared_library_fullname, &size, fp);
368 /* Success: filled shared_library_fullname. */
369 if (len > 0 && shared_library_fullname[len - 1] == '\n')
370 shared_library_fullname[len - 1] = '\0';
375 while (c = getc (fp), c != EOF && c != '\n')
383 #endif /* (WIN32 or Cygwin) / (Unix except Cygwin) */
385 /* Return the full pathname of the current shared library.
386 Return NULL if unknown.
387 Guaranteed to work only on Linux, Cygwin and Woe32. */
389 get_shared_library_fullname ()
391 #if !(defined _WIN32 || defined __WIN32__ || defined __CYGWIN__)
392 static bool tried_find_shared_library_fullname;
393 if (!tried_find_shared_library_fullname)
395 find_shared_library_fullname ();
396 tried_find_shared_library_fullname = true;
399 return shared_library_fullname;
404 /* Returns the pathname, relocated according to the current installation
407 relocate (const char *pathname)
409 #if defined PIC && defined INSTALLDIR
410 static int initialized;
412 /* Initialization code for a shared library. */
415 /* At this point, orig_prefix and curr_prefix likely have already been
416 set through the main program's set_program_name_and_installdir
417 function. This is sufficient in the case that the library has
418 initially been installed in the same orig_prefix. But we can do
419 better, to also cover the cases that 1. it has been installed
420 in a different prefix before being moved to orig_prefix and (later)
421 to curr_prefix, 2. unlike the program, it has not moved away from
423 const char *orig_installprefix = INSTALLPREFIX;
424 const char *orig_installdir = INSTALLDIR;
425 const char *curr_prefix_better;
428 compute_curr_prefix (orig_installprefix, orig_installdir,
429 get_shared_library_fullname ());
430 if (curr_prefix_better == NULL)
431 curr_prefix_better = curr_prefix;
433 set_relocation_prefix (orig_installprefix, curr_prefix_better);
439 /* Note: It is not necessary to perform case insensitive comparison here,
440 even for DOS-like filesystems, because the pathname argument was
441 typically created from the same Makefile variable as orig_prefix came
443 if (orig_prefix != NULL && curr_prefix != NULL
444 && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
446 if (pathname[orig_prefix_len] == '\0')
447 /* pathname equals orig_prefix. */
449 if (ISSLASH (pathname[orig_prefix_len]))
451 /* pathname starts with orig_prefix. */
452 const char *pathname_tail = &pathname[orig_prefix_len];
454 (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
460 memcpy (result, curr_prefix, curr_prefix_len);
461 strcpy (result + curr_prefix_len, pathname_tail);
466 /* Nothing to relocate. */