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