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