1/* Provide relocatable packages. 2 Copyright (C) 2003-2006, 2008-2011 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#define _GL_USE_STDLIB_ALLOC 1 29#include <config.h> 30 31/* Specification. */ 32#include "relocatable.h" 33 34#if ENABLE_RELOCATABLE 35 36#include <stddef.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40 41#ifdef NO_XMALLOC 42# define xmalloc malloc 43#else 44# include "xalloc.h" 45#endif 46 47#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__ 48# define WIN32_LEAN_AND_MEAN 49# include <windows.h> 50#endif 51 52#if DEPENDS_ON_LIBCHARSET 53# include <libcharset.h> 54#endif 55#if DEPENDS_ON_LIBICONV && HAVE_ICONV 56# include <iconv.h> 57#endif 58#if DEPENDS_ON_LIBINTL && ENABLE_NLS 59# include <libintl.h> 60#endif 61 62/* Faked cheap 'bool'. */ 63#undef bool 64#undef false 65#undef true 66#define bool int 67#define false 0 68#define true 1 69 70/* Pathname support. 71 ISSLASH(C) tests whether C is a directory separator character. 72 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification. 73 */ 74#if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__ 75 /* Win32, OS/2, DOS */ 76# define ISSLASH(C) ((C) == '/' || (C) == '\\') 77# define HAS_DEVICE(P) \ 78 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ 79 && (P)[1] == ':') 80# define IS_PATH_WITH_DIR(P) \ 81 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P)) 82# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0) 83#else 84 /* Unix */ 85# define ISSLASH(C) ((C) == '/') 86# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL) 87# define FILE_SYSTEM_PREFIX_LEN(P) 0 88#endif 89 90/* Original installation prefix. */ 91static char *orig_prefix; 92static size_t orig_prefix_len; 93/* Current installation prefix. */ 94static char *curr_prefix; 95static size_t curr_prefix_len; 96/* These prefixes do not end in a slash. Anything that will be concatenated 97 to them must start with a slash. */ 98 99/* Sets the original and the current installation prefix of this module. 100 Relocation simply replaces a pathname starting with the original prefix 101 by the corresponding pathname with the current prefix instead. Both 102 prefixes should be directory names without trailing slash (i.e. use "" 103 instead of "/"). */ 104static void 105set_this_relocation_prefix (const char *orig_prefix_arg, 106 const char *curr_prefix_arg) 107{ 108 if (orig_prefix_arg != NULL && curr_prefix_arg != NULL 109 /* Optimization: if orig_prefix and curr_prefix are equal, the 110 relocation is a nop. */ 111 && strcmp (orig_prefix_arg, curr_prefix_arg) != 0) 112 { 113 /* Duplicate the argument strings. */ 114 char *memory; 115 116 orig_prefix_len = strlen (orig_prefix_arg); 117 curr_prefix_len = strlen (curr_prefix_arg); 118 memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1); 119#ifdef NO_XMALLOC 120 if (memory != NULL) 121#endif 122 { 123 memcpy (memory, orig_prefix_arg, orig_prefix_len + 1); 124 orig_prefix = memory; 125 memory += orig_prefix_len + 1; 126 memcpy (memory, curr_prefix_arg, curr_prefix_len + 1); 127 curr_prefix = memory; 128 return; 129 } 130 } 131 orig_prefix = NULL; 132 curr_prefix = NULL; 133 /* Don't worry about wasted memory here - this function is usually only 134 called once. */ 135} 136 137/* Sets the original and the current installation prefix of the package. 138 Relocation simply replaces a pathname starting with the original prefix 139 by the corresponding pathname with the current prefix instead. Both 140 prefixes should be directory names without trailing slash (i.e. use "" 141 instead of "/"). */ 142void 143set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg) 144{ 145 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg); 146 147 /* Now notify all dependent libraries. */ 148#if DEPENDS_ON_LIBCHARSET 149 libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); 150#endif 151#if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109 152 libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); 153#endif 154#if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix 155 libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); 156#endif 157} 158 159#if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR) 160 161/* Convenience function: 162 Computes the current installation prefix, based on the original 163 installation prefix, the original installation directory of a particular 164 file, and the current pathname of this file. 165 Returns it, freshly allocated. Returns NULL upon failure. */ 166#ifdef IN_LIBRARY 167#define compute_curr_prefix local_compute_curr_prefix 168static 169#endif 170char * 171compute_curr_prefix (const char *orig_installprefix, 172 const char *orig_installdir, 173 const char *curr_pathname) 174{ 175 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 file system 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 file system */ 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 { 260 /* Unexpected: The curr_installdir does not end with rel_installdir. */ 261 free (curr_installdir); 262 return NULL; 263 } 264 265 { 266 size_t curr_prefix_len = cp - curr_installdir; 267 char *curr_prefix; 268 269 curr_prefix = (char *) xmalloc (curr_prefix_len + 1); 270#ifdef NO_XMALLOC 271 if (curr_prefix == NULL) 272 { 273 free (curr_installdir); 274 return NULL; 275 } 276#endif 277 memcpy (curr_prefix, curr_installdir, curr_prefix_len); 278 curr_prefix[curr_prefix_len] = '\0'; 279 280 free (curr_installdir); 281 282 return curr_prefix; 283 } 284 } 285} 286 287#endif /* !IN_LIBRARY || PIC */ 288 289#if defined PIC && defined INSTALLDIR 290 291/* Full pathname of shared library, or NULL. */ 292static char *shared_library_fullname; 293 294#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__ 295/* Native Win32 only. 296 On Cygwin, it is better to use the Cygwin provided /proc interface, than 297 to use native Win32 API and cygwin_conv_to_posix_path, because it supports 298 longer file names 299 (see <http://cygwin.com/ml/cygwin/2011-01/msg00410.html>). */ 300 301/* Determine the full pathname of the shared library when it is loaded. */ 302 303BOOL WINAPI 304DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved) 305{ 306 (void) reserved; 307 308 if (event == DLL_PROCESS_ATTACH) 309 { 310 /* The DLL is being loaded into an application's address range. */ 311 static char location[MAX_PATH]; 312 313 if (!GetModuleFileName (module_handle, location, sizeof (location))) 314 /* Shouldn't happen. */ 315 return FALSE; 316 317 if (!IS_PATH_WITH_DIR (location)) 318 /* Shouldn't happen. */ 319 return FALSE; 320 321 shared_library_fullname = strdup (location); 322 } 323 324 return TRUE; 325} 326 327#else /* Unix */ 328 329static void 330find_shared_library_fullname () 331{ 332#if (defined __linux__ && (__GLIBC__ >= 2 || defined __UCLIBC__)) || defined __CYGWIN__ 333 /* Linux has /proc/self/maps. glibc 2 and uClibc have the getline() 334 function. 335 Cygwin >= 1.5 has /proc/self/maps and the getline() function too. */ 336 FILE *fp; 337 338 /* Open the current process' maps file. It describes one VMA per line. */ 339 fp = fopen ("/proc/self/maps", "r"); 340 if (fp) 341 { 342 unsigned long address = (unsigned long) &find_shared_library_fullname; 343 for (;;) 344 { 345 unsigned long start, end; 346 int c; 347 348 if (fscanf (fp, "%lx-%lx", &start, &end) != 2) 349 break; 350 if (address >= start && address <= end - 1) 351 { 352 /* Found it. Now see if this line contains a filename. */ 353 while (c = getc (fp), c != EOF && c != '\n' && c != '/') 354 continue; 355 if (c == '/') 356 { 357 size_t size; 358 int len; 359 360 ungetc (c, fp); 361 shared_library_fullname = NULL; size = 0; 362 len = getline (&shared_library_fullname, &size, fp); 363 if (len >= 0) 364 { 365 /* Success: filled shared_library_fullname. */ 366 if (len > 0 && shared_library_fullname[len - 1] == '\n') 367 shared_library_fullname[len - 1] = '\0'; 368 } 369 } 370 break; 371 } 372 while (c = getc (fp), c != EOF && c != '\n') 373 continue; 374 } 375 fclose (fp); 376 } 377#endif 378} 379 380#endif /* WIN32 / Unix */ 381 382/* Return the full pathname of the current shared library. 383 Return NULL if unknown. 384 Guaranteed to work only on Linux, Cygwin and Woe32. */ 385static char * 386get_shared_library_fullname () 387{ 388#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) 389 static bool tried_find_shared_library_fullname; 390 if (!tried_find_shared_library_fullname) 391 { 392 find_shared_library_fullname (); 393 tried_find_shared_library_fullname = true; 394 } 395#endif 396 return shared_library_fullname; 397} 398 399#endif /* PIC */ 400 401/* Returns the pathname, relocated according to the current installation 402 directory. 403 The returned string is either PATHNAME unmodified or a freshly allocated 404 string that you can free with free() after casting it to 'char *'. */ 405const char * 406relocate (const char *pathname) 407{ 408#if defined PIC && defined INSTALLDIR 409 static int initialized; 410 411 /* Initialization code for a shared library. */ 412 if (!initialized) 413 { 414 /* At this point, orig_prefix and curr_prefix likely have already been 415 set through the main program's set_program_name_and_installdir 416 function. This is sufficient in the case that the library has 417 initially been installed in the same orig_prefix. But we can do 418 better, to also cover the cases that 1. it has been installed 419 in a different prefix before being moved to orig_prefix and (later) 420 to curr_prefix, 2. unlike the program, it has not moved away from 421 orig_prefix. */ 422 const char *orig_installprefix = INSTALLPREFIX; 423 const char *orig_installdir = INSTALLDIR; 424 char *curr_prefix_better; 425 426 curr_prefix_better = 427 compute_curr_prefix (orig_installprefix, orig_installdir, 428 get_shared_library_fullname ()); 429 430 set_relocation_prefix (orig_installprefix, 431 curr_prefix_better != NULL 432 ? curr_prefix_better 433 : curr_prefix); 434 435 if (curr_prefix_better != NULL) 436 free (curr_prefix_better); 437 438 initialized = 1; 439 } 440#endif 441 442 /* Note: It is not necessary to perform case insensitive comparison here, 443 even for DOS-like file systems, because the pathname argument was 444 typically created from the same Makefile variable as orig_prefix came 445 from. */ 446 if (orig_prefix != NULL && curr_prefix != NULL 447 && strncmp (pathname, orig_prefix, orig_prefix_len) == 0) 448 { 449 if (pathname[orig_prefix_len] == '\0') 450 { 451 /* pathname equals orig_prefix. */ 452 char *result = (char *) xmalloc (strlen (curr_prefix) + 1); 453 454#ifdef NO_XMALLOC 455 if (result != NULL) 456#endif 457 { 458 strcpy (result, curr_prefix); 459 return result; 460 } 461 } 462 else if (ISSLASH (pathname[orig_prefix_len])) 463 { 464 /* pathname starts with orig_prefix. */ 465 const char *pathname_tail = &pathname[orig_prefix_len]; 466 char *result = 467 (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1); 468 469#ifdef NO_XMALLOC 470 if (result != NULL) 471#endif 472 { 473 memcpy (result, curr_prefix, curr_prefix_len); 474 strcpy (result + curr_prefix_len, pathname_tail); 475 return result; 476 } 477 } 478 } 479 /* Nothing to relocate. */ 480 return pathname; 481} 482 483#endif 484