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