exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / relocatable.c
blob365c085825b6d78e33cfe4abc41dca37fdf77112
1 /* Provide relocatable packages.
2 Copyright (C) 2003-2006, 2008-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
10 This file 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.
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/>. */
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
26 #define _GL_USE_STDLIB_ALLOC 1
27 #include <config.h>
29 /* Specification. */
30 #include "relocatable.h"
32 #if ENABLE_RELOCATABLE
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #ifdef NO_XMALLOC
40 # define xmalloc malloc
41 #else
42 # include "xalloc.h"
43 #endif
45 #if defined _WIN32 && !defined __CYGWIN__
46 # define WIN32_LEAN_AND_MEAN
47 # include <windows.h>
48 #endif
50 #ifdef __EMX__
51 # define INCL_DOS
52 # include <os2.h>
54 # define strcmp stricmp
55 # define strncmp strnicmp
56 #endif
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
68 #if defined _WIN32 && !defined __CYGWIN__
69 /* Don't assume that UNICODE is not defined. */
70 # undef GetModuleFileName
71 # define GetModuleFileName GetModuleFileNameA
72 #endif
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
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.
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
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
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. */
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
130 set_this_relocation_prefix (const char *orig_prefix_arg,
131 const char *curr_prefix_arg)
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)
138 /* Duplicate the argument strings. */
139 char *memory;
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
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;
156 orig_prefix = NULL;
157 curr_prefix = NULL;
158 /* Don't worry about wasted memory here - this function is usually only
159 called once. */
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
168 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
170 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
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
184 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE)
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 *
196 compute_curr_prefix (const char *orig_installprefix,
197 const char *orig_installdir,
198 const char *curr_pathname)
200 char *curr_installdir;
201 const char *rel_installdir;
203 if (curr_pathname == NULL)
204 return NULL;
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);
215 /* Determine the current installation directory. */
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;
221 while (p > p_base)
223 p--;
224 if (ISSLASH (*p))
225 break;
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;
238 /* Compute the current installation prefix by removing the trailing
239 rel_installdir from it. */
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);
246 while (rp > rel_installdir && cp > cp_base)
248 bool same = false;
249 const char *rpi = rp;
250 const char *cpi = cp;
252 while (rpi > rel_installdir && cpi > cp_base)
254 rpi--;
255 cpi--;
256 if (ISSLASH (*rpi) || ISSLASH (*cpi))
258 if (ISSLASH (*rpi) && ISSLASH (*cpi))
259 same = true;
260 break;
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
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;
283 if (rp > rel_installdir)
285 /* Unexpected: The curr_installdir does not end with rel_installdir. */
286 free (curr_installdir);
287 return NULL;
291 size_t computed_curr_prefix_len = cp - curr_installdir;
292 char *computed_curr_prefix;
294 computed_curr_prefix = (char *) xmalloc (computed_curr_prefix_len + 1);
295 #ifdef NO_XMALLOC
296 if (computed_curr_prefix == NULL)
298 free (curr_installdir);
299 return NULL;
301 #endif
302 memcpy (computed_curr_prefix, curr_installdir, computed_curr_prefix_len);
303 computed_curr_prefix[computed_curr_prefix_len] = '\0';
305 free (curr_installdir);
307 return computed_curr_prefix;
312 #endif /* !IN_LIBRARY || PIC */
314 #if defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE
316 /* Full pathname of shared library, or NULL. */
317 static char *shared_library_fullname;
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>). */
326 /* Determine the full pathname of the shared library when it is loaded.
328 Documentation:
329 <https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain> */
331 BOOL WINAPI
332 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
334 (void) reserved;
336 if (event == DLL_PROCESS_ATTACH)
338 /* The DLL is being loaded into an application's address range. */
339 static char location[MAX_PATH];
341 if (!GetModuleFileName (module_handle, location, sizeof (location)))
342 /* Shouldn't happen. */
343 return FALSE;
345 if (!IS_FILE_NAME_WITH_DIR (location))
346 /* Shouldn't happen. */
347 return FALSE;
349 /* Avoid a memory leak when the same DLL get attached, detached,
350 attached, detached, and so on. This happens e.g. when a spell
351 checker DLL is used repeatedly by a mail program. */
352 if (!(shared_library_fullname != NULL
353 && strcmp (shared_library_fullname, location) == 0))
354 /* Remember the full pathname of the shared library. */
355 shared_library_fullname = strdup (location);
358 return TRUE;
361 #elif defined __EMX__
363 extern int _CRT_init (void);
364 extern void _CRT_term (void);
365 extern void __ctordtorInit (void);
366 extern void __ctordtorTerm (void);
368 unsigned long _System
369 _DLL_InitTerm (unsigned long hModule, unsigned long ulFlag)
371 static char location[CCHMAXPATH];
373 switch (ulFlag)
375 case 0:
376 if (_CRT_init () == -1)
377 return 0;
379 __ctordtorInit();
381 /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
382 for specification of DosQueryModuleName(). */
383 if (DosQueryModuleName (hModule, sizeof (location), location))
384 return 0;
386 _fnslashify (location);
387 shared_library_fullname = strdup (location);
388 break;
390 case 1:
391 __ctordtorTerm();
393 _CRT_term ();
394 break;
397 return 1;
400 #else /* Unix */
402 static void
403 find_shared_library_fullname ()
405 #if (defined __linux__ && (__GLIBC__ >= 2 || defined __UCLIBC__)) || defined __CYGWIN__
406 /* Linux has /proc/self/maps. glibc 2 and uClibc have the getline()
407 function.
408 Cygwin >= 1.5 has /proc/self/maps and the getline() function too.
409 But it is costly: ca. 0.3 ms on Linux, 3 ms on Cygwin 1.5, and 5 ms on
410 Cygwin 1.7. */
411 FILE *fp;
413 /* Open the current process' maps file. It describes one VMA per line. */
414 fp = fopen ("/proc/self/maps", "r");
415 if (fp)
417 unsigned long address = (unsigned long) &find_shared_library_fullname;
418 for (;;)
420 unsigned long start, end;
421 int c;
423 if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
424 break;
425 if (address >= start && address <= end - 1)
427 /* Found it. Now see if this line contains a filename. */
428 while (c = getc (fp), c != EOF && c != '\n' && c != '/')
429 continue;
430 if (c == '/')
432 size_t size;
433 int len;
435 ungetc (c, fp);
436 shared_library_fullname = NULL; size = 0;
437 len = getline (&shared_library_fullname, &size, fp);
438 if (len >= 0)
440 /* Success: filled shared_library_fullname. */
441 if (len > 0 && shared_library_fullname[len - 1] == '\n')
442 shared_library_fullname[len - 1] = '\0';
445 break;
447 while (c = getc (fp), c != EOF && c != '\n')
448 continue;
450 fclose (fp);
452 #endif
455 #endif /* Native Windows / EMX / Unix */
457 /* Return the full pathname of the current shared library.
458 Return NULL if unknown.
459 Guaranteed to work only on Linux, EMX, Cygwin, and native Windows. */
460 static char *
461 get_shared_library_fullname ()
463 #if !(defined _WIN32 && !defined __CYGWIN__) && !defined __EMX__
464 static bool tried_find_shared_library_fullname;
465 if (!tried_find_shared_library_fullname)
467 find_shared_library_fullname ();
468 tried_find_shared_library_fullname = true;
470 #endif
471 return shared_library_fullname;
474 #endif /* PIC */
476 /* Returns the pathname, relocated according to the current installation
477 directory.
478 The returned string is either PATHNAME unmodified or a freshly allocated
479 string that you can free with free() after casting it to 'char *'. */
480 const char *
481 relocate (const char *pathname)
483 #if defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE
484 static int initialized;
486 /* Initialization code for a shared library. */
487 if (!initialized)
489 /* At this point, orig_prefix and curr_prefix likely have already been
490 set through the main program's set_program_name_and_installdir
491 function. This is sufficient in the case that the library has
492 initially been installed in the same orig_prefix. But we can do
493 better, to also cover the cases that 1. it has been installed
494 in a different prefix before being moved to orig_prefix and (later)
495 to curr_prefix, 2. unlike the program, it has not moved away from
496 orig_prefix. */
497 const char *orig_installprefix = INSTALLPREFIX;
498 const char *orig_installdir = INSTALLDIR;
499 char *curr_prefix_better;
501 curr_prefix_better =
502 compute_curr_prefix (orig_installprefix, orig_installdir,
503 get_shared_library_fullname ());
505 set_relocation_prefix (orig_installprefix,
506 curr_prefix_better != NULL
507 ? curr_prefix_better
508 : curr_prefix);
510 if (curr_prefix_better != NULL)
511 free (curr_prefix_better);
513 initialized = 1;
515 #endif
517 /* Note: It is not necessary to perform case insensitive comparison here,
518 even for DOS-like file systems, because the pathname argument was
519 typically created from the same Makefile variable as orig_prefix came
520 from. */
521 if (orig_prefix != NULL && curr_prefix != NULL
522 && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
524 if (pathname[orig_prefix_len] == '\0')
526 /* pathname equals orig_prefix. */
527 char *result = (char *) xmalloc (strlen (curr_prefix) + 1);
529 #ifdef NO_XMALLOC
530 if (result != NULL)
531 #endif
533 strcpy (result, curr_prefix);
534 return result;
537 else if (ISSLASH (pathname[orig_prefix_len]))
539 /* pathname starts with orig_prefix. */
540 const char *pathname_tail = &pathname[orig_prefix_len];
541 char *result =
542 (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
544 #ifdef NO_XMALLOC
545 if (result != NULL)
546 #endif
548 memcpy (result, curr_prefix, curr_prefix_len);
549 strcpy (result + curr_prefix_len, pathname_tail);
550 return result;
555 #ifdef __EMX__
556 # ifdef __KLIBC__
557 # undef strncmp
559 if (strncmp (pathname, "/@unixroot", 10) == 0
560 && (pathname[10] == '\0' || ISSLASH (pathname[10])))
562 /* kLIBC itself processes /@unixroot prefix */
563 return pathname;
565 else
566 # endif
567 if (ISSLASH (pathname[0]))
569 const char *unixroot = getenv ("UNIXROOT");
571 if (unixroot && HAS_DEVICE (unixroot) && unixroot[2] == '\0')
573 char *result = (char *) xmalloc (2 + strlen (pathname) + 1);
574 #ifdef NO_XMALLOC
575 if (result != NULL)
576 #endif
578 memcpy (result, unixroot, 2);
579 strcpy (result + 2, pathname);
580 return result;
584 #endif
586 /* Nothing to relocate. */
587 return pathname;
590 /* Returns the pathname, relocated according to the current installation
591 directory.
592 This function sets *ALLOCATEDP to the allocated memory, or to NULL if
593 no memory allocation occurs. So that, after you're done with the return
594 value, to reclaim allocated memory, you can do: free (*ALLOCATEDP). */
595 const char *
596 relocate2 (const char *pathname, char **allocatedp)
598 const char *result = relocate (pathname);
599 *allocatedp = (result != pathname ? (char *) result : NULL);
600 return result;
603 #endif