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. */
23 # define _GNU_SOURCE 1
26 #define _GL_USE_STDLIB_ALLOC 1
30 #include "relocatable.h"
32 #if ENABLE_RELOCATABLE
40 # define xmalloc malloc
45 #if defined _WIN32 && !defined __CYGWIN__
46 # define WIN32_LEAN_AND_MEAN
54 # define strcmp stricmp
55 # define strncmp strnicmp
58 #if DEPENDS_ON_LIBCHARSET
59 # include <libcharset.h>
61 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
64 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
68 #if defined _WIN32 && !defined __CYGWIN__
69 /* Don't assume that UNICODE is not defined. */
70 # undef GetModuleFileName
71 # define GetModuleFileName GetModuleFileNameA
74 /* Faked cheap 'bool'. */
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')) \
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)
97 # define ISSLASH(C) ((C) == '/')
98 # define IS_FILE_NAME_WITH_DIR(P) (strchr (P, '/') != NULL)
99 # define FILE_SYSTEM_PREFIX_LEN(P) 0
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
111 # define ENABLE_COSTLY_RELOCATABLE 0
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 ""
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. */
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);
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
;
158 /* Don't worry about wasted memory here - this function is usually only
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 ""
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
);
176 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
177 libiconv_set_relocation_prefix (orig_prefix_arg
, curr_prefix_arg
);
179 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
180 libintl_set_relocation_prefix (orig_prefix_arg
, curr_prefix_arg
);
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. */
192 #define compute_curr_prefix local_compute_curr_prefix
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
)
206 /* Determine the relative installation directory, relative to the prefix.
207 This is simply the difference between orig_installprefix and
209 if (strncmp (orig_installprefix
, orig_installdir
, strlen (orig_installprefix
))
211 /* Shouldn't happen - nothing should be installed outside $(prefix). */
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
);
228 q
= (char *) xmalloc (p
- curr_pathname
+ 1);
233 memcpy (q
, curr_pathname
, p
- curr_pathname
);
234 q
[p
- curr_pathname
] = '\0';
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
)
249 const char *rpi
= rp
;
250 const char *cpi
= cp
;
252 while (rpi
> rel_installdir
&& cpi
> cp_base
)
256 if (ISSLASH (*rpi
) || ISSLASH (*cpi
))
258 if (ISSLASH (*rpi
) && ISSLASH (*cpi
))
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
))
277 /* The last pathname component was the same. rpi and cpi now point
278 to the slash before it. */
283 if (rp
> rel_installdir
)
285 /* Unexpected: The curr_installdir does not end with rel_installdir. */
286 free (curr_installdir
);
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);
296 if (computed_curr_prefix
== NULL
)
298 free (curr_installdir
);
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.
329 <https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain> */
332 DllMain (HINSTANCE module_handle
, DWORD event
, LPVOID 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. */
345 if (!IS_FILE_NAME_WITH_DIR (location
))
346 /* Shouldn't happen. */
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
);
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
];
376 if (_CRT_init () == -1)
381 /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
382 for specification of DosQueryModuleName(). */
383 if (DosQueryModuleName (hModule
, sizeof (location
), location
))
386 _fnslashify (location
);
387 shared_library_fullname
= strdup (location
);
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()
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
413 /* Open the current process' maps file. It describes one VMA per line. */
414 fp
= fopen ("/proc/self/maps", "r");
417 unsigned long address
= (unsigned long) &find_shared_library_fullname
;
420 unsigned long start
, end
;
423 if (fscanf (fp
, "%lx-%lx", &start
, &end
) != 2)
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
!= '/')
436 shared_library_fullname
= NULL
; size
= 0;
437 len
= getline (&shared_library_fullname
, &size
, fp
);
440 /* Success: filled shared_library_fullname. */
441 if (len
> 0 && shared_library_fullname
[len
- 1] == '\n')
442 shared_library_fullname
[len
- 1] = '\0';
447 while (c
= getc (fp
), c
!= EOF
&& c
!= '\n')
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. */
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;
471 return shared_library_fullname
;
476 /* Returns the pathname, relocated according to the current installation
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 *'. */
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. */
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
497 const char *orig_installprefix
= INSTALLPREFIX
;
498 const char *orig_installdir
= INSTALLDIR
;
499 char *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
510 if (curr_prefix_better
!= NULL
)
511 free (curr_prefix_better
);
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
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);
533 strcpy (result
, curr_prefix
);
537 else if (ISSLASH (pathname
[orig_prefix_len
]))
539 /* pathname starts with orig_prefix. */
540 const char *pathname_tail
= &pathname
[orig_prefix_len
];
542 (char *) xmalloc (curr_prefix_len
+ strlen (pathname_tail
) + 1);
548 memcpy (result
, curr_prefix
, curr_prefix_len
);
549 strcpy (result
+ curr_prefix_len
, pathname_tail
);
559 if (strncmp (pathname
, "/@unixroot", 10) == 0
560 && (pathname
[10] == '\0' || ISSLASH (pathname
[10])))
562 /* kLIBC itself processes /@unixroot prefix */
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);
578 memcpy (result
, unixroot
, 2);
579 strcpy (result
+ 2, pathname
);
586 /* Nothing to relocate. */
590 /* Returns the pathname, relocated according to the current installation
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). */
596 relocate2 (const char *pathname
, char **allocatedp
)
598 const char *result
= relocate (pathname
);
599 *allocatedp
= (result
!= pathname
? (char *) result
: NULL
);