binreloc.c

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 #include "config.h"
00016 
00017 #ifdef ENABLE_BINRELOC
00018         #include <sys/types.h>
00019         #include <sys/stat.h>
00020         #include <unistd.h>
00021 #endif /* ENABLE_BINRELOC */
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <limits.h>
00025 #include <string.h>
00026 #include "binreloc.h"
00027 #include <glib.h>
00028 
00029 G_BEGIN_DECLS
00030 
00031 
00037 static char *
00038 _br_find_exe (GbrInitError *error)
00039 {
00040 #ifndef ENABLE_BINRELOC
00041         if (error)
00042                 *error = GBR_INIT_ERROR_DISABLED;
00043         return NULL;
00044 #else
00045 #ifdef G_OS_WIN32
00046         /* I *thought* this program code already included the
00047            relocation code for windows. Unfortunately this is not
00048            the case and we have to add this manually. This is only
00049            one possibility; other ways of looking up the full path
00050            of gnucash-bin.exe probably exist.*/
00051         gchar *prefix;
00052         gchar *result;
00053 
00054         /* From the glib docs: This function looks in the Windows
00055            Registry for the value #InstallationDirectory in the key
00056            #HKLM\Software\${package}, and if that value exists and is
00057            a string, returns that. */
00058         prefix = g_win32_get_package_installation_directory ("GnuCash", NULL);
00059         result = g_build_filename (prefix,
00060                                    "bin", "gnucash-bin.exe",
00061                                    (char*)NULL);
00062         g_free (prefix);
00063         return result;
00064 #else
00065         char *path, *path2, *line, *result;
00066         size_t buf_size;
00067         ssize_t size;
00068         struct stat stat_buf;
00069         FILE *f;
00070 
00071         /* Read from /proc/self/exe (symlink) */
00072         if (sizeof (path) > SSIZE_MAX)
00073                 buf_size = SSIZE_MAX - 1;
00074         else
00075                 buf_size = PATH_MAX - 1;
00076         path = (char *) g_try_malloc (buf_size);
00077         if (path == NULL) {
00078                 /* Cannot allocate memory. */
00079                 if (error)
00080                         *error = GBR_INIT_ERROR_NOMEM;
00081                 return NULL;
00082         }
00083         path2 = (char *) g_try_malloc (buf_size);
00084         if (path2 == NULL) {
00085                 /* Cannot allocate memory. */
00086                 if (error)
00087                         *error = GBR_INIT_ERROR_NOMEM;
00088                 g_free (path);
00089                 return NULL;
00090         }
00091 
00092         strncpy (path2, "/proc/self/exe", buf_size - 1);
00093 
00094         while (1) {
00095                 int i;
00096 
00097                 size = readlink (path2, path, buf_size - 1);
00098                 if (size == -1) {
00099                         /* Error. */
00100                         g_free (path2);
00101                         break;
00102                 }
00103 
00104                 /* readlink() success. */
00105                 path[size] = '\0';
00106 
00107                 /* Check whether the symlink's target is also a symlink.
00108                  * We want to get the final target. */
00109                 i = stat (path, &stat_buf);
00110                 if (i == -1) {
00111                         /* Error. */
00112                         g_free (path2);
00113                         break;
00114                 }
00115 
00116                 /* stat() success. */
00117                 if (!S_ISLNK (stat_buf.st_mode)) {
00118                         /* path is not a symlink. Done. */
00119                         g_free (path2);
00120                         return path;
00121                 }
00122 
00123                 /* path is a symlink. Continue loop and resolve this. */
00124                 strncpy (path, path2, buf_size - 1);
00125         }
00126 
00127 
00128         /* readlink() or stat() failed; this can happen when the program is
00129          * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
00130 
00131         buf_size = PATH_MAX + 128;
00132         line = (char *) g_try_realloc (path, buf_size);
00133         if (line == NULL) {
00134                 /* Cannot allocate memory. */
00135                 g_free (path);
00136                 if (error)
00137                         *error = GBR_INIT_ERROR_NOMEM;
00138                 return NULL;
00139         }
00140 
00141         f = fopen ("/proc/self/maps", "r");
00142         if (f == NULL) {
00143                 g_free (line);
00144                 if (error)
00145                         *error = GBR_INIT_ERROR_OPEN_MAPS;
00146                 return NULL;
00147         }
00148 
00149         /* The first entry should be the executable name. */
00150         result = fgets (line, (int) buf_size, f);
00151         if (result == NULL) {
00152                 fclose (f);
00153                 g_free (line);
00154                 if (error)
00155                         *error = GBR_INIT_ERROR_READ_MAPS;
00156                 return NULL;
00157         }
00158 
00159         /* Get rid of newline character. */
00160         buf_size = strlen (line);
00161         if (buf_size <= 0) {
00162                 /* Huh? An empty string? */
00163                 fclose (f);
00164                 g_free (line);
00165                 if (error)
00166                         *error = GBR_INIT_ERROR_INVALID_MAPS;
00167                 return NULL;
00168         }
00169         if (line[buf_size - 1] == 10)
00170                 line[buf_size - 1] = 0;
00171 
00172         /* Extract the filename; it is always an absolute path. */
00173         path = strchr (line, '/');
00174 
00175         /* Sanity check. */
00176         if (strstr (line, " r-xp ") == NULL || path == NULL) {
00177                 fclose (f);
00178                 g_free (line);
00179                 if (error)
00180                         *error = GBR_INIT_ERROR_INVALID_MAPS;
00181                 return NULL;
00182         }
00183 
00184         path = g_strdup (path);
00185         g_free (line);
00186         fclose (f);
00187         return path;
00188 #endif /* G_OS_WINDOWS */
00189 #endif /* ENABLE_BINRELOC */
00190 }
00191 
00192 
00197 static char *
00198 _br_find_exe_for_symbol (const void *symbol, GbrInitError *error)
00199 {
00200 #ifndef ENABLE_BINRELOC
00201         if (error)
00202                 *error = GBR_INIT_ERROR_DISABLED;
00203         return (char *) NULL;
00204 #else
00205 #ifdef G_OS_WIN32
00206         g_warning ("_br_find_exe_for_symbol not implemented on win32.");
00207         if (error)
00208                 *error = GBR_INIT_ERROR_DISABLED;
00209         return (char *) NULL;
00210 #else
00211         #define SIZE PATH_MAX + 100
00212         FILE *f;
00213         size_t address_string_len;
00214         char *address_string, line[SIZE], *found;
00215 
00216         if (symbol == NULL)
00217                 return (char *) NULL;
00218 
00219         f = fopen ("/proc/self/maps", "r");
00220         if (f == NULL)
00221                 return (char *) NULL;
00222 
00223         address_string_len = 4;
00224         address_string = (char *) g_try_malloc (address_string_len);
00225         found = (char *) NULL;
00226 
00227         while (!feof (f)) {
00228                 char *start_addr, *end_addr, *end_addr_end, *file;
00229                 void *start_addr_p, *end_addr_p;
00230                 size_t len;
00231 
00232                 if (fgets (line, SIZE, f) == NULL)
00233                         break;
00234 
00235                 /* Sanity check. */
00236                 if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
00237                         continue;
00238 
00239                 /* Parse line. */
00240                 start_addr = line;
00241                 end_addr = strchr (line, '-');
00242                 file = strchr (line, '/');
00243 
00244                 /* More sanity check. */
00245                 if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
00246                         continue;
00247 
00248                 end_addr[0] = '\0';
00249                 end_addr++;
00250                 end_addr_end = strchr (end_addr, ' ');
00251                 if (end_addr_end == NULL)
00252                         continue;
00253 
00254                 end_addr_end[0] = '\0';
00255                 len = strlen (file);
00256                 if (len == 0)
00257                         continue;
00258                 if (file[len - 1] == '\n')
00259                         file[len - 1] = '\0';
00260 
00261                 /* Get rid of "(deleted)" from the filename. */
00262                 len = strlen (file);
00263                 if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
00264                         file[len - 10] = '\0';
00265 
00266                 /* I don't know whether this can happen but better safe than sorry. */
00267                 len = strlen (start_addr);
00268                 if (len != strlen (end_addr))
00269                         continue;
00270 
00271 
00272                 /* Transform the addresses into a string in the form of 0xdeadbeef,
00273                  * then transform that into a pointer. */
00274                 if (address_string_len < len + 3) {
00275                         address_string_len = len + 3;
00276                         address_string = (char *) g_try_realloc (address_string, address_string_len);
00277                 }
00278 
00279                 memcpy (address_string, "0x", 2);
00280                 memcpy (address_string + 2, start_addr, len);
00281                 address_string[2 + len] = '\0';
00282                 sscanf (address_string, "%p", &start_addr_p);
00283 
00284                 memcpy (address_string, "0x", 2);
00285                 memcpy (address_string + 2, end_addr, len);
00286                 address_string[2 + len] = '\0';
00287                 sscanf (address_string, "%p", &end_addr_p);
00288 
00289 
00290                 if (symbol >= start_addr_p && symbol < end_addr_p) {
00291                         found = file;
00292                         break;
00293                 }
00294         }
00295 
00296         g_free (address_string);
00297         fclose (f);
00298 
00299         if (found == NULL)
00300                 return (char *) NULL;
00301         else
00302                 return g_strdup (found);
00303 #endif /* G_OS_WIN32 */
00304 #endif /* ENABLE_BINRELOC */
00305 }
00306 
00307 
00308 static gchar *exe = NULL;
00309 
00310 static void set_gerror (GError **error, GbrInitError errcode);
00311 
00312 
00328 gboolean
00329 gbr_init (GError **error)
00330 {
00331         GbrInitError errcode = 0;
00332 
00333         /* Locate the application's filename. */
00334         exe = _br_find_exe (&errcode);
00335         if (exe != NULL)
00336                 /* Success! */
00337                 return TRUE;
00338         else {
00339                 /* Failed :-( */
00340                 set_gerror (error, errcode);
00341                 return FALSE;
00342         }
00343 }
00344 
00345 
00356 gboolean
00357 gbr_init_lib (GError **error)
00358 {
00359         GbrInitError errcode = 0;
00360 
00361         exe = _br_find_exe_for_symbol ((const void *) "", &errcode);
00362         if (exe != NULL)
00363                 /* Success! */
00364                 return TRUE;
00365         else {
00366                 /* Failed :-( */
00367                 set_gerror (error, errcode);
00368                 return exe != NULL;
00369         }
00370 }
00371 
00372 
00373 static void
00374 set_gerror (GError **error, GbrInitError errcode)
00375 {
00376         gchar *error_message;
00377 
00378         if (error == NULL)
00379                 return;
00380 
00381         switch (errcode) {
00382         case GBR_INIT_ERROR_NOMEM:
00383                 error_message = "Cannot allocate memory.";
00384                 break;
00385         case GBR_INIT_ERROR_OPEN_MAPS:
00386                 error_message = "Unable to open /proc/self/maps for reading.";
00387                 break;
00388         case GBR_INIT_ERROR_READ_MAPS:
00389                 error_message = "Unable to read from /proc/self/maps.";
00390                 break;
00391         case GBR_INIT_ERROR_INVALID_MAPS:
00392                 error_message = "The file format of /proc/self/maps is invalid.";
00393                 break;
00394         case GBR_INIT_ERROR_DISABLED:
00395                 error_message = "Binary relocation support is disabled.";
00396                 break;
00397         default:
00398                 error_message = "Unknown error.";
00399                 break;
00400         };
00401         g_set_error (error, g_quark_from_static_string ("GBinReloc"),
00402                      errcode, "%s", error_message);
00403 }
00404 
00405 
00415 gchar *
00416 gbr_find_exe (const gchar *default_exe)
00417 {
00418         if (exe == NULL) {
00419                 /* BinReloc is not initialized. */
00420                 if (default_exe != NULL)
00421                         return g_strdup (default_exe);
00422                 else
00423                         return NULL;
00424         }
00425         return g_strdup (exe);
00426 }
00427 
00428 
00443 gchar *
00444 gbr_find_exe_dir (const gchar *default_dir)
00445 {
00446         if (exe == NULL) {
00447                 /* BinReloc not initialized. */
00448                 if (default_dir != NULL)
00449                         return g_strdup (default_dir);
00450                 else
00451                         return NULL;
00452         }
00453 
00454         return g_path_get_dirname (exe);
00455 }
00456 
00457 
00472 gchar *
00473 gbr_find_prefix (const gchar *default_prefix)
00474 {
00475         gchar *dir1, *dir2;
00476 
00477         if (exe == NULL) {
00478                 /* BinReloc not initialized. */
00479                 if (default_prefix != NULL)
00480                         return g_strdup (default_prefix);
00481                 else
00482                         return NULL;
00483         }
00484 
00485         dir1 = g_path_get_dirname (exe);
00486         dir2 = g_path_get_dirname (dir1);
00487         g_free (dir1);
00488         return dir2;
00489 }
00490 
00491 
00505 gchar *
00506 gbr_find_bin_dir (const gchar *default_bin_dir)
00507 {
00508         gchar *prefix, *dir;
00509 
00510         prefix = gbr_find_prefix (NULL);
00511         if (prefix == NULL) {
00512                 /* BinReloc not initialized. */
00513                 if (default_bin_dir != NULL)
00514                         return g_strdup (default_bin_dir);
00515                 else
00516                         return NULL;
00517         }
00518 
00519         dir = g_build_filename (prefix, "bin", NULL);
00520         g_free (prefix);
00521         return dir;
00522 }
00523 
00524 
00538 gchar *
00539 gbr_find_sbin_dir (const gchar *default_sbin_dir)
00540 {
00541         gchar *prefix, *dir;
00542 
00543         prefix = gbr_find_prefix (NULL);
00544         if (prefix == NULL) {
00545                 /* BinReloc not initialized. */
00546                 if (default_sbin_dir != NULL)
00547                         return g_strdup (default_sbin_dir);
00548                 else
00549                         return NULL;
00550         }
00551 
00552         dir = g_build_filename (prefix, "sbin", NULL);
00553         g_free (prefix);
00554         return dir;
00555 }
00556 
00557 
00572 gchar *
00573 gbr_find_data_dir (const gchar *default_data_dir)
00574 {
00575         gchar *prefix, *dir;
00576 
00577         prefix = gbr_find_prefix (NULL);
00578         if (prefix == NULL) {
00579                 /* BinReloc not initialized. */
00580                 if (default_data_dir != NULL)
00581                         return g_strdup (default_data_dir);
00582                 else
00583                         return NULL;
00584         }
00585 
00586         dir = g_build_filename (prefix, "share", NULL);
00587         g_free (prefix);
00588         return dir;
00589 }
00590 
00591 
00605 gchar *
00606 gbr_find_lib_dir (const gchar *default_lib_dir)
00607 {
00608         gchar *prefix, *dir;
00609 
00610         prefix = gbr_find_prefix (NULL);
00611         if (prefix == NULL) {
00612                 /* BinReloc not initialized. */
00613                 if (default_lib_dir != NULL)
00614                         return g_strdup (default_lib_dir);
00615                 else
00616                         return NULL;
00617         }
00618 
00619         dir = g_build_filename (prefix, "lib", NULL);
00620         g_free (prefix);
00621         return dir;
00622 }
00623 
00624 
00638 gchar *
00639 gbr_find_libexec_dir (const gchar *default_libexec_dir)
00640 {
00641         gchar *prefix, *dir;
00642 
00643         prefix = gbr_find_prefix (NULL);
00644         if (prefix == NULL) {
00645                 /* BinReloc not initialized. */
00646                 if (default_libexec_dir != NULL)
00647                         return g_strdup (default_libexec_dir);
00648                 else
00649                         return NULL;
00650         }
00651 
00652         dir = g_build_filename (prefix, "libexec", NULL);
00653         g_free (prefix);
00654         return dir;
00655 }
00656 
00657 
00671 gchar *
00672 gbr_find_etc_dir (const gchar *default_etc_dir)
00673 {
00674         gchar *prefix, *dir;
00675 
00676         prefix = gbr_find_prefix (NULL);
00677         if (prefix == NULL) {
00678                 /* BinReloc not initialized. */
00679                 if (default_etc_dir != NULL)
00680                         return g_strdup (default_etc_dir);
00681                 else
00682                         return NULL;
00683         }
00684 
00685         dir = g_build_filename (prefix, "etc", NULL);
00686         g_free (prefix);
00687         return dir;
00688 }
00689 
00690 
00691 G_END_DECLS
00692 
00693 #endif /* __BINRELOC_C__ */

Generated on Fri Oct 10 05:06:02 2008 for GnuCash by  doxygen 1.5.2