]> git.cworth.org Git - glaze/blobdiff - libglaze.c
Add egl definitions and related buildsupport.
[glaze] / libglaze.c
index 17c473657f132b61bade34319ec268b9ce8f3c15..bf83669e16e3f2420f9572f633916e1840405f13 100644 (file)
@@ -23,6 +23,8 @@
 
 #define _GNU_SOURCE
 #include <dlfcn.h>
+#include <link.h>
+#include <execinfo.h>
 
 #include "glaze.h"
 
 /* For PATH_MAX */
 #include <linux/limits.h>
 
+#ifdef __GNUC__
+#define unused __attribute__ ((unused))
+#else
+#define unused
+#endif
+
+void **wrapper_handles;
+int num_wrapper_handles;
+
+/* FIXME: Both count_chars and open_wrapper_handles are copied code
+ * from glaze-gl.c. It would be better to avoid this code
+ * duplication. */
+
+/* Count the number of types 'chr' appears in 'str' */
+static int
+count_chars (const char *str, char chr)
+{
+       int count = 0;
+       const char *s = str;
+
+       while (1) {
+               s = strchr (s, chr);
+               if (s == NULL)
+                       break;
+               count++;
+               s++;
+               if (*s == '\0')
+                       break;
+       }
+
+       return count;
+}
+
+static void
+open_wrapper_handles (void)
+{
+       const char *path;
+       char *path_copy, *wrapper, *save;
+       int i;
+
+       if (wrapper_handles)
+               return;
+
+       path = getenv ("GLAZE_WRAPPER");
+       if (path == NULL) {
+               fprintf (stderr, "GLAZE_WRAPPER unset. Please set to path of Glaze-using wrapper library.\n");
+               exit (1);
+       }
+
+       num_wrapper_handles = count_chars (path, ':') + 1;
+
+       wrapper_handles = malloc (num_wrapper_handles * sizeof (void*));
+       if (wrapper_handles == NULL) {
+               fprintf (stderr, "Out of memory\n");
+               exit (1);
+       }
+
+       /* Clear dlerror state. */
+       dlerror ();
+
+       path_copy = strdup (path);
+       if (path_copy == NULL) {
+               fprintf (stderr, "Out of memory\n");
+               exit (1);
+       }
+
+       for (i = 0, wrapper = strtok_r (path_copy, ":", &save);
+            i < num_wrapper_handles;
+            i++, wrapper = strtok_r (NULL, ":", &save))
+       {
+               wrapper_handles[i] = dlopen (wrapper, RTLD_LAZY);
+               if (wrapper_handles[i] == NULL) {
+                       const char *error = dlerror();
+                       fprintf (stderr, "Error: Failed to dlopen %s: %s\n",
+                                wrapper, error);
+                       exit (1);
+               }
+       }
+
+       free (path_copy);
+}
+
+static int
+symbol_is_in_backtrace (void *symbol, void **bt, int num_bt)
+{
+       Dl_info info;
+       int status;
+       int i;
+
+       for (i = 0; i < num_bt; i++) {
+               status = dladdr (bt[i], &info);
+               if (status == 0)
+                       continue;
+               if (info.dli_saddr == symbol)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* FIXME: The hardcoded MAX_BT value here is uncool.
+ *
+ * If the number is too small, we could miss one of the wrapper
+ * functions in the backtrace which could lead to an infinite
+ * loop. For this bound, something on the order of the number of
+ * wrappers is likely sufficiently large.
+ *
+ * Meanwhile, if the number here is too large, we just waste a bunch
+ * of time grubbing through the backtrace of the application itself.
+ *
+ * The Right Thing to do here is to look at the back trace and ensure
+ * we have seen back to Glaze's libGL.so. If we see that, then we've
+ * looked far enough and should not have to look any further.
+ *
+ * Until then, 15 levels of backtrace out to be enough for anybody...
+ */
+#define MAX_BT 15
+
 void *
 glaze_lookup (char *function)
 {
        static void *libgl_handle = NULL;
        void *ret;
+       void *bt_buffer[MAX_BT];
+       int i, num_bt;
+
+       /* Before looking up the symbol in the underlying libGL, we
+        * first check whether any wrapper library has that symbol. Of
+        * course, we're already within at least one wrapper, and it
+        * wants to call into the next. So, for each wrapper, skip the
+        * looked up symbol if it already appears in the current
+        * backtrace. */
+
+       if (wrapper_handles == NULL)
+               open_wrapper_handles ();
+
+       num_bt = backtrace (bt_buffer, MAX_BT);
 
+       for (i = 0; i < num_wrapper_handles; i++) {
+               ret = dlsym (wrapper_handles[i], function);
+               if (ret == NULL)
+                       continue;
+
+               /* Check if this function appears in the backtrace. */
+               if (symbol_is_in_backtrace (ret, bt_buffer, num_bt))
+                       continue;
+
+               /* Otherwise, we've found the right thing to chain to. */
+               return ret;
+       }
+
+       /* Did not find the symbol in any wrapper library, so defer to
+        * the real, underlying function in libGL.so */
        if (libgl_handle == NULL) {
                const char *path;
 
@@ -273,7 +422,19 @@ glaze_execute (int argc, char *argv[], const char *wrapper)
 
        wrapper = resolve_wrapper_path (ctx, wrapper);
 
-       setenv ("GLAZE_WRAPPER", wrapper, 1);
+       /* If GLAZE_WRAPPER is already set, append so that both
+        * wrappers get involved correctly. */
+       if (getenv ("GLAZE_WRAPPER")) {
+               char *aggregate;
+
+               aggregate = talloc_asprintf (ctx, "%s:%s",
+                                            getenv ("GLAZE_WRAPPER"),
+                                            wrapper);
+               setenv ("GLAZE_WRAPPER", aggregate, 1);
+               talloc_free (aggregate);
+       } else {
+               setenv ("GLAZE_WRAPPER", wrapper, 1);
+       }
 
        /* Ensure GLAZE_LIBGL is set. If not, set GLAZE_LIBGL_32_AUTO
         * and GLAZE_LIBGL_64_AUTO