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