[gnome-games] libgames-support: add binreloc support



commit 3a04b7094862d15dba17440dcd89e20d687eefeb
Author: Christian Persch <chpe gnome org>
Date:   Sat Jun 27 21:32:41 2009 +0200

    libgames-support: add binreloc support
    
    Enable runtime relocation also on non-win32 platform.
    Code to find the prefix from the executable copied from
    BinReloc [autopackage.org], here used under LGPL2+.

 configure.in                     |   34 ++++++
 libgames-support/games-runtime.c |  225 +++++++++++++++++++++++++++++++++++---
 libgames-support/games-runtime.h |    4 +-
 3 files changed, 246 insertions(+), 17 deletions(-)
---
diff --git a/configure.in b/configure.in
index 86e7961..c4335dc 100644
--- a/configure.in
+++ b/configure.in
@@ -749,6 +749,39 @@ if test "$platform_win32" = "yes" -a "$os_win32" = "yes"; then
   fi
 fi
 
+# ********
+# Binreloc
+# ********
+
+AC_MSG_CHECKING([whether to enable binary relocation support])
+AC_ARG_ENABLE([binreloc],
+  [AS_HELP_STRING([--enable-binreloc],[enable binary relocation support (default: disabled)])],
+  [],
+  [enable_binreloc="$platform_win32"])
+AC_MSG_RESULT([$enable_binreloc])
+
+if test "$enable_binreloc" = "yes"; then
+
+  # Check that all variables use the same prefix
+  # Note: datarootdir exists only since autoconf 2.60, so we have to
+  # check for the old and the new form of datadir.
+  if test "$exec_prefix" != '${prefix}' -o \
+          "$bindir" != '${exec_prefix}/bin' -o \
+          "$sbindir" != '${exec_prefix}/sbin' -o \
+          "$libdir" != '${exec_prefix}/lib' -o \
+          "$libexecdir" != '${exec_prefix}/libexec' -o \
+          "$sysconfdir" != '${prefix}/etc' -o \
+          "$localstatedir" != '${prefix}/var' -o \
+          \( -n "$datarootdir" -a "$datarootdir" != '${prefix}/share' \) -o \
+          \( "$datadir" != '${datarootdir}' -a "$datadir" != '${prefix}/share' \) -o \
+          \( "$localedir" != '${datarootdir}/locale' -a "$localedir" != '${datadir}/locale' \) -o \
+          \( "$mandir" != '${datarootdir}/man' -a "$mandir" != '${datadir}/man' \); then
+    AC_MSG_ERROR([cannot use binary relocation with different prefixes])
+  fi
+
+  AC_DEFINE([ENABLE_BINRELOC],[1],[Define for binary relocation support])
+fi
+
 # *************
 # Check for GGZ
 # *************
@@ -1189,6 +1222,7 @@ echo "
     Scores user:           ${scores_user}
     Scores & setgid group: ${scores_group}
     Tests enabled:         ${enable_tests}
+    Binreloc:              ${enable_binreloc}
 "
 
 if grep "$scores_group:" /etc/group > /dev/null; then
diff --git a/libgames-support/games-runtime.c b/libgames-support/games-runtime.c
index 973caaf..f8beda7 100644
--- a/libgames-support/games-runtime.c
+++ b/libgames-support/games-runtime.c
@@ -36,12 +36,184 @@
 #include "games-profile.h"
 #include "games-runtime.h"
 
