src/include/binreloc.c

Go to the documentation of this file.
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 #ifdef ENABLE_BINRELOC
00017         #include <sys/types.h>
00018         #include <sys/stat.h>
00019         #include <unistd.h>
00020 #endif /* ENABLE_BINRELOC */
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <limits.h>
00024 #include <string.h>
00025 #include "binreloc.h"
00026 
00027 #ifdef __cplusplus
00028 extern "C" {
00029 #endif /* __cplusplus */
00030 
00031 
00032 
00038 static char *
00039 _br_find_exe (BrInitError *error)
00040 {
00041 #ifndef ENABLE_BINRELOC
00042         if (error)
00043                 *error = BR_INIT_ERROR_DISABLED;
00044         return NULL;
00045 #else
00046         char *path, *path2, *line, *result;
00047         size_t buf_size;
00048         ssize_t size;
00049         struct stat stat_buf;
00050         FILE *f;
00051 
00052         /* Read from /proc/self/exe (symlink) */
00053         if (sizeof (path) > SSIZE_MAX)
00054                 buf_size = SSIZE_MAX - 1;
00055         else
00056                 buf_size = PATH_MAX - 1;
00057         path = (char *) malloc (buf_size);
00058         if (path == NULL) {
00059                 /* Cannot allocate memory. */
00060                 if (error)
00061                         *error = BR_INIT_ERROR_NOMEM;
00062                 return NULL;
00063         }
00064         path2 = (char *) malloc (buf_size);
00065         if (path2 == NULL) {
00066                 /* Cannot allocate memory. */
00067                 if (error)
00068                         *error = BR_INIT_ERROR_NOMEM;
00069                 free (path);
00070                 return NULL;
00071         }
00072 
00073         strncpy (path2, "/proc/self/exe", buf_size - 1);
00074 
00075         while (1) {
00076                 int i;
00077 
00078                 size = readlink (path2, path, buf_size - 1);
00079                 if (size == -1) {
00080                         /* Error. */
00081                         free (path2);
00082                         break;
00083                 }
00084 
00085                 /* readlink() success. */
00086                 path[size] = '\0';
00087 
00088                 /* Check whether the symlink's target is also a symlink.
00089                  * We want to get the final target. */
00090                 i = stat (path, &stat_buf);
00091                 if (i == -1) {
00092                         /* Error. */
00093                         free (path2);
00094                         break;
00095                 }
00096 
00097                 /* stat() success. */
00098                 if (!S_ISLNK (stat_buf.st_mode)) {
00099                         /* path is not a symlink. Done. */
00100                         free (path2);
00101                         return path;
00102                 }
00103 
00104                 /* path is a symlink. Continue loop and resolve this. */
00105                 strncpy (path, path2, buf_size - 1);
00106         }
00107 
00108 
00109         /* readlink() or stat() failed; this can happen when the program is
00110          * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
00111 
00112         buf_size = PATH_MAX + 128;
00113         line = (char *) realloc (path, buf_size);
00114         if (line == NULL) {
00115                 /* Cannot allocate memory. */
00116                 free (path);
00117                 if (error)
00118                         *error = BR_INIT_ERROR_NOMEM;
00119                 return NULL;
00120         }
00121 
00122         f = fopen ("/proc/self/maps", "r");
00123         if (f == NULL) {
00124                 free (line);
00125                 if (error)
00126                         *error = BR_INIT_ERROR_OPEN_MAPS;
00127                 return NULL;
00128         }
00129 
00130         /* The first entry should be the executable name. */
00131         result = fgets (line, (int) buf_size, f);
00132         if (result == NULL) {
00133                 fclose (f);
00134                 free (line);
00135                 if (error)
00136                         *error = BR_INIT_ERROR_READ_MAPS;
00137                 return NULL;
00138         }
00139 
00140         /* Get rid of newline character. */
00141         buf_size = strlen (line);
00142         if (buf_size <= 0) {
00143                 /* Huh? An empty string? */
00144                 fclose (f);
00145                 free (line);
00146                 if (error)
00147                         *error = BR_INIT_ERROR_INVALID_MAPS;
00148                 return NULL;
00149         }
00150         if (line[buf_size - 1] == 10)
00151                 line[buf_size - 1] = 0;
00152 
00153         /* Extract the filename; it is always an absolute path. */
00154         path = strchr (line, '/');
00155 
00156         /* Sanity check. */
00157         if (strstr (line, " r-xp ") == NULL || path == NULL) {
00158                 fclose (f);
00159                 free (line);
00160                 if (error)
00161                         *error = BR_INIT_ERROR_INVALID_MAPS;
00162                 return NULL;
00163         }
00164 
00165         path = strdup (path);
00166         free (line);
00167         fclose (f);
00168         return path;
00169 #endif /* ENABLE_BINRELOC */
00170 }
00171 
00172 
00177 static char *
00178 _br_find_exe_for_symbol (const void *symbol, BrInitError *error)
00179 {
00180 #ifndef ENABLE_BINRELOC
00181         if (error)
00182                 *error = BR_INIT_ERROR_DISABLED;
00183         return (char *) NULL;
00184 #else
00185         #define SIZE PATH_MAX + 100
00186         FILE *f;
00187         size_t address_string_len;
00188         char *address_string, line[SIZE], *found;
00189 
00190         if (symbol == NULL)
00191                 return (char *) NULL;
00192 
00193         f = fopen ("/proc/self/maps", "r");
00194         if (f == NULL)
00195                 return (char *) NULL;
00196 
00197         address_string_len = 4;
00198         address_string = (char *) malloc (address_string_len);
00199         found = (char *) NULL;
00200 
00201         while (!feof (f)) {
00202                 char *start_addr, *end_addr, *end_addr_end, *file;
00203                 void *start_addr_p, *end_addr_p;
00204                 size_t len;
00205 
00206                 if (fgets (line, SIZE, f) == NULL)
00207                         break;
00208 
00209                 /* Sanity check. */
00210                 if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
00211                         continue;
00212 
00213                 /* Parse line. */
00214                 start_addr = line;
00215                 end_addr = strchr (line, '-');
00216                 file = strchr (line, '/');
00217 
00218                 /* More sanity check. */
00219                 if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
00220                         continue;
00221 
00222                 end_addr[0] = '\0';
00223                 end_addr++;
00224                 end_addr_end = strchr (end_addr, ' ');
00225                 if (end_addr_end == NULL)
00226                         continue;
00227 
00228                 end_addr_end[0] = '\0';
00229                 len = strlen (file);
00230                 if (len == 0)
00231                         continue;
00232                 if (file[len - 1] == '\n')
00233                         file[len - 1] = '\0';
00234 
00235                 /* Get rid of "(deleted)" from the filename. */
00236                 len = strlen (file);
00237                 if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
00238                         file[len - 10] = '\0';
00239 
00240                 /* I don't know whether this can happen but better safe than sorry. */
00241                 len = strlen (start_addr);
00242                 if (len != strlen (end_addr))
00243                         continue;
00244 
00245 
00246                 /* Transform the addresses into a string in the form of 0xdeadbeef,
00247                  * then transform that into a pointer. */
00248                 if (address_string_len < len + 3) {
00249                         address_string_len = len + 3;
00250                         address_string = (char *) realloc (address_string, address_string_len);
00251                 }
00252 
00253                 memcpy (address_string, "0x", 2);
00254                 memcpy (address_string + 2, start_addr, len);
00255                 address_string[2 + len] = '\0';
00256                 sscanf (address_string, "%p", &start_addr_p);
00257 
00258                 memcpy (address_string, "0x", 2);
00259                 memcpy (address_string + 2, end_addr, len);
00260                 address_string[2 + len] = '\0';
00261                 sscanf (address_string, "%p", &end_addr_p);
00262 
00263 
00264                 if (symbol >= start_addr_p && symbol < end_addr_p) {
00265                         found = file;
00266                         break;
00267                 }
00268         }
00269 
00270         free (address_string);
00271         fclose (f);
00272 
00273         if (found == NULL)
00274                 return (char *) NULL;
00275         else
00276                 return strdup (found);
00277 #endif /* ENABLE_BINRELOC */
00278 }
00279 
00280 
00281 #ifndef BINRELOC_RUNNING_DOXYGEN
00282         #undef NULL
00283         #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
00284 #endif
00285 
00286 static char *exe = (char *) NULL;
00287 
00288 
00303 int
00304 br_init (BrInitError *error)
00305 {
00306         exe = _br_find_exe (error);
00307         return exe != NULL;
00308 }
00309 
00310 
00325 int
00326 br_init_lib (BrInitError *error)
00327 {
00328         exe = _br_find_exe_for_symbol ((const void *) "", error);
00329         return exe != NULL;
00330 }
00331 
00332 
00342 char *
00343 br_find_exe (const char *default_exe)
00344 {
00345         if (exe == (char *) NULL) {
00346                 /* BinReloc is not initialized. */
00347                 if (default_exe != (const char *) NULL)
00348                         return strdup (default_exe);
00349                 else
00350                         return (char *) NULL;
00351         }
00352         return strdup (exe);
00353 }
00354 
00355 
00370 char *
00371 br_find_exe_dir (const char *default_dir)
00372 {
00373         if (exe == NULL) {
00374                 /* BinReloc not initialized. */
00375                 if (default_dir != NULL)
00376                         return strdup (default_dir);
00377                 else
00378                         return NULL;
00379         }
00380 
00381         return br_dirname (exe);
00382 }
00383 
00384 
00398 char *
00399 br_find_prefix (const char *default_prefix)
00400 {
00401         char *dir1, *dir2;
00402 
00403         if (exe == (char *) NULL) {
00404                 /* BinReloc not initialized. */
00405                 if (default_prefix != (const char *) NULL)
00406                         return strdup (default_prefix);
00407                 else
00408                         return (char *) NULL;
00409         }
00410 
00411         dir1 = br_dirname (exe);
00412         dir2 = br_dirname (dir1);
00413         free (dir1);
00414         return dir2;
00415 }
00416 
00417 
00431 char *
00432 br_find_bin_dir (const char *default_bin_dir)
00433 {
00434         char *prefix, *dir;
00435 
00436         prefix = br_find_prefix ((const char *) NULL);
00437         if (prefix == (char *) NULL) {
00438                 /* BinReloc not initialized. */
00439                 if (default_bin_dir != (const char *) NULL)
00440                         return strdup (default_bin_dir);
00441                 else
00442                         return (char *) NULL;
00443         }
00444 
00445         dir = br_build_path (prefix, "bin");
00446         free (prefix);
00447         return dir;
00448 }
00449 
00450 
00464 char *
00465 br_find_sbin_dir (const char *default_sbin_dir)
00466 {
00467         char *prefix, *dir;
00468 
00469         prefix = br_find_prefix ((const char *) NULL);
00470         if (prefix == (char *) NULL) {
00471                 /* BinReloc not initialized. */
00472                 if (default_sbin_dir != (const char *) NULL)
00473                         return strdup (default_sbin_dir);
00474                 else
00475                         return (char *) NULL;
00476         }
00477 
00478         dir = br_build_path (prefix, "sbin");
00479         free (prefix);
00480         return dir;
00481 }
00482 
00483 
00498 char *
00499 br_find_data_dir (const char *default_data_dir)
00500 {
00501         char *prefix, *dir;
00502 
00503         prefix = br_find_prefix ((const char *) NULL);
00504         if (prefix == (char *) NULL) {
00505                 /* BinReloc not initialized. */
00506                 if (default_data_dir != (const char *) NULL)
00507                         return strdup (default_data_dir);
00508                 else
00509                         return (char *) NULL;
00510         }
00511 
00512         dir = br_build_path (prefix, "share");
00513         free (prefix);
00514         return dir;
00515 }
00516 
00517 
00531 char *
00532 br_find_locale_dir (const char *default_locale_dir)
00533 {
00534         char *data_dir, *dir;
00535 
00536         data_dir = br_find_data_dir ((const char *) NULL);
00537         if (data_dir == (char *) NULL) {
00538                 /* BinReloc not initialized. */
00539                 if (default_locale_dir != (const char *) NULL)
00540                         return strdup (default_locale_dir);
00541                 else
00542                         return (char *) NULL;
00543         }
00544 
00545         dir = br_build_path (data_dir, "locale");
00546         free (data_dir);
00547         return dir;
00548 }
00549 
00550 
00564 char *
00565 br_find_lib_dir (const char *default_lib_dir)
00566 {
00567         char *prefix, *dir;
00568 
00569         prefix = br_find_prefix ((const char *) NULL);
00570         if (prefix == (char *) NULL) {
00571                 /* BinReloc not initialized. */
00572                 if (default_lib_dir != (const char *) NULL)
00573                         return strdup (default_lib_dir);
00574                 else
00575                         return (char *) NULL;
00576         }
00577 
00578         dir = br_build_path (prefix, "lib");
00579         free (prefix);
00580         return dir;
00581 }
00582 
00583 
00597 char *
00598 br_find_libexec_dir (const char *default_libexec_dir)
00599 {
00600         char *prefix, *dir;
00601 
00602         prefix = br_find_prefix ((const char *) NULL);
00603         if (prefix == (char *) NULL) {
00604                 /* BinReloc not initialized. */
00605                 if (default_libexec_dir != (const char *) NULL)
00606                         return strdup (default_libexec_dir);
00607                 else
00608                         return (char *) NULL;
00609         }
00610 
00611         dir = br_build_path (prefix, "libexec");
00612         free (prefix);
00613         return dir;
00614 }
00615 
00616 
00630 char *
00631 br_find_etc_dir (const char *default_etc_dir)
00632 {
00633         char *prefix, *dir;
00634 
00635         prefix = br_find_prefix ((const char *) NULL);
00636         if (prefix == (char *) NULL) {
00637                 /* BinReloc not initialized. */
00638                 if (default_etc_dir != (const char *) NULL)
00639                         return strdup (default_etc_dir);
00640                 else
00641                         return (char *) NULL;
00642         }
00643 
00644         dir = br_build_path (prefix, "etc");
00645         free (prefix);
00646         return dir;
00647 }
00648 
00649 
00650 /***********************
00651  * Utility functions
00652  ***********************/
00653 
00660 char *
00661 br_strcat (const char *str1, const char *str2)
00662 {
00663         char *result;
00664         size_t len1, len2;
00665 
00666         if (str1 == NULL)
00667                 str1 = "";
00668         if (str2 == NULL)
00669                 str2 = "";
00670 
00671         len1 = strlen (str1);
00672         len2 = strlen (str2);
00673 
00674         result = (char *) malloc (len1 + len2 + 1);
00675         memcpy (result, str1, len1);
00676         memcpy (result + len1, str2, len2);
00677         result[len1 + len2] = '\0';
00678 
00679         return result;
00680 }
00681 
00682 
00683 char *
00684 br_build_path (const char *dir, const char *file)
00685 {
00686         char *dir2, *result;
00687         size_t len;
00688         int must_free = 0;
00689 
00690         len = strlen (dir);
00691         if (len > 0 && dir[len - 1] != '/') {
00692                 dir2 = br_strcat (dir, "/");
00693                 must_free = 1;
00694         } else
00695                 dir2 = (char *) dir;
00696 
00697         result = br_strcat (dir2, file);
00698         if (must_free)
00699                 free (dir2);
00700         return result;
00701 }
00702 
00703 
00704 /* Emulates glibc's strndup() */
00705 static char *
00706 br_strndup (const char *str, size_t size)
00707 {
00708         char *result = (char *) NULL;
00709         size_t len;
00710 
00711         if (str == (const char *) NULL)
00712                 return (char *) NULL;
00713 
00714         len = strlen (str);
00715         if (len == 0)
00716                 return strdup ("");
00717         if (size > len)
00718                 size = len;
00719 
00720         result = (char *) malloc (len + 1);
00721         memcpy (result, str, size);
00722         result[size] = '\0';
00723         return result;
00724 }
00725 
00726 
00739 char *
00740 br_dirname (const char *path)
00741 {
00742         char *end, *result;
00743 
00744         if (path == (const char *) NULL)
00745                 return (char *) NULL;
00746 
00747         end = strrchr (path, '/');
00748         if (end == (const char *) NULL)
00749                 return strdup (".");
00750 
00751         while (end > path && *end == '/')
00752                 end--;
00753         result = br_strndup (path, end - path + 1);
00754         if (result[0] == 0) {
00755                 free (result);
00756                 return strdup ("/");
00757         } else
00758                 return result;
00759 }
00760 
00761 
00762 #ifdef __cplusplus
00763 }
00764 #endif /* __cplusplus */
00765 
00766 #endif /* __BINRELOC_C__ */

Generated on Mon Jan 1 13:10:57 2007 for Wormux by  doxygen 1.4.7