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 "xalloc.h"
46 #endif
47
48 #if defined _WIN32 || defined __WIN32__
49 # define WIN32_LEAN_AND_MEAN
50 # include <windows.h>
51 #endif
52
53 #if DEPENDS_ON_LIBCHARSET
54 # include <libcharset.h>
55 #endif
56 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
57 # include <iconv.h>
58 #endif
59 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
60 # include <libintl.h>
61 #endif
62
63 /* Faked cheap 'bool'. */
64 #undef bool
65 #undef false
66 #undef true
67 #define bool int
68 #define false 0
69 #define true 1
70
71 /* Pathname support.
72 ISSLASH(C) tests whether C is a directory separator character.
73 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
74 */
75 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
76 /* Win32, 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')) \
80 && (P)[1] == ':')
81 # define IS_PATH_WITH_DIR(P) \
82 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
83 # define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
84 #else
85 /* Unix */
86 # define ISSLASH(C) ((C) == '/')
87 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
88 # define FILESYSTEM_PREFIX_LEN(P) 0
89 #endif
90
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. */
99
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 ""
104 instead of "/"). */
105 static void
set_this_relocation_prefix(const char * orig_prefix_arg,const char * curr_prefix_arg)106 set_this_relocation_prefix (const char *orig_prefix_arg,
107 const char *curr_prefix_arg)
108 {
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)
113 {
114 /* Duplicate the argument strings. */
115 char *memory;
116
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);
120 #ifdef NO_XMALLOC
121 if (memory != NULL)
122 #endif
123 {
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;
129 return;
130 }
131 }
132 orig_prefix = NULL;
133 curr_prefix = NULL;
134 /* Don't worry about wasted memory here - this function is usually only
135 called once. */
136 }
137
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 ""
142 instead of "/"). */
143 void
set_relocation_prefix(const char * orig_prefix_arg,const char * curr_prefix_arg)144 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
145 {
146 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
147
148 /* Now notify all dependent libraries. */
149 #if DEPENDS_ON_LIBCHARSET
150 libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
151 #endif
152 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
153 libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
154 #endif
155 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
156 libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
157 #endif
158 }
159
160 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR)
161
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. */
166 #ifdef IN_LIBRARY
167 #define compute_curr_prefix local_compute_curr_prefix
168 static
169 #endif
170 const char *
compute_curr_prefix(const char * orig_installprefix,const char * orig_installdir,const char * curr_pathname)171 compute_curr_prefix (const char *orig_installprefix,
172 const char *orig_installdir,
173 const char *curr_pathname)
174 {
175 const char *curr_installdir;
176 const char *rel_installdir;
177
178 if (curr_pathname == NULL)
179 return NULL;
180
181 /* Determine the relative installation directory, relative to the prefix.
182 This is simply the difference between orig_installprefix and
183 orig_installdir. */
184 if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
185 != 0)
186 /* Shouldn't happen - nothing should be installed outside $(prefix). */
187 return NULL;
188 rel_installdir = orig_installdir + strlen (orig_installprefix);
189
190 /* Determine the current installation directory. */
191 {
192 const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
193 const char *p = curr_pathname + strlen (curr_pathname);
194 char *q;
195
196 while (p > p_base)
197 {
198 p--;
199 if (ISSLASH (*p))
200 break;
201 }
202
203 q = (char *) xmalloc (p - curr_pathname + 1);
204 #ifdef NO_XMALLOC
205 if (q == NULL)
206 return NULL;
207 #endif
208 memcpy (q, curr_pathname, p - curr_pathname);
209 q[p - curr_pathname] = '\0';
210 curr_installdir = q;
211 }
212
213 /* Compute the current installation prefix by removing the trailing
214 rel_installdir from it. */
215 {
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 + FILESYSTEM_PREFIX_LEN (curr_installdir);
220
221 while (rp > rel_installdir && cp > cp_base)
222 {
223 bool same = false;
224 const char *rpi = rp;
225 const char *cpi = cp;
226
227 while (rpi > rel_installdir && cpi > cp_base)
228 {
229 rpi--;
230 cpi--;
231 if (ISSLASH (*rpi) || ISSLASH (*cpi))
232 {
233 if (ISSLASH (*rpi) && ISSLASH (*cpi))
234 same = true;
235 break;
236 }
237 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
238 /* Win32, OS/2, DOS - case insignificant filesystem */
239 if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
240 != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
241 break;
242 #else
243 if (*rpi != *cpi)
244 break;
245 #endif
246 }
247 if (!same)
248 break;
249 /* The last pathname component was the same. opi and cpi now point
250 to the slash before it. */
251 rp = rpi;
252 cp = cpi;
253 }
254
255 if (rp > rel_installdir)
256 /* Unexpected: The curr_installdir does not end with rel_installdir. */
257 return NULL;
258
259 {
260 size_t curr_prefix_len = cp - curr_installdir;
261 char *curr_prefix;
262
263 curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
264 #ifdef NO_XMALLOC
265 if (curr_prefix == NULL)
266 return NULL;
267 #endif
268 memcpy (curr_prefix, curr_installdir, curr_prefix_len);
269 curr_prefix[curr_prefix_len] = '\0';
270
271 return curr_prefix;
272 }
273 }
274 }
275
276 #endif /* !IN_LIBRARY || PIC */
277
278 #if defined PIC && defined INSTALLDIR
279
280 /* Full pathname of shared library, or NULL. */
281 static char *shared_library_fullname;
282
283 #if defined _WIN32 || defined __WIN32__
284
285 /* Determine the full pathname of the shared library when it is loaded. */
286
287 BOOL WINAPI
DllMain(HINSTANCE module_handle,DWORD event,LPVOID reserved)288 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
289 {
290 (void) reserved;
291
292 if (event == DLL_PROCESS_ATTACH)
293 {
294 /* The DLL is being loaded into an application's address range. */
295 static char location[MAX_PATH];
296
297 if (!GetModuleFileName (module_handle, location, sizeof (location)))
298 /* Shouldn't happen. */
299 return FALSE;
300
301 if (!IS_PATH_WITH_DIR (location))
302 /* Shouldn't happen. */
303 return FALSE;
304
305 shared_library_fullname = strdup (location);
306 }
307
308 return TRUE;
309 }
310
311 #else /* Unix */
312
313 static void
find_shared_library_fullname()314 find_shared_library_fullname ()
315 {
316 #if defined __linux__ && __GLIBC__ >= 2
317 /* Linux has /proc/self/maps. glibc 2 has the getline() function. */
318 FILE *fp;
319
320 /* Open the current process' maps file. It describes one VMA per line. */
321 fp = fopen ("/proc/self/maps", "r");
322 if (fp)
323 {
324 unsigned long address = (unsigned long) &find_shared_library_fullname;
325 for (;;)
326 {
327 unsigned long start, end;
328 int c;
329
330 if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
331 break;
332 if (address >= start && address <= end - 1)
333 {
334 /* Found it. Now see if this line contains a filename. */
335 while (c = getc (fp), c != EOF && c != '\n' && c != '/')
336 continue;
337 if (c == '/')
338 {
339 size_t size;
340 int len;
341
342 ungetc (c, fp);
343 shared_library_fullname = NULL; size = 0;
344 len = getline (&shared_library_fullname, &size, fp);
345 if (len >= 0)
346 {
347 /* Success: filled shared_library_fullname. */
348 if (len > 0 && shared_library_fullname[len - 1] == '\n')
349 shared_library_fullname[len - 1] = '\0';
350 }
351 }
352 break;
353 }
354 while (c = getc (fp), c != EOF && c != '\n')
355 continue;
356 }
357 fclose (fp);
358 }
359 #endif
360 }
361
362 #endif /* WIN32 / Unix */
363
364 /* Return the full pathname of the current shared library.
365 Return NULL if unknown.
366 Guaranteed to work only on Linux and Woe32. */
367 static char *
get_shared_library_fullname()368 get_shared_library_fullname ()
369 {
370 #if !(defined _WIN32 || defined __WIN32__)
371 static bool tried_find_shared_library_fullname;
372 if (!tried_find_shared_library_fullname)
373 {
374 find_shared_library_fullname ();
375 tried_find_shared_library_fullname = true;
376 }
377 #endif
378 return shared_library_fullname;
379 }
380
381 #endif /* PIC */
382
383 /* Returns the pathname, relocated according to the current installation
384 directory. */
385 const char *
relocate(const char * pathname)386 relocate (const char *pathname)
387 {
388 #if defined PIC && defined INSTALLDIR
389 static int initialized;
390
391 /* Initialization code for a shared library. */
392 if (!initialized)
393 {
394 /* At this point, orig_prefix and curr_prefix likely have already been
395 set through the main program's set_program_name_and_installdir
396 function. This is sufficient in the case that the library has
397 initially been installed in the same orig_prefix. But we can do
398 better, to also cover the cases that 1. it has been installed
399 in a different prefix before being moved to orig_prefix and (later)
400 to curr_prefix, 2. unlike the program, it has not moved away from
401 orig_prefix. */
402 const char *orig_installprefix = INSTALLPREFIX;
403 const char *orig_installdir = INSTALLDIR;
404 const char *curr_prefix_better;
405
406 curr_prefix_better =
407 compute_curr_prefix (orig_installprefix, orig_installdir,
408 get_shared_library_fullname ());
409 if (curr_prefix_better == NULL)
410 curr_prefix_better = curr_prefix;
411
412 set_relocation_prefix (orig_installprefix, curr_prefix_better);
413
414 initialized = 1;
415 }
416 #endif
417
418 /* Note: It is not necessary to perform case insensitive comparison here,
419 even for DOS-like filesystems, because the pathname argument was
420 typically created from the same Makefile variable as orig_prefix came
421 from. */
422 if (orig_prefix != NULL && curr_prefix != NULL
423 && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
424 {
425 if (pathname[orig_prefix_len] == '\0')
426 /* pathname equals orig_prefix. */
427 return curr_prefix;
428 if (ISSLASH (pathname[orig_prefix_len]))
429 {
430 /* pathname starts with orig_prefix. */
431 const char *pathname_tail = &pathname[orig_prefix_len];
432 char *result =
433 (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
434
435 #ifdef NO_XMALLOC
436 if (result != NULL)
437 #endif
438 {
439 memcpy (result, curr_prefix, curr_prefix_len);
440 strcpy (result + curr_prefix_len, pathname_tail);
441 return result;
442 }
443 }
444 }
445 /* Nothing to relocate. */
446 return pathname;
447 }
448
449 #endif
450