+#if defined(G_OS_WIN32) && !defined(ENABLE_BINRELOC)
+#error binreloc must be enabled on win32
+#endif
+
+#if defined(ENABLE_BINRELOC) && !defined(G_OS_WIN32)
+
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h lai chello nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */
+typedef enum {
+	/** Cannot allocate memory. */
+	GBR_INIT_ERROR_NOMEM,
+	/** Unable to open /proc/self/maps; see errno for details. */
+	GBR_INIT_ERROR_OPEN_MAPS,
+	/** Unable to read from /proc/self/maps; see errno for details. */
+	GBR_INIT_ERROR_READ_MAPS,
+	/** The file format of /proc/self/maps is invalid; kernel bug? */
+	GBR_INIT_ERROR_INVALID_MAPS,
+	/** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */
+	GBR_INIT_ERROR_DISABLED
+} GbrInitError;
+
+/** @internal
+ * Find the canonical filename of the executable. Returns the filename
+ * (which must be freed) or NULL on error. If the parameter 'error' is
+ * not NULL, the error code will be stored there, if an error occured.
+ */
+static char *
+_br_find_exe (GbrInitError *error)
+{
+	char *path, *path2, *line, *result;
+	size_t buf_size;
+	ssize_t size;
+	struct stat stat_buf;
+	FILE *f;
+
+	/* Read from /proc/self/exe (symlink) */
+	if (sizeof (path) > SSIZE_MAX)
+		buf_size = SSIZE_MAX - 1;
+	else
+		buf_size = PATH_MAX - 1;
+	path = (char *) g_try_malloc (buf_size);
+	if (path == NULL) {
+		/* Cannot allocate memory. */
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		return NULL;
+	}
+	path2 = (char *) g_try_malloc (buf_size);
+	if (path2 == NULL) {
+		/* Cannot allocate memory. */
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		g_free (path);
+		return NULL;
+	}
+
+	strncpy (path2, "/proc/self/exe", buf_size - 1);
+
+	while (1) {
+		int i;
+
+		size = readlink (path2, path, buf_size - 1);
+		if (size == -1) {
+			/* Error. */
+			g_free (path2);
+			break;
+		}
+
+		/* readlink() success. */
+		path[size] = '\0';
+
+		/* Check whether the symlink's target is also a symlink.
+		 * We want to get the final target. */
+		i = stat (path, &stat_buf);
+		if (i == -1) {
+			/* Error. */
+			g_free (path2);
+			break;
+		}
+
+		/* stat() success. */
+		if (!S_ISLNK (stat_buf.st_mode)) {
+			/* path is not a symlink. Done. */
+			g_free (path2);
+			return path;
+		}
+
+		/* path is a symlink. Continue loop and resolve this. */
+		strncpy (path, path2, buf_size - 1);
+	}
+
+
+	/* readlink() or stat() failed; this can happen when the program is
+	 * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
+
+	buf_size = PATH_MAX + 128;
+	line = (char *) g_try_realloc (path, buf_size);
+	if (line == NULL) {
+		/* Cannot allocate memory. */
+		g_free (path);
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		return NULL;
+	}
+
+	f = fopen ("/proc/self/maps", "r");
+	if (f == NULL) {
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_OPEN_MAPS;
+		return NULL;
+	}
+
+	/* The first entry should be the executable name. */
+	result = fgets (line, (int) buf_size, f);
+	if (result == NULL) {
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_READ_MAPS;
+		return NULL;
+	}
+
+	/* Get rid of newline character. */
+	buf_size = strlen (line);
+	if (buf_size <= 0) {
+		/* Huh? An empty string? */
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_INVALID_MAPS;
+		return NULL;
+	}
+	if (line[buf_size - 1] == 10)
+		line[buf_size - 1] = 0;
+
+	/* Extract the filename; it is always an absolute path. */
+	path = strchr (line, '/');
+
+	/* Sanity check. */
+	if (strstr (line, " r-xp ") == NULL || path == NULL) {
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_INVALID_MAPS;
+		return NULL;
+	}
+
+	path = g_strdup (path);
+	g_free (line);
+	fclose (f);
+	return path;
+}
+
+#endif /* ENABLE_BINRELOC && !G_OS_WIN32 */
+
 static char *app_name;
 static int gpl_version;
 static char *cached_directories[GAMES_RUNTIME_LAST_DIRECTORY];
