1 /* Provide relocatable packages.
2 Copyright (C) 2003 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 DEPENDS_ON_LIBCHARSET
49 # include <libcharset.h>
51 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
54 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
58 /* Faked cheap 'bool'. */
67 ISSLASH(C) tests whether C is a directory separator character.
68 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
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')) \
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)
81 # define ISSLASH(C) ((C) == '/')
82 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
83 # define FILESYSTEM_PREFIX_LEN(P) 0
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. */
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 ""
101 set_this_relocation_prefix (const char *orig_prefix_arg,
102 const char *curr_prefix_arg)
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)
109 /* Duplicate the argument strings. */
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);
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;
129 /* Don't worry about wasted memory here - this function is usually only
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 ""
139 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
141 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
143 /* Now notify all dependent libraries. */
144 #if DEPENDS_ON_LIBCHARSET
145 libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
147 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
148 libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
150 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
151 libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
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. */
160 #define compute_curr_prefix local_compute_curr_prefix
164 compute_curr_prefix (const char *orig_installprefix,
165 const char *orig_installdir,
166 const char *curr_pathname)
168 const char *curr_installdir;
169 const char *rel_installdir;
171 if (curr_pathname == NULL)
174 /* Determine the relative installation directory, relative to the prefix.
175 This is simply the difference between orig_installprefix and
177 if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
179 /* Shouldn't happen - nothing should be installed outside $(prefix). */
181 rel_installdir = orig_installdir + strlen (orig_installprefix);
183 /* Determine the current installation directory. */
185 const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
186 const char *p = curr_pathname + strlen (curr_pathname);
196 q = (char *) xmalloc (p - curr_pathname + 1);
201 memcpy (q, curr_pathname, p - curr_pathname);
202 q[p - curr_pathname] = '\0';
206 /* Compute the current installation prefix by removing the trailing
207 rel_installdir from it. */
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);
214 while (rp > rel_installdir && cp > cp_base)
217 const char *rpi = rp;
218 const char *cpi = cp;
220 while (rpi > rel_installdir && cpi > cp_base)
224 if (ISSLASH (*rpi) || ISSLASH (*cpi))
226 if (ISSLASH (*rpi) && ISSLASH (*cpi))
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))
242 /* The last pathname component was the same. opi and cpi now point
243 to the slash before it. */
248 if (rp > rel_installdir)
249 /* Unexpected: The curr_installdir does not end with rel_installdir. */
253 size_t curr_prefix_len = cp - curr_installdir;
256 curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
258 if (curr_prefix == NULL)
261 memcpy (curr_prefix, curr_installdir, curr_prefix_len);
262 curr_prefix[curr_prefix_len] = '\0';
269 #if defined PIC && defined INSTALLDIR
271 /* Full pathname of shared library, or NULL. */
272 static char *shared_library_fullname;
274 #if defined _WIN32 || defined __WIN32__
276 /* Determine the full pathname of the shared library when it is loaded. */
279 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
283 if (event == DLL_PROCESS_ATTACH)
285 /* The DLL is being loaded into an application's address range. */
286 static char location[MAX_PATH];
288 if (!GetModuleFileName (module_handle, location, sizeof (location)))
289 /* Shouldn't happen. */
292 if (!IS_PATH_WITH_DIR (location))
293 /* Shouldn't happen. */
296 shared_library_fullname = strdup (location);
305 find_shared_library_fullname ()
310 /* Open the current process' maps file. It describes one VMA per line. */
311 fp = fopen ("/proc/self/maps", "r");
314 unsigned long address = (unsigned long) &find_shared_library_fullname;
317 unsigned long start, end;
320 if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
322 if (address >= start && address <= end - 1)
324 /* Found it. Now see if this line contains a filename. */
325 while (c = getc (fp), c != EOF && c != '\n' && c != '/')
333 shared_library_fullname = NULL; size = 0;
334 len = getline (&shared_library_fullname, &size, fp);
337 /* Success: filled shared_library_fullname. */
338 if (len > 0 && shared_library_fullname[len - 1] == '\n')
339 shared_library_fullname[len - 1] = '\0';
344 while (c = getc (fp), c != EOF && c != '\n')
352 #endif /* WIN32 / Unix */
354 /* Return the full pathname of the current shared library.
355 Return NULL if unknown.
356 Guaranteed to work only on Linux and Woe32. */
358 get_shared_library_fullname ()
360 #if !(defined _WIN32 || defined __WIN32__)
361 static bool tried_find_shared_library_fullname;
362 if (!tried_find_shared_library_fullname)
364 find_shared_library_fullname ();
365 tried_find_shared_library_fullname = true;
368 return shared_library_fullname;
373 /* Returns the pathname, relocated according to the current installation
376 relocate (const char *pathname)
378 #if defined PIC && defined INSTALLDIR
379 static int initialized;
381 /* Initialization code for a shared library. */
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
392 const char *orig_installprefix = INSTALLPREFIX;
393 const char *orig_installdir = INSTALLDIR;
394 const char *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;
402 set_relocation_prefix (orig_installprefix, curr_prefix_better);
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
412 if (orig_prefix != NULL && curr_prefix != NULL
413 && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
415 if (pathname[orig_prefix_len] == '\0')
416 /* pathname equals orig_prefix. */
418 if (ISSLASH (pathname[orig_prefix_len]))
420 /* pathname starts with orig_prefix. */
421 const char *pathname_tail = &pathname[orig_prefix_len];
423 (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
429 memcpy (result, curr_prefix, curr_prefix_len);
430 strcpy (result + curr_prefix_len, pathname_tail);
435 /* Nothing to relocate. */