0
|
1 /* Provide relocatable packages.
|
|
2 Copyright (C) 2003 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 Library 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 Library General Public
|
|
16 License along with this program; if not, write to the Free Software
|
|
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
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 #ifdef HAVE_CONFIG_H
|
|
29 # include "config.h"
|
|
30 #endif
|
|
31
|
|
32 /* Specification. */
|
|
33 #include "relocatable.h"
|
|
34
|
|
35 #if ENABLE_RELOCATABLE
|
|
36
|
|
37 #include <stddef.h>
|
|
38 #include <stdio.h>
|
|
39 #include <stdlib.h>
|
|
40 #include <string.h>
|
|
41
|
|
42 #ifdef NO_XMALLOC
|
|
43 # define xmalloc malloc
|
|
44 #else
|
|
45 # include "xmalloc.h"
|
|
46 #endif
|
|
47
|
|
48 #if DEPENDS_ON_LIBCHARSET
|
|
49 # include <libcharset.h>
|
|
50 #endif
|
|
51 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
|
|
52 # include <iconv.h>
|
|
53 #endif
|
|
54 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
|
|
55 # include <libintl.h>
|
|
56 #endif
|
|
57
|
|
58 /* Faked cheap 'bool'. */
|
|
59 #undef bool
|
|
60 #undef false
|
|
61 #undef true
|
|
62 #define bool int
|
|
63 #define false 0
|
|
64 #define true 1
|
|
65
|
|
66 /* Pathname support.
|
|
67 ISSLASH(C) tests whether C is a directory separator character.
|
|
68 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
|
|
69 */
|
|
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')) \
|
|
75 && (P)[1] == ':')
|
|
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)
|
|
79 #else
|
|
80 /* Unix */
|
|
81 # define ISSLASH(C) ((C) == '/')
|
|
82 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
|
|
83 # define FILESYSTEM_PREFIX_LEN(P) 0
|
|
84 #endif
|
|
85
|
|
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. */
|
|
94
|
|
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 ""
|
|
99 instead of "/"). */
|
|
100 static void
|
|
101 set_this_relocation_prefix (const char *orig_prefix_arg,
|
|
102 const char *curr_prefix_arg)
|
|
103 {
|
|
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)
|
|
108 {
|
|
109 /* Duplicate the argument strings. */
|
|
110 char *memory;
|
|
111
|
|
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);
|
|
115 #ifdef NO_XMALLOC
|
|
116 if (memory != NULL)
|
|
117 #endif
|
|
118 {
|
|
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;
|
|
124 return;
|
|
125 }
|
|
126 }
|
|
127 orig_prefix = NULL;
|
|
128 curr_prefix = NULL;
|
|
129 /* Don't worry about wasted memory here - this function is usually only
|
|
130 called once. */
|
|
131 }
|
|
132
|
|
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 ""
|
|
137 instead of "/"). */
|
|
138 void
|
|
139 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
|
|
140 {
|
|
141 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
|
|
142
|
|
143 /* Now notify all dependent libraries. */
|
|
144 #if DEPENDS_ON_LIBCHARSET
|
|
145 libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
|
|
146 #endif
|
|
147 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
|
|
148 libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
|
|
149 #endif
|
|
150 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
|
|
151 libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
|
|
152 #endif
|
|
153 }
|
|
154
|
|
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. */
|
|
159 #ifdef IN_LIBRARY
|
|
160 #define compute_curr_prefix local_compute_curr_prefix
|
|
161 static
|
|
162 #endif
|
|
163 const char *
|
|
164 compute_curr_prefix (const char *orig_installprefix,
|
|
165 const char *orig_installdir,
|
|
166 const char *curr_pathname)
|
|
167 {
|
|
168 const char *curr_installdir;
|
|
169 const char *rel_installdir;
|
|
170
|
|
171 if (curr_pathname == NULL)
|
|
172 return NULL;
|
|
173
|
|
174 /* Determine the relative installation directory, relative to the prefix.
|
|
175 This is simply the difference between orig_installprefix and
|
|
176 orig_installdir. */
|
|
177 if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
|
|
178 != 0)
|
|
179 /* Shouldn't happen - nothing should be installed outside $(prefix). */
|
|
180 return NULL;
|
|
181 rel_installdir = orig_installdir + strlen (orig_installprefix);
|
|
182
|
|
183 /* Determine the current installation directory. */
|
|
184 {
|
|
185 const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
|
|
186 const char *p = curr_pathname + strlen (curr_pathname);
|
|
187 char *q;
|
|
188
|
|
189 while (p > p_base)
|
|
190 {
|
|
191 p--;
|
|
192 if (ISSLASH (*p))
|
|
193 break;
|
|
194 }
|
|
195
|
|
196 q = (char *) xmalloc (p - curr_pathname + 1);
|
|
197 #ifdef NO_XMALLOC
|
|
198 if (q == NULL)
|
|
199 return NULL;
|
|
200 #endif
|
|
201 memcpy (q, curr_pathname, p - curr_pathname);
|
|
202 q[p - curr_pathname] = '\0';
|
|
203 curr_installdir = q;
|
|
204 }
|
|
205
|
|
206 /* Compute the current installation prefix by removing the trailing
|
|
207 rel_installdir from it. */
|
|
208 {
|
|
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);
|
|
213
|
|
214 while (rp > rel_installdir && cp > cp_base)
|
|
215 {
|
|
216 bool same = false;
|
|
217 const char *rpi = rp;
|
|
218 const char *cpi = cp;
|
|
219
|
|
220 while (rpi > rel_installdir && cpi > cp_base)
|
|
221 {
|
|
222 rpi--;
|
|
223 cpi--;
|
|
224 if (ISSLASH (*rpi) || ISSLASH (*cpi))
|
|
225 {
|
|
226 if (ISSLASH (*rpi) && ISSLASH (*cpi))
|
|
227 same = true;
|
|
228 break;
|
|
229 }
|
|
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))
|
|
234 break;
|
|
235 #else
|
|
236 if (*rpi != *cpi)
|
|
237 break;
|
|
238 #endif
|
|
239 }
|
|
240 if (!same)
|
|
241 break;
|
|
242 /* The last pathname component was the same. opi and cpi now point
|
|
243 to the slash before it. */
|
|
244 rp = rpi;
|
|
245 cp = cpi;
|
|
246 }
|
|
247
|
|
248 if (rp > rel_installdir)
|
|
249 /* Unexpected: The curr_installdir does not end with rel_installdir. */
|
|
250 return NULL;
|
|
251
|
|
252 {
|
|
253 size_t curr_prefix_len = cp - curr_installdir;
|
|
254 char *curr_prefix;
|
|
255
|
|
256 curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
|
|
257 #ifdef NO_XMALLOC
|
|
258 if (curr_prefix == NULL)
|
|
259 return NULL;
|
|
260 #endif
|
|
261 memcpy (curr_prefix, curr_installdir, curr_prefix_len);
|
|
262 curr_prefix[curr_prefix_len] = '\0';
|
|
263
|
|
264 return curr_prefix;
|
|
265 }
|
|
266 }
|
|
267 }
|
|
268
|
|
269 #if defined PIC && defined INSTALLDIR
|
|
270
|
|
271 /* Full pathname of shared library, or NULL. */
|
|
272 static char *shared_library_fullname;
|
|
273
|
|
274 #if defined _WIN32 || defined __WIN32__
|
|
275
|
|
276 /* Determine the full pathname of the shared library when it is loaded. */
|
|
277
|
|
278 BOOL WINAPI
|
|
279 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
|
|
280 {
|
|
281 (void) reserved;
|
|
282
|
|
283 if (event == DLL_PROCESS_ATTACH)
|
|
284 {
|
|
285 /* The DLL is being loaded into an application's address range. */
|
|
286 static char location[MAX_PATH];
|
|
287
|
|
288 if (!GetModuleFileName (module_handle, location, sizeof (location)))
|
|
289 /* Shouldn't happen. */
|
|
290 return FALSE;
|
|
291
|
|
292 if (!IS_PATH_WITH_DIR (location))
|
|
293 /* Shouldn't happen. */
|
|
294 return FALSE;
|
|
295
|
|
296 shared_library_fullname = strdup (location);
|
|
297 }
|
|
298
|
|
299 return TRUE;
|
|
300 }
|
|
301
|
|
302 #else /* Unix */
|
|
303
|
|
304 static void
|
|
305 find_shared_library_fullname ()
|
|
306 {
|
|
307 #ifdef __linux__
|
|
308 FILE *fp;
|
|
309
|
|
310 /* Open the current process' maps file. It describes one VMA per line. */
|
|
311 fp = fopen ("/proc/self/maps", "r");
|
|
312 if (fp)
|
|
313 {
|
|
314 unsigned long address = (unsigned long) &find_shared_library_fullname;
|
|
315 for (;;)
|
|
316 {
|
|
317 unsigned long start, end;
|
|
318 int c;
|
|
319
|
|
320 if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
|
|
321 break;
|
|
322 if (address >= start && address <= end - 1)
|
|
323 {
|
|
324 /* Found it. Now see if this line contains a filename. */
|
|
325 while (c = getc (fp), c != EOF && c != '\n' && c != '/')
|
|
326 continue;
|
|
327 if (c == '/')
|
|
328 {
|
|
329 size_t size;
|
|
330 int len;
|
|
331
|
|
332 ungetc (c, fp);
|
|
333 shared_library_fullname = NULL; size = 0;
|
|
334 len = getline (&shared_library_fullname, &size, fp);
|
|
335 if (len >= 0)
|
|
336 {
|
|
337 /* Success: filled shared_library_fullname. */
|
|
338 if (len > 0 && shared_library_fullname[len - 1] == '\n')
|
|
339 shared_library_fullname[len - 1] = '\0';
|
|
340 }
|
|
341 }
|
|
342 break;
|
|
343 }
|
|
344 while (c = getc (fp), c != EOF && c != '\n')
|
|
345 continue;
|
|
346 }
|
|
347 fclose (fp);
|
|
348 }
|
|
349 #endif
|
|
350 }
|
|
351
|
|
352 #endif /* WIN32 / Unix */
|
|
353
|
|
354 /* Return the full pathname of the current shared library.
|
|
355 Return NULL if unknown.
|
|
356 Guaranteed to work only on Linux and Woe32. */
|
|
357 static char *
|
|
358 get_shared_library_fullname ()
|
|
359 {
|
|
360 #if !(defined _WIN32 || defined __WIN32__)
|
|
361 static bool tried_find_shared_library_fullname;
|
|
362 if (!tried_find_shared_library_fullname)
|
|
363 {
|
|
364 find_shared_library_fullname ();
|
|
365 tried_find_shared_library_fullname = true;
|
|
366 }
|
|
367 #endif
|
|
368 return shared_library_fullname;
|
|
369 }
|
|
370
|
|
371 #endif /* PIC */
|
|
372
|
|
373 /* Returns the pathname, relocated according to the current installation
|
|
374 directory. */
|
|
375 const char *
|
|
376 relocate (const char *pathname)
|
|
377 {
|
|
378 #if defined PIC && defined INSTALLDIR
|
|
379 static int initialized;
|
|
380
|
|
381 /* Initialization code for a shared library. */
|
|
382 if (!initialized)
|
|
383 {
|
|
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
|
|
391 orig_prefix. */
|
|
392 const char *orig_installprefix = INSTALLPREFIX;
|
|
393 const char *orig_installdir = INSTALLDIR;
|
|
394 const char *curr_prefix_better;
|
|
395
|
|
396 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;
|
|
401
|
|
402 set_relocation_prefix (orig_installprefix, curr_prefix_better);
|
|
403
|
|
404 initialized = 1;
|
|
405 }
|
|
406 #endif
|
|
407
|
|
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
|
|
411 from. */
|
|
412 if (orig_prefix != NULL && curr_prefix != NULL
|
|
413 && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
|
|
414 {
|
|
415 if (pathname[orig_prefix_len] == '\0')
|
|
416 /* pathname equals orig_prefix. */
|
|
417 return curr_prefix;
|
|
418 if (ISSLASH (pathname[orig_prefix_len]))
|
|
419 {
|
|
420 /* pathname starts with orig_prefix. */
|
|
421 const char *pathname_tail = &pathname[orig_prefix_len];
|
|
422 char *result =
|
|
423 (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
|
|
424
|
|
425 #ifdef NO_XMALLOC
|
|
426 if (result != NULL)
|
|
427 #endif
|
|
428 {
|
|
429 memcpy (result, curr_prefix, curr_prefix_len);
|
|
430 strcpy (result + curr_prefix_len, pathname_tail);
|
|
431 return result;
|
|
432 }
|
|
433 }
|
|
434 }
|
|
435 /* Nothing to relocate. */
|
|
436 return pathname;
|
|
437 }
|
|
438
|
|
439 #endif
|