-#ifdef G_OS_WIN32
-static char *module_path;
-#endif
 
 typedef struct {
   GamesRuntimeDirectory base_dir;
@@ -50,12 +222,12 @@ typedef struct {
 
 static const DerivedDirectory derived_directories[] = {
   /* Keep this in the same order as in the GamesRuntimeDirectory enum! */
-#ifdef G_OS_WIN32
+#ifdef ENABLE_BINRELOC
   { GAMES_RUNTIME_MODULE_DIRECTORY,   "share"              }, /* GAMES_RUNTIME_DATA_DIRECTORY              */
   { GAMES_RUNTIME_DATA_DIRECTORY,     "gnome-games-common" }, /* GAMES_RUNTIME_COMMON_DATA_DIRECTORY       */
   { GAMES_RUNTIME_DATA_DIRECTORY,     "gnome-games"        }, /* GAMES_RUNTIME_PKG_DATA_DIRECTORY          */
   { GAMES_RUNTIME_DATA_DIRECTORY,     "scores"             }, /* GAMES_RUNTIME_SCORES_DIRECTORY            */
-#endif /* G_OS_WIN32 */
+#endif /* ENABLE_BINRELOC */
   { GAMES_RUNTIME_DATA_DIRECTORY,         "locale"         }, /* GAMES_RUNTIME_LOCALE_DIRECTORY            */
   { GAMES_RUNTIME_COMMON_DATA_DIRECTORY,  "pixmaps"        }, /* GAMES_RUNTIME_COMMON_PIXMAP_DIRECTORY     */
   { GAMES_RUNTIME_COMMON_DATA_DIRECTORY,  "card-themes"    }, /* GAMES_RUNTIME_PRERENDERED_CARDS_DIRECTORY */
@@ -137,7 +309,7 @@ games_runtime_init (const char *name)
   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
   textdomain(GETTEXT_PACKAGE);
 
-#ifdef G_OS_WIN32
+#ifdef ENABLE_BINRELOC
 {
   const char *path;
 
@@ -149,9 +321,9 @@ games_runtime_init (const char *name)
 
   retval = path != NULL;
 }
-#else
+#else /* !ENABLE_BINRELOC */
   retval = TRUE;
-#endif
+#endif /* ENABLE_BINRELOC */
 
 #if defined(ENABLE_CARD_THEME_FORMAT_KDE) || defined(ENABLE_CARD_THEME_FORMAT_SLICED) || defined(ENABLE_CARD_THEME_FORMAT_PYSOL)
   if (strcmp (app_name, "aisleriot") == 0 || strcmp (app_name, "blackjack") == 0) {
@@ -204,7 +376,35 @@ games_runtime_get_directory (GamesRuntimeDirectory directory)
     return cached_directories[directory];
 
   switch ((int) directory) {
-#ifndef G_OS_WIN32
+#ifdef ENABLE_BINRELOC
+    case GAMES_RUNTIME_MODULE_DIRECTORY:
+#ifdef G_OS_WIN32
+      path = g_win32_get_package_installation_directory_of_module (NULL);
+#else
+      {
+        GbrInitError errv = 0;
+        char *exe, *bindir, *prefix;
+
+        exe = _br_find_exe (&errv);
+        if (exe == NULL) {
+          g_printerr ("Failed to locate the binary relocation prefix (error code %u)\n", errv);
+        } else {
+          bindir = g_path_get_dirname (exe);
+          g_free (exe);
+          prefix = g_path_get_dirname (bindir);
+          g_free (bindir);
+
+          if (prefix != NULL && strcmp (prefix, ".") != 0) {
+            path = prefix;
+          } else {
+            g_free (prefix);
+          }
+        }
+      }
+#endif /* G_OS_WIN32 */
+      break;
+#else /* !ENABLE_BINRELOC */
+
     case GAMES_RUNTIME_DATA_DIRECTORY:
       path = g_strdup (DATADIR);
       break;
@@ -220,12 +420,7 @@ games_runtime_get_directory (GamesRuntimeDirectory directory)
     case GAMES_RUNTIME_SCORES_DIRECTORY:
       path = g_strdup (SCORESDIR);
       break;
-
-#else /* G_OS_WIN32 */
-    case GAMES_RUNTIME_MODULE_DIRECTORY:
-      path = g_win32_get_package_installation_directory_of_module (NULL);
-      break;
-#endif /* !G_OS_WIN32 */
+#endif /* ENABLE_BINRELOC */
 
     default: {
       const DerivedDirectory *base = &derived_directories[directory - GAMES_RUNTIME_FIRST_DERIVED_DIRECTORY];
diff --git a/libgames-support/games-runtime.h b/libgames-support/games-runtime.h
index 229d9f1..bd9a15a 100644
--- a/libgames-support/games-runtime.h
+++ b/libgames-support/games-runtime.h
@@ -25,7 +25,7 @@ G_BEGIN_DECLS
 
 typedef enum {
   /* Base directories */
-#ifdef G_OS_WIN32
+#ifdef ENABLE_BINRELOC
   GAMES_RUNTIME_MODULE_DIRECTORY,
 #endif
 
@@ -53,7 +53,7 @@ typedef enum {
   GAMES_RUNTIME_GAME_HELP_DIRECTORY,
 
   GAMES_RUNTIME_LAST_DIRECTORY,
-#ifdef G_OS_WIN32
+#ifdef ENABLE_BINRELOC
   GAMES_RUNTIME_FIRST_DERIVED_DIRECTORY = GAMES_RUNTIME_DATA_DIRECTORY,
 #else
   GAMES_RUNTIME_FIRST_DERIVED_DIRECTORY = GAMES_RUNTIME_LOCALE_DIRECTORY,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]