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
|
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
|
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 *
|
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
|
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
|
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 *
|
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 *
|
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
|