1 /* Provide relocatable packages.
2 Copyright (C) 2003-2006, 2008-2020 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
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) 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
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18
19 /* Tell glibc's <stdio.h> to provide a prototype for getline().
20 This must come before <config.h> because <config.h> may include
21 <features.h>, and once <features.h> has been included, it's too late. */
22 #ifndef _GNU_SOURCE
23 # define _GNU_SOURCE 1
24 #endif
25
26 #define _GL_USE_STDLIB_ALLOC 1
27 #include <config.h>
28
29 /* Specification. */
30 #include "relocatable.h"
31
32 #if ENABLE_RELOCATABLE
33
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #ifdef NO_XMALLOC
40 # define xmalloc malloc
41 #else
42 # include "xalloc.h"
43 #endif
44
45 #if defined _WIN32 && !defined __CYGWIN__
46 # define WIN32_LEAN_AND_MEAN
47 # include <windows.h>
48 #endif
49
50 #ifdef __EMX__
51 # define INCL_DOS
52 # include <os2.h>
53
54 # define strcmp stricmp
55 # define strncmp strnicmp
56 #endif
57
58 #if DEPENDS_ON_LIBCHARSET
59 # include <libcharset.h>
60 #endif
61 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
62 # include <iconv.h>
63 #endif
64 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
65 # include <libintl.h>
66 #endif
67
68 #if defined _WIN32 && !defined __CYGWIN__
69 /* Don't assume that UNICODE is not defined. */
70 # undef GetModuleFileName
71 # define GetModuleFileName GetModuleFileNameA
72 #endif
73
74 /* Faked cheap 'bool'. */
75 #undef bool
76 #undef false
77 #undef true
78 #define bool int
79 #define false 0
80 #define true 1
81
82 /* Pathname support.
83 ISSLASH(C) tests whether C is a directory separator character.
84 IS_FILE_NAME_WITH_DIR(P) tests whether P contains a directory specification.
85 */
86 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
87 /* Native Windows, OS/2, DOS */
88 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
89 # define HAS_DEVICE(P) \
90 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
91 && (P)[1] == ':')
92 # define IS_FILE_NAME_WITH_DIR(P) \
93 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
94 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
95 #else
96 /* Unix */
97 # define ISSLASH(C) ((C) == '/')
98 # define IS_FILE_NAME_WITH_DIR(P) (strchr (P, '/') != NULL)
99 # define FILE_SYSTEM_PREFIX_LEN(P) 0
100 #endif
101
102 /* Whether to enable the more costly support for relocatable libraries.
103 It allows libraries to be have been installed with a different original
104 prefix than the program. But it is quite costly, especially on Cygwin
105 platforms, see below. Therefore we enable it by default only on native
106 Windows platforms. */
107 #ifndef ENABLE_COSTLY_RELOCATABLE
108 # if defined _WIN32 && !defined __CYGWIN__
109 # define ENABLE_COSTLY_RELOCATABLE 1
110 # else
111 # define ENABLE_COSTLY_RELOCATABLE 0
112 # endif
113 #endif
114
115 /* Original installation prefix. */
116 static char *orig_prefix;
117 static size_t orig_prefix_len;
118 /* Current installation prefix. */
119 static char *curr_prefix;
120 static size_t curr_prefix_len;
121 /* These prefixes do not end in a slash. Anything that will be concatenated
122 to them must start with a slash. */
123
124 /* Sets the original and the current installation prefix of this module.
125 Relocation simply replaces a pathname starting with the original prefix
126 by the corresponding pathname with the current prefix instead. Both
127 prefixes should be directory names without trailing slash (i.e. use ""
128 instead of "/"). */
129 static void
set_this_relocation_prefix(const char * orig_prefix_arg,const char * curr_prefix_arg)130 set_this_relocation_prefix (const char *orig_prefix_arg,
131 const char *curr_prefix_arg)
132 {
133 if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
134 /* Optimization: if orig_prefix and curr_prefix are equal, the
135 relocation is a nop. */
136 && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
137 {
138 /* Duplicate the argument strings. */
139 char *memory;
140
141 orig_prefix_len = strlen (orig_prefix_arg);
142 curr_prefix_len = strlen (curr_prefix_arg);
143 memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
144 #ifdef NO_XMALLOC
145 if (memory != NULL)
146 #endif
147 {
148 memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
149 orig_prefix = memory;
150 memory += orig_prefix_len + 1;
151 memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
152 curr_prefix = memory;
153 return;
154 }
155 }
156 orig_prefix = NULL;
157 curr_prefix = NULL;
158 /* Don't worry about wasted memory here - this function is usually only
159 called once. */
160 }
161
162 /* Sets the original and the current installation prefix of the package.
163 Relocation simply replaces a pathname starting with the original prefix
164 by the corresponding pathname with the current prefix instead. Both
165 prefixes should be directory names without trailing slash (i.e. use ""
166 instead of "/"). */
167 void
set_relocation_prefix(const char * orig_prefix_arg,const char * curr_prefix_arg)168 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
169 {
170 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
171
172 /* Now notify all dependent libraries. */
173 #if DEPENDS_ON_LIBCHARSET
174 libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
175 #endif
176 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
177 libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
178 #endif
179 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
180 libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
181 #endif
182 }
183
184 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE)
185
186 /* Convenience function:
187 Computes the current installation prefix, based on the original
188 installation prefix, the original installation directory of a particular
189 file, and the current pathname of this file.
190 Returns it, freshly allocated. Returns NULL upon failure. */
191 #ifdef IN_LIBRARY
192 #define compute_curr_prefix local_compute_curr_prefix
193 static
194 #endif
195 char *
compute_curr_prefix(const char * orig_installprefix,const char * orig_installdir,const char * curr_pathname)196 compute_curr_prefix (const char *orig_installprefix,
197 const char *orig_installdir,
198 const char *curr_pathname)
199 {
200 char *curr_installdir;
201 const char *rel_installdir;
202
203 if (curr_pathname == NULL)
204 return NULL;
205
206 /* Determine the relative installation directory, relative to the prefix.
207 This is simply the difference between orig_installprefix and
208 orig_installdir. */
209 if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
210 != 0)
211 /* Shouldn't happen - nothing should be installed outside $(prefix). */
212 return NULL;
213 rel_installdir = orig_installdir + strlen (orig_installprefix);
214
215 /* Determine the current installation directory. */
216 {
217 const char *p_base = curr_pathname + FILE_SYSTEM_PREFIX_LEN (curr_pathname);
218 const char *p = curr_pathname + strlen (curr_pathname);
219 char *q;
220
221 while (p > p_base)
222 {
223 p--;
224 if (ISSLASH (*p))
225 break;
226 }
227
228 q = (char *) xmalloc (p - curr_pathname + 1);
229 #ifdef NO_XMALLOC
230 if (q == NULL)
231 return NULL;
232 #endif
233 memcpy (q, curr_pathname, p - curr_pathname);
234 q[p - curr_pathname] = '\0';
235 curr_installdir = q;
236 }
237
238 /* Compute the current installation prefix by removing the trailing
239 rel_installdir from it. */
240 {
241 const char *rp = rel_installdir + strlen (rel_installdir);
242 const char *cp = curr_installdir + strlen (curr_installdir);
243 const char *cp_base =
244 curr_installdir + FILE_SYSTEM_PREFIX_LEN (curr_installdir);
245
246 while (rp > rel_installdir && cp > cp_base)
247 {
248 bool same = false;
249 const char *rpi = rp;
250 const char *cpi = cp;
251
252 while (rpi > rel_installdir && cpi > cp_base)
253 {
254 rpi--;
255 cpi--;
256 if (ISSLASH (*rpi) || ISSLASH (*cpi))
257 {
258 if (ISSLASH (*rpi) && ISSLASH (*cpi))
259 same = true;
260 break;
261 }
262 /* Do case-insensitive comparison if the file system is always or
263 often case-insensitive. It's better to accept the comparison
264 if the difference is only in case, rather than to fail. */
265 #if defined _WIN32 || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
266 /* Native Windows, Cygwin, OS/2, DOS - case insignificant file system */
267 if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
268 != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
269 break;
270 #else
271 if (*rpi != *cpi)
272 break;
273 #endif
274 }
275 if (!same)
276 break;
277 /* The last pathname component was the same. rpi and cpi now point
278 to the slash before it. */
279 rp = rpi;
280 cp = cpi;
281 }
282
283 if (rp > rel_installdir)
284 {
285 /* Unexpected: The curr_installdir does not end with rel_installdir. */
286 free (curr_installdir);
287 return NULL;
288 }
289
290 {
291 size_t computed_curr_prefix_len = cp - curr_installdir;
292 char *computed_curr_prefix;
293
294 computed_curr_prefix = (char *) xmalloc (computed_curr_prefix_len + 1);
295 #ifdef NO_XMALLOC
296 if (computed_curr_prefix == NULL)
297 {
298 free (curr_installdir);
299 return NULL;
300 }
301 #endif
302 memcpy (computed_curr_prefix, curr_installdir, computed_curr_prefix_len);
303 computed_curr_prefix[computed_curr_prefix_len] = '\0';
304
305 free (curr_installdir);
306
307 return computed_curr_prefix;
308 }
309 }
310 }
311
312 #endif /* !IN_LIBRARY || PIC */
313
314 #if defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE
315
316 /* Full pathname of shared library, or NULL. */
317 static char *shared_library_fullname;
318
319 #if defined _WIN32 && !defined __CYGWIN__
320 /* Native Windows only.
321 On Cygwin, it is better to use the Cygwin provided /proc interface, than
322 to use native Windows API and cygwin_conv_to_posix_path, because it
323 supports longer file names
324 (see <https://cygwin.com/ml/cygwin/2011-01/msg00410.html>). */
325
326 /* Determine the full pathname of the shared library when it is loaded. */
327
328 BOOL WINAPI
DllMain(HINSTANCE module_handle,DWORD event,LPVOID reserved)329 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
330 {
331 (void) reserved;
332
333 if (event == DLL_PROCESS_ATTACH)
334 {
335 /* The DLL is being loaded into an application's address range. */
336 static char location[MAX_PATH];
337
338 if (!GetModuleFileName (module_handle, location, sizeof (location)))
339 /* Shouldn't happen. */
340 return FALSE;
341
342 if (!IS_FILE_NAME_WITH_DIR (location))
343 /* Shouldn't happen. */
344 return FALSE;
345
346 shared_library_fullname = strdup (location);
347 }
348
349 return TRUE;
350 }
351
352 #elif defined __EMX__
353
354 extern int _CRT_init (void);
355 extern void _CRT_term (void);
356 extern void __ctordtorInit (void);
357 extern void __ctordtorTerm (void);
358
359 unsigned long _System
_DLL_InitTerm(unsigned long hModule,unsigned long ulFlag)360 _DLL_InitTerm (unsigned long hModule, unsigned long ulFlag)
361 {
362 static char location[CCHMAXPATH];
363
364 switch (ulFlag)
365 {
366 case 0:
367 if (_CRT_init () == -1)
368 return 0;
369
370 __ctordtorInit();
371
372 /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
373 for specification of DosQueryModuleName(). */
374 if (DosQueryModuleName (hModule, sizeof (location), location))
375 return 0;
376
377 _fnslashify (location);
378 shared_library_fullname = strdup (location);
379 break;
380
381 case 1:
382 __ctordtorTerm();
383
384 _CRT_term ();
385 break;
386 }
387
388 return 1;
389 }
390
391 #else /* Unix */
392
393 static void
find_shared_library_fullname()394 find_shared_library_fullname ()
395 {
396 #if (defined __linux__ && (__GLIBC__ >= 2 || defined __UCLIBC__)) || defined __CYGWIN__
397 /* Linux has /proc/self/maps. glibc 2 and uClibc have the getline()
398 function.
399 Cygwin >= 1.5 has /proc/self/maps and the getline() function too.
400 But it is costly: ca. 0.3 ms on Linux, 3 ms on Cygwin 1.5, and 5 ms on
401 Cygwin 1.7. */
402 FILE *fp;
403
404 /* Open the current process' maps file. It describes one VMA per line. */
405 fp = fopen ("/proc/self/maps", "r");
406 if (fp)
407 {
408 unsigned long address = (unsigned long) &find_shared_library_fullname;
409 for (;;)
410 {
411 unsigned long start, end;
412 int c;
413
414 if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
415 break;
416 if (address >= start && address <= end - 1)
417 {
418 /* Found it. Now see if this line contains a filename. */
419 while (c = getc (fp), c != EOF && c != '\n' && c != '/')
420 continue;
421 if (c == '/')
422 {
423 size_t size;
424 int len;
425
426 ungetc (c, fp);
427 shared_library_fullname = NULL; size = 0;
428 len = getline (&shared_library_fullname, &size, fp);
429 if (len >= 0)
430 {
431 /* Success: filled shared_library_fullname. */
432 if (len > 0 && shared_library_fullname[len - 1] == '\n')
433 shared_library_fullname[len - 1] = '\0';
434 }
435 }
436 break;
437 }
438 while (c = getc (fp), c != EOF && c != '\n')
439 continue;
440 }
441 fclose (fp);
442 }
443 #endif
444 }
445
446 #endif /* Native Windows / EMX / Unix */
447
448 /* Return the full pathname of the current shared library.
449 Return NULL if unknown.
450 Guaranteed to work only on Linux, EMX, Cygwin, and native Windows. */
451 static char *
get_shared_library_fullname()452 get_shared_library_fullname ()
453 {
454 #if !(defined _WIN32 && !defined __CYGWIN__) && !defined __EMX__
455 static bool tried_find_shared_library_fullname;
456 if (!tried_find_shared_library_fullname)
457 {
458 find_shared_library_fullname ();
459 tried_find_shared_library_fullname = true;
460 }
461 #endif
462 return shared_library_fullname;
463 }
464
465 #endif /* PIC */
466
467 /* Returns the pathname, relocated according to the current installation
468 directory.
469 The returned string is either PATHNAME unmodified or a freshly allocated
470 string that you can free with free() after casting it to 'char *'. */
471 const char *
relocate(const char * pathname)472 relocate (const char *pathname)
473 {
474 #if defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE
475 static int initialized;
476
477 /* Initialization code for a shared library. */
478 if (!initialized)
479 {
480 /* At this point, orig_prefix and curr_prefix likely have already been
481 set through the main program's set_program_name_and_installdir
482 function. This is sufficient in the case that the library has
483 initially been installed in the same orig_prefix. But we can do
484 better, to also cover the cases that 1. it has been installed
485 in a different prefix before being moved to orig_prefix and (later)
486 to curr_prefix, 2. unlike the program, it has not moved away from
487 orig_prefix. */
488 const char *orig_installprefix = INSTALLPREFIX;
489 const char *orig_installdir = INSTALLDIR;
490 char *curr_prefix_better;
491
492 curr_prefix_better =
493 compute_curr_prefix (orig_installprefix, orig_installdir,
494 get_shared_library_fullname ());
495
496 set_relocation_prefix (orig_installprefix,
497 curr_prefix_better != NULL
498 ? curr_prefix_better
499 : curr_prefix);
500
501 if (curr_prefix_better != NULL)
502 free (curr_prefix_better);
503
504 initialized = 1;
505 }
506 #endif
507
508 /* Note: It is not necessary to perform case insensitive comparison here,
509 even for DOS-like file systems, because the pathname argument was
510 typically created from the same Makefile variable as orig_prefix came
511 from. */
512 if (orig_prefix != NULL && curr_prefix != NULL
513 && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
514 {
515 if (pathname[orig_prefix_len] == '\0')
516 {
517 /* pathname equals orig_prefix. */
518 char *result = (char *) xmalloc (strlen (curr_prefix) + 1);
519
520 #ifdef NO_XMALLOC
521 if (result != NULL)
522 #endif
523 {
524 strcpy (result, curr_prefix);
525 return result;
526 }
527 }
528 else if (ISSLASH (pathname[orig_prefix_len]))
529 {
530 /* pathname starts with orig_prefix. */
531 const char *pathname_tail = &pathname[orig_prefix_len];
532 char *result =
533 (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
534
535 #ifdef NO_XMALLOC
536 if (result != NULL)
537 #endif
538 {
539 memcpy (result, curr_prefix, curr_prefix_len);
540 strcpy (result + curr_prefix_len, pathname_tail);
541 return result;
542 }
543 }
544 }
545
546 #ifdef __EMX__
547 # ifdef __KLIBC__
548 # undef strncmp
549
550 if (strncmp (pathname, "/@unixroot", 10) == 0
551 && (pathname[10] == '\0' || ISSLASH (pathname[10])))
552 {
553 /* kLIBC itself processes /@unixroot prefix */
554 return pathname;
555 }
556 else
557 # endif
558 if (ISSLASH (pathname[0]))
559 {
560 const char *unixroot = getenv ("UNIXROOT");
561
562 if (unixroot && HAS_DEVICE (unixroot) && unixroot[2] == '\0')
563 {
564 char *result = (char *) xmalloc (2 + strlen (pathname) + 1);
565 #ifdef NO_XMALLOC
566 if (result != NULL)
567 #endif
568 {
569 memcpy (result, unixroot, 2);
570 strcpy (result + 2, pathname);
571 return result;
572 }
573 }
574 }
575 #endif
576
577 /* Nothing to relocate. */
578 return pathname;
579 }
580
581 /* Returns the pathname, relocated according to the current installation
582 directory.
583 This function sets *ALLOCATEDP to the allocated memory, or to NULL if
584 no memory allocation occurs. So that, after you're done with the return
585 value, to reclaim allocated memory, you can do: free (*ALLOCATEDP). */
586 const char *
relocate2(const char * pathname,char ** allocatedp)587 relocate2 (const char *pathname, char **allocatedp)
588 {
589 const char *result = relocate (pathname);
590 *allocatedp = (result != pathname ? (char *) result : NULL);
591 return result;
592 }
593
594 #endif
595