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