Back to index

gcompris  8.2.2
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 #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 <glib/gstdio.h>
00024 #include <stdlib.h>
00025 #include <limits.h>
00026 #include <string.h>
00027 #include "binreloc.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        char *path, *path2, *line, *result;
00046        size_t buf_size;
00047        ssize_t size;
00048        struct stat stat_buf;
00049        FILE *f;
00050 
00051        /* Read from /proc/self/exe (symlink) */
00052        if (sizeof (path) > SSIZE_MAX)
00053               buf_size = SSIZE_MAX - 1;
00054        else
00055               buf_size = PATH_MAX - 1;
00056        path = (char *) g_try_malloc (buf_size);
00057        if (path == NULL) {
00058               /* Cannot allocate memory. */
00059               if (error)
00060                      *error = GBR_INIT_ERROR_NOMEM;
00061               return NULL;
00062        }
00063        path2 = (char *) g_try_malloc (buf_size);
00064        if (path2 == NULL) {
00065               /* Cannot allocate memory. */
00066               if (error)
00067                      *error = GBR_INIT_ERROR_NOMEM;
00068               g_free (path);
00069               return NULL;
00070        }
00071 
00072        strncpy (path2, "/proc/self/exe", buf_size - 1);
00073 
00074        while (1) {
00075               int i;
00076 
00077               size = readlink (path2, path, buf_size - 1);
00078               if (size == -1) {
00079                      /* Error. */
00080                      g_free (path2);
00081                      break;
00082               }
00083 
00084               /* readlink() success. */
00085               path[size] = '\0';
00086 
00087               /* Check whether the symlink's target is also a symlink.
00088                * We want to get the final target. */
00089               i = stat (path, &stat_buf);
00090               if (i == -1) {
00091                      /* Error. */
00092                      g_free (path2);
00093                      break;
00094               }
00095 
00096               /* stat() success. */
00097               if (!S_ISLNK (stat_buf.st_mode)) {
00098                      /* path is not a symlink. Done. */
00099                      g_free (path2);
00100                      return path;
00101               }
00102 
00103               /* path is a symlink. Continue loop and resolve this. */
00104               strncpy (path, path2, buf_size - 1);
00105        }
00106 
00107 
00108        /* readlink() or stat() failed; this can happen when the program is
00109         * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
00110 
00111        buf_size = PATH_MAX + 128;
00112        line = (char *) g_try_realloc (path, buf_size);
00113        if (line == NULL) {
00114               /* Cannot allocate memory. */
00115               g_free (path);
00116               if (error)
00117                      *error = GBR_INIT_ERROR_NOMEM;
00118               return NULL;
00119        }
00120 
00121        f = g_fopen ("/proc/self/maps", "r");
00122        if (f == NULL) {
00123               g_free (line);
00124               if (error)
00125                      *error = GBR_INIT_ERROR_OPEN_MAPS;
00126               return NULL;
00127        }
00128 
00129        /* The first entry should be the executable name. */
00130        result = fgets (line, (int) buf_size, f);
00131        if (result == NULL) {
00132               fclose (f);
00133               g_free (line);
00134               if (error)
00135                      *error = GBR_INIT_ERROR_READ_MAPS;
00136               return NULL;
00137        }
00138 
00139        /* Get rid of newline character. */
00140        buf_size = strlen (line);
00141        if (buf_size <= 0) {
00142               /* Huh? An empty string? */
00143               fclose (f);
00144               g_free (line);
00145               if (error)
00146                      *error = GBR_INIT_ERROR_INVALID_MAPS;
00147               return NULL;
00148        }
00149        if (line[buf_size - 1] == 10)
00150               line[buf_size - 1] = 0;
00151 
00152        /* Extract the filename; it is always an absolute path. */
00153        path = strchr (line, '/');
00154 
00155        /* Sanity check. */
00156        if (strstr (line, " r-xp ") == NULL || path == NULL) {
00157               fclose (f);
00158               g_free (line);
00159               if (error)
00160                      *error = GBR_INIT_ERROR_INVALID_MAPS;
00161               return NULL;
00162        }
00163 
00164        path = g_strdup (path);
00165        g_free (line);
00166        fclose (f);
00167        return path;
00168 #endif /* ENABLE_BINRELOC */
00169 }
00170 
00171 
00176 static char *
00177 _br_find_exe_for_symbol (const void *symbol, GbrInitError *error)
00178 {
00179 #ifndef ENABLE_BINRELOC
00180        if (error)
00181               *error = GBR_INIT_ERROR_DISABLED;
00182        return (char *) NULL;
00183 #else
00184        #define SIZE PATH_MAX + 100
00185        FILE *f;
00186        size_t address_string_len;
00187        char *address_string, line[SIZE], *found;
00188 
00189        if (symbol == NULL)
00190               return (char *) NULL;
00191 
00192        f = g_fopen ("/proc/self/maps", "r");
00193        if (f == NULL)
00194               return (char *) NULL;
00195 
00196        address_string_len = 4;
00197        address_string = (char *) g_try_malloc (address_string_len);
00198        found = (char *) NULL;
00199 
00200        while (!feof (f)) {
00201               char *start_addr, *end_addr, *end_addr_end, *file;
00202               void *start_addr_p, *end_addr_p;
00203               size_t len;
00204 
00205               if (fgets (line, SIZE, f) == NULL)
00206                      break;
00207 
00208               /* Sanity check. */
00209               if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
00210                      continue;
00211 
00212               /* Parse line. */
00213               start_addr = line;
00214               end_addr = strchr (line, '-');
00215               file = strchr (line, '/');
00216 
00217               /* More sanity check. */
00218               if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
00219                      continue;
00220 
00221               end_addr[0] = '\0';
00222               end_addr++;
00223               end_addr_end = strchr (end_addr, ' ');
00224               if (end_addr_end == NULL)
00225                      continue;
00226 
00227               end_addr_end[0] = '\0';
00228               len = strlen (file);
00229               if (len == 0)
00230                      continue;
00231               if (file[len - 1] == '\n')
00232                      file[len - 1] = '\0';
00233 
00234               /* Get rid of "(deleted)" from the filename. */
00235               len = strlen (file);
00236               if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
00237                      file[len - 10] = '\0';
00238 
00239               /* I don't know whether this can happen but better safe than sorry. */
00240               len = strlen (start_addr);
00241               if (len != strlen (end_addr))
00242                      continue;
00243 
00244 
00245               /* Transform the addresses into a string in the form of 0xdeadbeef,
00246                * then transform that into a pointer. */
00247               if (address_string_len < len + 3) {
00248                      address_string_len = len + 3;
00249                      address_string = (char *) g_try_realloc (address_string, address_string_len);
00250               }
00251 
00252               memcpy (address_string, "0x", 2);
00253               memcpy (address_string + 2, start_addr, len);
00254               address_string[2 + len] = '\0';
00255               sscanf (address_string, "%p", &start_addr_p);
00256 
00257               memcpy (address_string, "0x", 2);
00258               memcpy (address_string + 2, end_addr, len);
00259               address_string[2 + len] = '\0';
00260               sscanf (address_string, "%p", &end_addr_p);
00261 
00262 
00263               if (symbol >= start_addr_p && symbol < end_addr_p) {
00264                      found = file;
00265                      break;
00266               }
00267        }
00268 
00269        g_free (address_string);
00270        fclose (f);
00271 
00272        if (found == NULL)
00273               return (char *) NULL;
00274        else
00275               return g_strdup (found);
00276 #endif /* ENABLE_BINRELOC */
00277 }
00278 
00279 
00280 static gchar *exe = NULL;
00281 
00282 static void set_gerror (GError **error, GbrInitError errcode);
00283 
00284 
00300 gboolean
00301 gbr_init (GError **error)
00302 {
00303        GbrInitError errcode = GBR_INIT_ERROR_DISABLED;
00304 
00305        /* Locate the application's filename. */
00306        exe = _br_find_exe (&errcode);
00307        if (exe != NULL)
00308               /* Success! */
00309               return TRUE;
00310        else {
00311               /* Failed :-( */
00312               set_gerror (error, errcode);
00313               return FALSE;
00314        }
00315 }
00316 
00317 
00328 gboolean
00329 gbr_init_lib (GError **error)
00330 {
00331        GbrInitError errcode = GBR_INIT_ERROR_DISABLED;
00332 
00333        exe = _br_find_exe_for_symbol ((const void *) "", &errcode);
00334        if (exe != NULL)
00335               /* Success! */
00336               return TRUE;
00337        else {
00338               /* Failed :-( */
00339               set_gerror (error, errcode);
00340               return exe != NULL;
00341        }
00342 }
00343 
00344 
00345 static void
00346 set_gerror (GError **error, GbrInitError errcode)
00347 {
00348        gchar *error_message;
00349 
00350        if (error == NULL)
00351               return;
00352 
00353        switch (errcode) {
00354        case GBR_INIT_ERROR_NOMEM:
00355               error_message = "Cannot allocate memory.";
00356               break;
00357        case GBR_INIT_ERROR_OPEN_MAPS:
00358               error_message = "Unable to open /proc/self/maps for reading.";
00359               break;
00360        case GBR_INIT_ERROR_READ_MAPS:
00361               error_message = "Unable to read from /proc/self/maps.";
00362               break;
00363        case GBR_INIT_ERROR_INVALID_MAPS:
00364               error_message = "The file format of /proc/self/maps is invalid.";
00365               break;
00366        case GBR_INIT_ERROR_DISABLED:
00367               error_message = "Binary relocation support is disabled.";
00368               break;
00369        default:
00370               error_message = "Unknown error.";
00371               break;
00372        };
00373        g_set_error (error, g_quark_from_static_string ("GBinReloc"),
00374                    errcode, "%s", error_message);
00375 }
00376 
00377 
00387 gchar *
00388 gbr_find_exe (const gchar *default_exe)
00389 {
00390        if (exe == NULL) {
00391               /* BinReloc is not initialized. */
00392               if (default_exe != NULL)
00393                      return g_strdup (default_exe);
00394               else
00395                      return NULL;
00396        }
00397        return g_strdup (exe);
00398 }
00399 
00400 
00415 gchar *
00416 gbr_find_exe_dir (const gchar *default_dir)
00417 {
00418        if (exe == NULL) {
00419               /* BinReloc not initialized. */
00420               if (default_dir != NULL)
00421                      return g_strdup (default_dir);
00422               else
00423                      return NULL;
00424        }
00425 
00426        return g_path_get_dirname (exe);
00427 }
00428 
00429 
00444 gchar *
00445 gbr_find_prefix (const gchar *default_prefix)
00446 {
00447        gchar *dir1, *dir2;
00448 
00449        if (exe == NULL) {
00450               /* BinReloc not initialized. */
00451               if (default_prefix != NULL)
00452                      return g_strdup (default_prefix);
00453               else
00454                      return NULL;
00455        }
00456 
00457        dir1 = g_path_get_dirname (exe);
00458        dir2 = g_path_get_dirname (dir1);
00459        g_free (dir1);
00460        return dir2;
00461 }
00462 
00463 
00477 gchar *
00478 gbr_find_bin_dir (const gchar *default_bin_dir)
00479 {
00480        gchar *prefix, *dir;
00481 
00482        prefix = gbr_find_prefix (NULL);
00483        if (prefix == NULL) {
00484               /* BinReloc not initialized. */
00485               if (default_bin_dir != NULL)
00486                      return g_strdup (default_bin_dir);
00487               else
00488                      return NULL;
00489        }
00490 
00491        dir = g_build_filename (prefix, "bin", NULL);
00492        g_free (prefix);
00493        return dir;
00494 }
00495 
00496 
00510 gchar *
00511 gbr_find_sbin_dir (const gchar *default_sbin_dir)
00512 {
00513        gchar *prefix, *dir;
00514 
00515        prefix = gbr_find_prefix (NULL);
00516        if (prefix == NULL) {
00517               /* BinReloc not initialized. */
00518               if (default_sbin_dir != NULL)
00519                      return g_strdup (default_sbin_dir);
00520               else
00521                      return NULL;
00522        }
00523 
00524        dir = g_build_filename (prefix, "sbin", NULL);
00525        g_free (prefix);
00526        return dir;
00527 }
00528 
00529 
00544 gchar *
00545 gbr_find_data_dir (const gchar *default_data_dir)
00546 {
00547        gchar *prefix, *dir;
00548 
00549        prefix = gbr_find_prefix (NULL);
00550        if (prefix == NULL) {
00551               /* BinReloc not initialized. */
00552               if (default_data_dir != NULL)
00553                      return g_strdup (default_data_dir);
00554               else
00555                      return NULL;
00556        }
00557 
00558        dir = g_build_filename (prefix, "share", NULL);
00559        g_free (prefix);
00560        return dir;
00561 }
00562 
00563 
00577 gchar *
00578 gbr_find_locale_dir (const gchar *default_locale_dir)
00579 {
00580        gchar *data_dir, *dir;
00581 
00582        data_dir = gbr_find_data_dir (NULL);
00583        if (data_dir == NULL) {
00584               /* BinReloc not initialized. */
00585               if (default_locale_dir != NULL)
00586                      return g_strdup (default_locale_dir);
00587               else
00588                      return NULL;
00589        }
00590 
00591        dir = g_build_filename (data_dir, "locale", NULL);
00592        g_free (data_dir);
00593        return dir;
00594 }
00595 
00596 
00610 gchar *
00611 gbr_find_lib_dir (const gchar *default_lib_dir)
00612 {
00613        gchar *prefix, *dir;
00614 
00615        prefix = gbr_find_prefix (NULL);
00616        if (prefix == NULL) {
00617               /* BinReloc not initialized. */
00618               if (default_lib_dir != NULL)
00619                      return g_strdup (default_lib_dir);
00620               else
00621                      return NULL;
00622        }
00623 
00624        dir = g_build_filename (prefix, "lib", NULL);
00625        g_free (prefix);
00626        return dir;
00627 }
00628 
00629 
00643 gchar *
00644 gbr_find_libexec_dir (const gchar *default_libexec_dir)
00645 {
00646        gchar *prefix, *dir;
00647 
00648        prefix = gbr_find_prefix (NULL);
00649        if (prefix == NULL) {
00650               /* BinReloc not initialized. */
00651               if (default_libexec_dir != NULL)
00652                      return g_strdup (default_libexec_dir);
00653               else
00654                      return NULL;
00655        }
00656 
00657        dir = g_build_filename (prefix, "libexec", NULL);
00658        g_free (prefix);
00659        return dir;
00660 }
00661 
00662 
00676 gchar *
00677 gbr_find_etc_dir (const gchar *default_etc_dir)
00678 {
00679        gchar *prefix, *dir;
00680 
00681        prefix = gbr_find_prefix (NULL);
00682        if (prefix == NULL) {
00683               /* BinReloc not initialized. */
00684               if (default_etc_dir != NULL)
00685                      return g_strdup (default_etc_dir);
00686               else
00687                      return NULL;
00688        }
00689 
00690        dir = g_build_filename (prefix, "etc", NULL);
00691        g_free (prefix);
00692        return dir;
00693 }
00694 
00695 
00696 G_END_DECLS
00697 
00698 #endif /* __BINRELOC_C__ */