1
/* Provide relocatable packages.
2
Copyright (C) 2003-2006 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
31
#include "relocatable.h"
33
#if ENABLE_RELOCATABLE
41
# define xmalloc malloc
46
#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
47
# define WIN32_LEAN_AND_MEAN
51
#if DEPENDS_ON_LIBCHARSET
52
# include <libcharset.h>
54
#if DEPENDS_ON_LIBICONV && HAVE_ICONV
57
#if DEPENDS_ON_LIBINTL && ENABLE_NLS
61
/* Faked cheap 'bool'. */
70
ISSLASH(C) tests whether C is a directory separator character.
71
IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
73
#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
74
/* Win32, Cygwin, OS/2, DOS */
75
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
76
# define HAS_DEVICE(P) \
77
((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
79
# define IS_PATH_WITH_DIR(P) \
80
(strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
81
# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
84
# define ISSLASH(C) ((C) == '/')
85
# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
86
# define FILE_SYSTEM_PREFIX_LEN(P) 0
89
/* Original installation prefix. */
90
static char *orig_prefix;
91
static size_t orig_prefix_len;
92
/* Current installation prefix. */
93
static char *curr_prefix;
94
static size_t curr_prefix_len;
95
/* These prefixes do not end in a slash. Anything that will be concatenated
96
to them must start with a slash. */
98
/* Sets the original and the current installation prefix of this module.
99
Relocation simply replaces a pathname starting with the original prefix
100
by the corresponding pathname with the current prefix instead. Both
101
prefixes should be directory names without trailing slash (i.e. use ""
104
set_this_relocation_prefix (const char *orig_prefix_arg,
105
const char *curr_prefix_arg)
107
if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
108
/* Optimization: if orig_prefix and curr_prefix are equal, the
109
relocation is a nop. */
110
&& strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
112
/* Duplicate the argument strings. */
115
orig_prefix_len = strlen (orig_prefix_arg);
116
curr_prefix_len = strlen (curr_prefix_arg);
117
memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
122
memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
123
orig_prefix = memory;
124
memory += orig_prefix_len + 1;
125
memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
126
curr_prefix = memory;
132
/* Don't worry about wasted memory here - this function is usually only
136
/* Sets the original and the current installation prefix of the package.
137
Relocation simply replaces a pathname starting with the original prefix
138
by the corresponding pathname with the current prefix instead. Both
139
prefixes should be directory names without trailing slash (i.e. use ""
142
set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
144
set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
146
/* Now notify all dependent libraries. */
147
#if DEPENDS_ON_LIBCHARSET
148
libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
150
#if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
151
libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
153
#if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
154
libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
158
#if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR)
160
/* Convenience function:
161
Computes the current installation prefix, based on the original
162
installation prefix, the original installation directory of a particular
163
file, and the current pathname of this file. Returns NULL upon failure. */
165
#define compute_curr_prefix local_compute_curr_prefix
169
compute_curr_prefix (const char *orig_installprefix,
170
const char *orig_installdir,
171
const char *curr_pathname)
173
const char *curr_installdir;
174
const char *rel_installdir;
176
if (curr_pathname == NULL)
179
/* Determine the relative installation directory, relative to the prefix.
180
This is simply the difference between orig_installprefix and
182
if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
184
/* Shouldn't happen - nothing should be installed outside $(prefix). */
186
rel_installdir = orig_installdir + strlen (orig_installprefix);
188
/* Determine the current installation directory. */
190
const char *p_base = curr_pathname + FILE_SYSTEM_PREFIX_LEN (curr_pathname);
191
const char *p = curr_pathname + strlen (curr_pathname);
201
q = (char *) xmalloc (p - curr_pathname + 1);
206
memcpy (q, curr_pathname, p - curr_pathname);
207
q[p - curr_pathname] = '\0';
211
/* Compute the current installation prefix by removing the trailing
212
rel_installdir from it. */
214
const char *rp = rel_installdir + strlen (rel_installdir);
215
const char *cp = curr_installdir + strlen (curr_installdir);
216
const char *cp_base =
217
curr_installdir + FILE_SYSTEM_PREFIX_LEN (curr_installdir);
219
while (rp > rel_installdir && cp > cp_base)
222
const char *rpi = rp;
223
const char *cpi = cp;
225
while (rpi > rel_installdir && cpi > cp_base)
229
if (ISSLASH (*rpi) || ISSLASH (*cpi))
231
if (ISSLASH (*rpi) && ISSLASH (*cpi))
235
/* Do case-insensitive comparison if the filesystem is always or
236
often case-insensitive. It's better to accept the comparison
237
if the difference is only in case, rather than to fail. */
238
#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
239
/* Win32, Cygwin, OS/2, DOS - case insignificant filesystem */
240
if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
241
!= (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
250
/* The last pathname component was the same. opi and cpi now point
251
to the slash before it. */
256
if (rp > rel_installdir)
257
/* Unexpected: The curr_installdir does not end with rel_installdir. */
261
size_t curr_prefix_len = cp - curr_installdir;
264
curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
266
if (curr_prefix == NULL)
269
memcpy (curr_prefix, curr_installdir, curr_prefix_len);
270
curr_prefix[curr_prefix_len] = '\0';
277
#endif /* !IN_LIBRARY || PIC */
279
#if defined PIC && defined INSTALLDIR
281
/* Full pathname of shared library, or NULL. */
282
static char *shared_library_fullname;
284
#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
286
/* Determine the full pathname of the shared library when it is loaded. */
289
DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
293
if (event == DLL_PROCESS_ATTACH)
295
/* The DLL is being loaded into an application's address range. */
296
static char location[MAX_PATH];
298
if (!GetModuleFileName (module_handle, location, sizeof (location)))
299
/* Shouldn't happen. */
302
if (!IS_PATH_WITH_DIR (location))
303
/* Shouldn't happen. */
307
#if defined __CYGWIN__
308
/* On Cygwin, we need to convert paths coming from Win32 system calls
309
to the Unix-like slashified notation. */
310
static char location_as_posix_path[2 * MAX_PATH];
311
/* There's no error return defined for cygwin_conv_to_posix_path.
312
See cygwin-api/func-cygwin-conv-to-posix-path.html.
313
Does it overflow the buffer of expected size MAX_PATH or does it
314
truncate the path? I don't know. Let's catch both. */
315
cygwin_conv_to_posix_path (location, location_as_posix_path);
316
location_as_posix_path[MAX_PATH - 1] = '\0';
317
if (strlen (location_as_posix_path) >= MAX_PATH - 1)
318
/* A sign of buffer overflow or path truncation. */
320
shared_library_fullname = strdup (location_as_posix_path);
322
shared_library_fullname = strdup (location);
330
#else /* Unix except Cygwin */
333
find_shared_library_fullname ()
335
#if defined __linux__ && __GLIBC__ >= 2
336
/* Linux has /proc/self/maps. glibc 2 has the getline() function. */
339
/* Open the current process' maps file. It describes one VMA per line. */
340
fp = fopen ("/proc/self/maps", "r");
343
unsigned long address = (unsigned long) &find_shared_library_fullname;
346
unsigned long start, end;
349
if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
351
if (address >= start && address <= end - 1)
353
/* Found it. Now see if this line contains a filename. */
354
while (c = getc (fp), c != EOF && c != '\n' && c != '/')
362
shared_library_fullname = NULL; size = 0;
363
len = getline (&shared_library_fullname, &size, fp);
366
/* Success: filled shared_library_fullname. */
367
if (len > 0 && shared_library_fullname[len - 1] == '\n')
368
shared_library_fullname[len - 1] = '\0';
373
while (c = getc (fp), c != EOF && c != '\n')
381
#endif /* (WIN32 or Cygwin) / (Unix except Cygwin) */
383
/* Return the full pathname of the current shared library.
384
Return NULL if unknown.
385
Guaranteed to work only on Linux, Cygwin and Woe32. */
387
get_shared_library_fullname ()
389
#if !(defined _WIN32 || defined __WIN32__ || defined __CYGWIN__)
390
static bool tried_find_shared_library_fullname;
391
if (!tried_find_shared_library_fullname)
393
find_shared_library_fullname ();
394
tried_find_shared_library_fullname = true;
397
return shared_library_fullname;
402
/* Returns the pathname, relocated according to the current installation
405
relocate (const char *pathname)
407
#if defined PIC && defined INSTALLDIR
408
static int initialized;
410
/* Initialization code for a shared library. */
413
/* At this point, orig_prefix and curr_prefix likely have already been
414
set through the main program's set_program_name_and_installdir
415
function. This is sufficient in the case that the library has
416
initially been installed in the same orig_prefix. But we can do
417
better, to also cover the cases that 1. it has been installed
418
in a different prefix before being moved to orig_prefix and (later)
419
to curr_prefix, 2. unlike the program, it has not moved away from
421
const char *orig_installprefix = INSTALLPREFIX;
422
const char *orig_installdir = INSTALLDIR;
423
const char *curr_prefix_better;
426
compute_curr_prefix (orig_installprefix, orig_installdir,
427
get_shared_library_fullname ());
428
if (curr_prefix_better == NULL)
429
curr_prefix_better = curr_prefix;
431
set_relocation_prefix (orig_installprefix, curr_prefix_better);
437
/* Note: It is not necessary to perform case insensitive comparison here,
438
even for DOS-like filesystems, because the pathname argument was
439
typically created from the same Makefile variable as orig_prefix came
441
if (orig_prefix != NULL && curr_prefix != NULL
442
&& strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
444
if (pathname[orig_prefix_len] == '\0')
445
/* pathname equals orig_prefix. */
447
if (ISSLASH (pathname[orig_prefix_len]))
449
/* pathname starts with orig_prefix. */
450
const char *pathname_tail = &pathname[orig_prefix_len];
452
(char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
458
memcpy (result, curr_prefix, curr_prefix_len);
459
strcpy (result + curr_prefix_len, pathname_tail);
464
/* Nothing to relocate. */