1 /* Provide relocatable packages.
2 Copyright (C) 2003-2006, 2008-2011 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
31 #include "relocatable.h"
33 #if ENABLE_RELOCATABLE
41 # define xmalloc malloc
46 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
47 # define WIN32_LEAN_AND_MEAN
51 #if DEPENDS_ON_LIBCHARSET
52 # include <libcharset.h>
54 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
57 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
61 /* Faked cheap 'bool'. */
70 ISSLASH(C) tests whether C is a directory separator character.
71 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
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')) \
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)
84 # define ISSLASH(C) ((C) == '/')
85 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
86 # define FILE_SYSTEM_PREFIX_LEN(P) 0
89 /* Use the system functions, not the gnulib overrides in this file. */
92 /* Original installation prefix. */
93 static char *orig_prefix;
94 static size_t orig_prefix_len;
95 /* Current installation prefix. */
96 static char *curr_prefix;
97 static size_t curr_prefix_len;
98 /* These prefixes do not end in a slash. Anything that will be concatenated
99 to them must start with a slash. */
101 /* Sets the original and the current installation prefix of this module.
102 Relocation simply replaces a pathname starting with the original prefix
103 by the corresponding pathname with the current prefix instead. Both
104 prefixes should be directory names without trailing slash (i.e. use ""
107 set_this_relocation_prefix (const char *orig_prefix_arg,
108 const char *curr_prefix_arg)
110 if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
111 /* Optimization: if orig_prefix and curr_prefix are equal, the
112 relocation is a nop. */
113 && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
115 /* Duplicate the argument strings. */
118 orig_prefix_len = strlen (orig_prefix_arg);
119 curr_prefix_len = strlen (curr_prefix_arg);
120 memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
125 memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
126 orig_prefix = memory;
127 memory += orig_prefix_len + 1;
128 memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
129 curr_prefix = memory;
135 /* Don't worry about wasted memory here - this function is usually only
139 /* Sets the original and the current installation prefix of the package.
140 Relocation simply replaces a pathname starting with the original prefix
141 by the corresponding pathname with the current prefix instead. Both
142 prefixes should be directory names without trailing slash (i.e. use ""
145 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
147 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
149 /* Now notify all dependent libraries. */
150 #if DEPENDS_ON_LIBCHARSET
151 libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
153 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
154 libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
156 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
157 libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
161 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR)
163 /* Convenience function:
164 Computes the current installation prefix, based on the original
165 installation prefix, the original installation directory of a particular
166 file, and the current pathname of this file.
167 Returns it, freshly allocated. Returns NULL upon failure. */
169 #define compute_curr_prefix local_compute_curr_prefix
173 compute_curr_prefix (const char *orig_installprefix,
174 const char *orig_installdir,
175 const char *curr_pathname)
177 char *curr_installdir;
178 const char *rel_installdir;
180 if (curr_pathname == NULL)
183 /* Determine the relative installation directory, relative to the prefix.
184 This is simply the difference between orig_installprefix and
186 if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
188 /* Shouldn't happen - nothing should be installed outside $(prefix). */
190 rel_installdir = orig_installdir + strlen (orig_installprefix);
192 /* Determine the current installation directory. */
194 const char *p_base = curr_pathname + FILE_SYSTEM_PREFIX_LEN (curr_pathname);
195 const char *p = curr_pathname + strlen (curr_pathname);
205 q = (char *) xmalloc (p - curr_pathname + 1);
210 memcpy (q, curr_pathname, p - curr_pathname);
211 q[p - curr_pathname] = '\0';
215 /* Compute the current installation prefix by removing the trailing
216 rel_installdir from it. */
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 + FILE_SYSTEM_PREFIX_LEN (curr_installdir);
223 while (rp > rel_installdir && cp > cp_base)
226 const char *rpi = rp;
227 const char *cpi = cp;
229 while (rpi > rel_installdir && cpi > cp_base)
233 if (ISSLASH (*rpi) || ISSLASH (*cpi))
235 if (ISSLASH (*rpi) && ISSLASH (*cpi))
239 /* Do case-insensitive comparison if the file system is always or
240 often case-insensitive. It's better to accept the comparison
241 if the difference is only in case, rather than to fail. */
242 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
243 /* Win32, Cygwin, OS/2, DOS - case insignificant file system */
244 if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
245 != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
254 /* The last pathname component was the same. opi and cpi now point
255 to the slash before it. */
260 if (rp > rel_installdir)
262 /* Unexpected: The curr_installdir does not end with rel_installdir. */
263 free (curr_installdir);
268 size_t curr_prefix_len = cp - curr_installdir;
271 curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
273 if (curr_prefix == NULL)
275 free (curr_installdir);
279 memcpy (curr_prefix, curr_installdir, curr_prefix_len);
280 curr_prefix[curr_prefix_len] = '\0';
282 free (curr_installdir);
289 #endif /* !IN_LIBRARY || PIC */
291 #if defined PIC && defined INSTALLDIR
293 /* Full pathname of shared library, or NULL. */
294 static char *shared_library_fullname;
296 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
298 /* Determine the full pathname of the shared library when it is loaded. */
301 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
305 if (event == DLL_PROCESS_ATTACH)
307 /* The DLL is being loaded into an application's address range. */
308 static char location[MAX_PATH];
310 if (!GetModuleFileName (module_handle, location, sizeof (location)))
311 /* Shouldn't happen. */
314 if (!IS_PATH_WITH_DIR (location))
315 /* Shouldn't happen. */
319 #if defined __CYGWIN__
320 /* On Cygwin, we need to convert paths coming from Win32 system calls
321 to the Unix-like slashified notation. */
322 static char location_as_posix_path[2 * MAX_PATH];
323 /* There's no error return defined for cygwin_conv_to_posix_path.
324 See cygwin-api/func-cygwin-conv-to-posix-path.html.
325 Does it overflow the buffer of expected size MAX_PATH or does it
326 truncate the path? I don't know. Let's catch both. */
327 cygwin_conv_to_posix_path (location, location_as_posix_path);
328 location_as_posix_path[MAX_PATH - 1] = '\0';
329 if (strlen (location_as_posix_path) >= MAX_PATH - 1)
330 /* A sign of buffer overflow or path truncation. */
332 shared_library_fullname = strdup (location_as_posix_path);
334 shared_library_fullname = strdup (location);
342 #else /* Unix except Cygwin */
345 find_shared_library_fullname ()
347 #if defined __linux__ && (__GLIBC__ >= 2 || defined __UCLIBC__)
348 /* Linux has /proc/self/maps. glibc 2 and uClibc have the getline()
352 /* Open the current process' maps file. It describes one VMA per line. */
353 fp = fopen ("/proc/self/maps", "r");
356 unsigned long address = (unsigned long) &find_shared_library_fullname;
359 unsigned long start, end;
362 if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
364 if (address >= start && address <= end - 1)
366 /* Found it. Now see if this line contains a filename. */
367 while (c = getc (fp), c != EOF && c != '\n' && c != '/')
375 shared_library_fullname = NULL; size = 0;
376 len = getline (&shared_library_fullname, &size, fp);
379 /* Success: filled shared_library_fullname. */
380 if (len > 0 && shared_library_fullname[len - 1] == '\n')
381 shared_library_fullname[len - 1] = '\0';
386 while (c = getc (fp), c != EOF && c != '\n')
394 #endif /* (WIN32 or Cygwin) / (Unix except Cygwin) */
396 /* Return the full pathname of the current shared library.
397 Return NULL if unknown.
398 Guaranteed to work only on Linux, Cygwin and Woe32. */
400 get_shared_library_fullname ()
402 #if !(defined _WIN32 || defined __WIN32__ || defined __CYGWIN__)
403 static bool tried_find_shared_library_fullname;
404 if (!tried_find_shared_library_fullname)
406 find_shared_library_fullname ();
407 tried_find_shared_library_fullname = true;
410 return shared_library_fullname;
415 /* Returns the pathname, relocated according to the current installation
417 The returned string is either PATHNAME unmodified or a freshly allocated
418 string that you can free with free() after casting it to 'char *'. */
420 relocate (const char *pathname)
422 #if defined PIC && defined INSTALLDIR
423 static int initialized;
425 /* Initialization code for a shared library. */
428 /* At this point, orig_prefix and curr_prefix likely have already been
429 set through the main program's set_program_name_and_installdir
430 function. This is sufficient in the case that the library has
431 initially been installed in the same orig_prefix. But we can do
432 better, to also cover the cases that 1. it has been installed
433 in a different prefix before being moved to orig_prefix and (later)
434 to curr_prefix, 2. unlike the program, it has not moved away from
436 const char *orig_installprefix = INSTALLPREFIX;
437 const char *orig_installdir = INSTALLDIR;
438 char *curr_prefix_better;
441 compute_curr_prefix (orig_installprefix, orig_installdir,
442 get_shared_library_fullname ());
444 set_relocation_prefix (orig_installprefix,
445 curr_prefix_better != NULL
449 if (curr_prefix_better != NULL)
450 free (curr_prefix_better);
456 /* Note: It is not necessary to perform case insensitive comparison here,
457 even for DOS-like file systems, because the pathname argument was
458 typically created from the same Makefile variable as orig_prefix came
460 if (orig_prefix != NULL && curr_prefix != NULL
461 && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
463 if (pathname[orig_prefix_len] == '\0')
465 /* pathname equals orig_prefix. */
466 char *result = (char *) xmalloc (strlen (curr_prefix) + 1);
472 strcpy (result, curr_prefix);
476 else if (ISSLASH (pathname[orig_prefix_len]))
478 /* pathname starts with orig_prefix. */
479 const char *pathname_tail = &pathname[orig_prefix_len];
481 (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
487 memcpy (result, curr_prefix, curr_prefix_len);
488 strcpy (result + curr_prefix_len, pathname_tail);
493 /* Nothing to relocate. */