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