gwenhywfar
4.3.3
|
00001 /* 00002 * BinReloc - a library for creating relocatable executables 00003 * Written by: Hongli Lai <h.lai@chello.nl> 00004 * http://autopackage.org/ 00005 * 00006 * This source code is public domain. You can relicense this code 00007 * under whatever license you want. 00008 * 00009 * See http://autopackage.org/docs/binreloc/ for 00010 * more information and how to use this. 00011 */ 00012 00013 #ifndef __BINRELOC_C__ 00014 #define __BINRELOC_C__ 00015 00016 #include "config.h" 00017 00018 #ifdef ENABLE_BINRELOC 00019 #include <sys/types.h> 00020 #include <sys/stat.h> 00021 #include <unistd.h> 00022 #endif /* ENABLE_BINRELOC */ 00023 #include <stdio.h> 00024 #include <stdlib.h> 00025 #include <limits.h> 00026 #include <string.h> 00027 #include "binreloc.h" 00028 00029 #ifdef __cplusplus 00030 extern "C" { 00031 #endif /* __cplusplus */ 00032 00033 00034 #ifdef OS_WIN32 00035 # define DIRSEP "\\" 00036 # define DIRSEP_C '\\' 00037 #else 00038 # define DIRSEP "/" 00039 # define DIRSEP_C '/' 00040 #endif 00041 00047 static char * 00048 _br_find_exe (BrInitError *error) 00049 { 00050 #ifndef ENABLE_BINRELOC 00051 if (error) 00052 *error = BR_INIT_ERROR_DISABLED; 00053 return NULL; 00054 #else 00055 char *path, *path2, *line, *result; 00056 size_t buf_size; 00057 ssize_t size; 00058 struct stat stat_buf; 00059 FILE *f; 00060 00061 /* Read from /proc/self/exe (symlink) */ 00062 if (sizeof (path) > SSIZE_MAX) 00063 buf_size = SSIZE_MAX - 1; 00064 else 00065 buf_size = PATH_MAX - 1; 00066 path = (char *) malloc (buf_size); 00067 if (path == NULL) { 00068 /* Cannot allocate memory. */ 00069 if (error) 00070 *error = BR_INIT_ERROR_NOMEM; 00071 return NULL; 00072 } 00073 path2 = (char *) malloc (buf_size); 00074 if (path2 == NULL) { 00075 /* Cannot allocate memory. */ 00076 if (error) 00077 *error = BR_INIT_ERROR_NOMEM; 00078 free (path); 00079 return NULL; 00080 } 00081 00082 strncpy (path2, "/proc/self/exe", buf_size - 1); 00083 00084 while (1) { 00085 int i; 00086 00087 size = readlink (path2, path, buf_size - 1); 00088 if (size == -1) { 00089 /* Error. */ 00090 free (path2); 00091 break; 00092 } 00093 00094 /* readlink() success. */ 00095 path[size] = '\0'; 00096 00097 /* Check whether the symlink's target is also a symlink. 00098 * We want to get the final target. */ 00099 i = stat (path, &stat_buf); 00100 if (i == -1) { 00101 /* Error. */ 00102 free (path2); 00103 break; 00104 } 00105 00106 /* stat() success. */ 00107 if (!S_ISLNK (stat_buf.st_mode)) { 00108 /* path is not a symlink. Done. */ 00109 free (path2); 00110 return path; 00111 } 00112 00113 /* path is a symlink. Continue loop and resolve this. */ 00114 strncpy (path, path2, buf_size - 1); 00115 } 00116 00117 00118 /* readlink() or stat() failed; this can happen when the program is 00119 * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */ 00120 00121 buf_size = PATH_MAX + 128; 00122 line = (char *) realloc (path, buf_size); 00123 if (line == NULL) { 00124 /* Cannot allocate memory. */ 00125 free (path); 00126 if (error) 00127 *error = BR_INIT_ERROR_NOMEM; 00128 return NULL; 00129 } 00130 00131 f = fopen ("/proc/self/maps", "r"); 00132 if (f == NULL) { 00133 free (line); 00134 if (error) 00135 *error = BR_INIT_ERROR_OPEN_MAPS; 00136 return NULL; 00137 } 00138 00139 /* The first entry should be the executable name. */ 00140 result = fgets (line, (int) buf_size, f); 00141 if (result == NULL) { 00142 fclose (f); 00143 free (line); 00144 if (error) 00145 *error = BR_INIT_ERROR_READ_MAPS; 00146 return NULL; 00147 } 00148 00149 /* Get rid of newline character. */ 00150 buf_size = strlen (line); 00151 if (buf_size <= 0) { 00152 /* Huh? An empty string? */ 00153 fclose (f); 00154 free (line); 00155 if (error) 00156 *error = BR_INIT_ERROR_INVALID_MAPS; 00157 return NULL; 00158 } 00159 if (line[buf_size - 1] == 10) 00160 line[buf_size - 1] = 0; 00161 00162 /* Extract the filename; it is always an absolute path. */ 00163 path = strchr (line, DIRSEP_C); 00164 00165 /* Sanity check. */ 00166 if (strstr (line, " r-xp ") == NULL || path == NULL) { 00167 fclose (f); 00168 free (line); 00169 if (error) 00170 *error = BR_INIT_ERROR_INVALID_MAPS; 00171 return NULL; 00172 } 00173 00174 path = strdup (path); 00175 free (line); 00176 fclose (f); 00177 return path; 00178 #endif /* ENABLE_BINRELOC */ 00179 } 00180 00181 00186 static char * 00187 _br_find_exe_for_symbol (const void *symbol, BrInitError *error) 00188 { 00189 #ifndef ENABLE_BINRELOC 00190 if (error) 00191 *error = BR_INIT_ERROR_DISABLED; 00192 return (char *) NULL; 00193 #else 00194 #define SIZE PATH_MAX + 100 00195 FILE *f; 00196 size_t address_string_len; 00197 char *address_string, line[SIZE], *found; 00198 00199 if (symbol == NULL) 00200 return (char *) NULL; 00201 00202 f = fopen ("/proc/self/maps", "r"); 00203 if (f == NULL) 00204 return (char *) NULL; 00205 00206 address_string_len = 4; 00207 address_string = (char *) malloc (address_string_len); 00208 found = (char *) NULL; 00209 00210 while (!feof (f)) { 00211 char *start_addr, *end_addr, *end_addr_end, *file; 00212 void *start_addr_p, *end_addr_p; 00213 size_t len; 00214 00215 if (fgets (line, SIZE, f) == NULL) 00216 break; 00217 00218 /* Sanity check. */ 00219 if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL) 00220 continue; 00221 00222 /* Parse line. */ 00223 start_addr = line; 00224 end_addr = strchr (line, '-'); 00225 file = strchr (line, '/'); 00226 00227 /* More sanity check. */ 00228 if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-')) 00229 continue; 00230 00231 end_addr[0] = '\0'; 00232 end_addr++; 00233 end_addr_end = strchr (end_addr, ' '); 00234 if (end_addr_end == NULL) 00235 continue; 00236 00237 end_addr_end[0] = '\0'; 00238 len = strlen (file); 00239 if (len == 0) 00240 continue; 00241 if (file[len - 1] == '\n') 00242 file[len - 1] = '\0'; 00243 00244 /* Get rid of "(deleted)" from the filename. */ 00245 len = strlen (file); 00246 if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0) 00247 file[len - 10] = '\0'; 00248 00249 /* I don't know whether this can happen but better safe than sorry. */ 00250 len = strlen (start_addr); 00251 if (len != strlen (end_addr)) 00252 continue; 00253 00254 00255 /* Transform the addresses into a string in the form of 0xdeadbeef, 00256 * then transform that into a pointer. */ 00257 if (address_string_len < len + 3) { 00258 address_string_len = len + 3; 00259 address_string = (char *) realloc (address_string, address_string_len); 00260 } 00261 00262 memcpy (address_string, "0x", 2); 00263 memcpy (address_string + 2, start_addr, len); 00264 address_string[2 + len] = '\0'; 00265 sscanf (address_string, "%p", &start_addr_p); 00266 00267 memcpy (address_string, "0x", 2); 00268 memcpy (address_string + 2, end_addr, len); 00269 address_string[2 + len] = '\0'; 00270 sscanf (address_string, "%p", &end_addr_p); 00271 00272 00273 if (symbol >= start_addr_p && symbol < end_addr_p) { 00274 found = file; 00275 break; 00276 } 00277 } 00278 00279 free (address_string); 00280 fclose (f); 00281 00282 if (found == NULL) 00283 return (char *) NULL; 00284 else 00285 return strdup (found); 00286 #endif /* ENABLE_BINRELOC */ 00287 } 00288 00289 00290 #ifndef BINRELOC_RUNNING_DOXYGEN 00291 #undef NULL 00292 #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */ 00293 #endif 00294 00295 static char *exe = (char *) NULL; 00296 00297 00312 int 00313 br_init (BrInitError *error) 00314 { 00315 exe = _br_find_exe (error); 00316 return exe != NULL; 00317 } 00318 00319 00334 int 00335 br_init_lib (BrInitError *error) 00336 { 00337 exe = _br_find_exe_for_symbol ((const void *) "", error); 00338 return exe != NULL; 00339 } 00340 00341 00351 char * 00352 br_find_exe (const char *default_exe) 00353 { 00354 if (exe == (char *) NULL) { 00355 /* BinReloc is not initialized. */ 00356 if (default_exe != (const char *) NULL) 00357 return strdup (default_exe); 00358 else 00359 return (char *) NULL; 00360 } 00361 return strdup (exe); 00362 } 00363 00364 00379 char * 00380 br_find_exe_dir (const char *default_dir) 00381 { 00382 if (exe == NULL) { 00383 /* BinReloc not initialized. */ 00384 if (default_dir != NULL) 00385 return strdup (default_dir); 00386 else 00387 return NULL; 00388 } 00389 00390 return br_dirname (exe); 00391 } 00392 00393 00407 char * 00408 br_find_prefix (const char *default_prefix) 00409 { 00410 char *dir1, *dir2; 00411 00412 if (exe == (char *) NULL) { 00413 /* BinReloc not initialized. */ 00414 if (default_prefix != (const char *) NULL) 00415 return strdup (default_prefix); 00416 else 00417 return (char *) NULL; 00418 } 00419 00420 dir1 = br_dirname (exe); 00421 dir2 = br_dirname (dir1); 00422 free (dir1); 00423 return dir2; 00424 } 00425 00426 00440 char * 00441 br_find_bin_dir (const char *default_bin_dir) 00442 { 00443 char *prefix, *dir; 00444 00445 prefix = br_find_prefix ((const char *) NULL); 00446 if (prefix == (char *) NULL) { 00447 /* BinReloc not initialized. */ 00448 if (default_bin_dir != (const char *) NULL) 00449 return strdup (default_bin_dir); 00450 else 00451 return (char *) NULL; 00452 } 00453 00454 dir = br_build_path (prefix, "bin"); 00455 free (prefix); 00456 return dir; 00457 } 00458 00459 00473 char * 00474 br_find_sbin_dir (const char *default_sbin_dir) 00475 { 00476 char *prefix, *dir; 00477 00478 prefix = br_find_prefix ((const char *) NULL); 00479 if (prefix == (char *) NULL) { 00480 /* BinReloc not initialized. */ 00481 if (default_sbin_dir != (const char *) NULL) 00482 return strdup (default_sbin_dir); 00483 else 00484 return (char *) NULL; 00485 } 00486 00487 dir = br_build_path (prefix, "sbin"); 00488 free (prefix); 00489 return dir; 00490 } 00491 00492 00507 char * 00508 br_find_data_dir (const char *default_data_dir) 00509 { 00510 char *prefix, *dir; 00511 00512 prefix = br_find_prefix ((const char *) NULL); 00513 if (prefix == (char *) NULL) { 00514 /* BinReloc not initialized. */ 00515 if (default_data_dir != (const char *) NULL) 00516 return strdup (default_data_dir); 00517 else 00518 return (char *) NULL; 00519 } 00520 00521 dir = br_build_path (prefix, "share"); 00522 free (prefix); 00523 return dir; 00524 } 00525 00526 00540 char * 00541 br_find_locale_dir (const char *default_locale_dir) 00542 { 00543 char *data_dir, *dir; 00544 00545 data_dir = br_find_data_dir ((const char *) NULL); 00546 if (data_dir == (char *) NULL) { 00547 /* BinReloc not initialized. */ 00548 if (default_locale_dir != (const char *) NULL) 00549 return strdup (default_locale_dir); 00550 else 00551 return (char *) NULL; 00552 } 00553 00554 dir = br_build_path (data_dir, "locale"); 00555 free (data_dir); 00556 return dir; 00557 } 00558 00559 00573 char * 00574 br_find_lib_dir (const char *default_lib_dir) 00575 { 00576 char *prefix, *dir; 00577 00578 prefix = br_find_prefix ((const char *) NULL); 00579 if (prefix == (char *) NULL) { 00580 /* BinReloc not initialized. */ 00581 if (default_lib_dir != (const char *) NULL) 00582 return strdup (default_lib_dir); 00583 else 00584 return (char *) NULL; 00585 } 00586 00587 dir = br_build_path (prefix, "lib"); 00588 free (prefix); 00589 return dir; 00590 } 00591 00592 00606 char * 00607 br_find_libexec_dir (const char *default_libexec_dir) 00608 { 00609 char *prefix, *dir; 00610 00611 prefix = br_find_prefix ((const char *) NULL); 00612 if (prefix == (char *) NULL) { 00613 /* BinReloc not initialized. */ 00614 if (default_libexec_dir != (const char *) NULL) 00615 return strdup (default_libexec_dir); 00616 else 00617 return (char *) NULL; 00618 } 00619 00620 dir = br_build_path (prefix, "libexec"); 00621 free (prefix); 00622 return dir; 00623 } 00624 00625 00639 char * 00640 br_find_etc_dir (const char *default_etc_dir) 00641 { 00642 char *prefix, *dir; 00643 00644 prefix = br_find_prefix ((const char *) NULL); 00645 if (prefix == (char *) NULL) { 00646 /* BinReloc not initialized. */ 00647 if (default_etc_dir != (const char *) NULL) 00648 return strdup (default_etc_dir); 00649 else 00650 return (char *) NULL; 00651 } 00652 00653 dir = br_build_path (prefix, "etc"); 00654 free (prefix); 00655 return dir; 00656 } 00657 00658 00659 /*********************** 00660 * Utility functions 00661 ***********************/ 00662 00669 char * 00670 br_strcat (const char *str1, const char *str2) 00671 { 00672 char *result; 00673 size_t len1, len2; 00674 00675 if (str1 == NULL) 00676 str1 = ""; 00677 if (str2 == NULL) 00678 str2 = ""; 00679 00680 len1 = strlen (str1); 00681 len2 = strlen (str2); 00682 00683 result = (char *) malloc (len1 + len2 + 1); 00684 memcpy (result, str1, len1); 00685 memcpy (result + len1, str2, len2); 00686 result[len1 + len2] = '\0'; 00687 00688 return result; 00689 } 00690 00691 00692 char * 00693 br_build_path (const char *dir, const char *file) 00694 { 00695 char *dir2, *result; 00696 size_t len; 00697 int must_free = 0; 00698 00699 len = strlen (dir); 00700 if (len > 0 && dir[len - 1] != DIRSEP_C) { 00701 dir2 = br_strcat (dir, DIRSEP ); 00702 must_free = 1; 00703 } else 00704 dir2 = (char *) dir; 00705 00706 result = br_strcat (dir2, file); 00707 if (must_free) 00708 free (dir2); 00709 return result; 00710 } 00711 00712 00713 /* Emulates glibc's strndup() */ 00714 static char * 00715 br_strndup (const char *str, size_t size) 00716 { 00717 char *result = (char *) NULL; 00718 size_t len; 00719 00720 if (str == (const char *) NULL) 00721 return (char *) NULL; 00722 00723 len = strlen (str); 00724 if (len == 0) 00725 return strdup (""); 00726 if (size > len) 00727 size = len; 00728 00729 result = (char *) malloc (len + 1); 00730 memcpy (result, str, size); 00731 result[size] = '\0'; 00732 return result; 00733 } 00734 00735 00748 char * 00749 br_dirname (const char *path) 00750 { 00751 char *end, *result; 00752 00753 if (path == (const char *) NULL) 00754 return (char *) NULL; 00755 00756 end = strrchr (path, DIRSEP_C); 00757 if (end == (const char *) NULL) 00758 return strdup ("."); 00759 00760 while (end > path && *end == DIRSEP_C) 00761 end--; 00762 result = br_strndup (path, end - path + 1); 00763 if (result[0] == 0) { 00764 free (result); 00765 return strdup (DIRSEP); 00766 } else 00767 return result; 00768 } 00769 00770 00771 #ifdef __cplusplus 00772 } 00773 #endif /* __cplusplus */ 00774 00775 #endif /* __BINRELOC_C__ */