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