#include <fcntl.h>
void *libgl_handle;
-void *wrapper_handle;
+void **wrapper_handles;
+int num_wrapper_handles;
#define STRNCMP_LITERAL(str, literal) strncmp (str, literal, sizeof (literal) - 1)
}
}
+/* 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_handle (void)
+open_wrapper_handles (void)
{
const char *path;
+ char *path_copy, *wrapper, *save;
+ int i;
- if (wrapper_handle)
+ if (wrapper_handles)
return;
path = getenv ("GLAZE_WRAPPER");
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 ();
- wrapper_handle = dlopen (path, RTLD_LAZY);
- if (wrapper_handle == NULL) {
- const char *error = dlerror();
- fprintf (stderr, "Error: Failed to dlopen %s: %s\n", path, error);
+
+ 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 void
glaze_init (void)
{
open_libgl_handle ();
- open_wrapper_handle ();
+ open_wrapper_handles ();
+}
+
+static void
+call_first_gl_call_callbacks (void)
+{
+ char *callbacks = getenv ("GLAZE_FIRST_GL_CALL_CALLBACK");
+ char *name, *names, *save;
+ void (*callback) (void);
+ int i;
+
+ if (callbacks == NULL)
+ return;
+
+ names = strdup (callbacks);
+ if (names == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ exit (1);
+ }
+
+ for (name = strtok_r (names, ":", &save);
+ name;
+ name = strtok_r (NULL, ":", &save))
+ {
+ for (i = 0; i < num_wrapper_handles; i++) {
+ callback = dlsym (wrapper_handles[i], name);
+ if (callback) {
+ (callback) ();
+ goto NEXT_NAME;
+ }
+ }
+ fprintf (stderr, "Error: Failed to find function %s "
+ "in any GLAZE_WRAPPER library.\n", name);
+ NEXT_NAME:
+ ;
+ }
+
+ free (names);
+}
+
+static void *
+find_symbol_in_glaze_wrappers (const char *name)
+{
+ void *symbol;
+ int i;
+
+ for (i = 0; i < num_wrapper_handles; i++) {
+ symbol = dlsym (wrapper_handles[i], name);
+ if (symbol)
+ return symbol;
+ }
+
+ return NULL;
}
static void *
STRNCMP_LITERAL (name, "gl") == 0 &&
STRNCMP_LITERAL (name, "glX") != 0)
{
- char *callback_name = getenv ("GLAZE_FIRST_GL_CALL_CALLBACK");
- if (callback_name) {
- void (*callback) (void) = dlsym (wrapper_handle,
- callback_name);
- if (callback) {
- (callback) ();
- } else {
- fprintf (stderr,
- "Error: Failed to find function %s "
- "in GLAZE_WRAPPER library.\n",
- callback_name);
- }
- }
+ call_first_gl_call_callbacks ();
+
before_first_gl_call = 0;
}
- /* The wrapper gets first choice on all symbols. */
- symbol = dlsym (wrapper_handle, name);
+ /* The wrappers get first choice on all symbols. */
+ symbol = find_symbol_in_glaze_wrappers (name);
if (symbol)
return symbol;
/* On the first call, check if the wrapper provides an
* implementation of this function. */
if (first_call) {
- wrapper_glXGetProcAddress = dlsym (wrapper_handle,
- "glXGetProcAddress");
+ wrapper_glXGetProcAddress =
+ find_symbol_in_glaze_wrappers ("glXGetProcAddress");
libgl_glXGetProcAddress = dlsym (libgl_handle,
"glXGetProcAddress");
first_call = 0;
/* Otherwise, we need to resolve the name.
*
- * The wrapper gets first choice on all symbols. */
- symbol = dlsym (wrapper_handle, (char *) name);
+ * The wrappers get first choice on all symbols. */
+ symbol = find_symbol_in_glaze_wrappers ((char *) name);
if (symbol)
return symbol;
/* On the first call, check if the wrapper provides an
* implementation of this function. */
if (first_call) {
- wrapper_glXGetProcAddressARB = dlsym (wrapper_handle,
- "glXGetProcAddressARB");
+ wrapper_glXGetProcAddressARB =
+ find_symbol_in_glaze_wrappers ("glXGetProcAddressARB");
libgl_glXGetProcAddressARB = dlsym (libgl_handle,
"glXGetProcAddressARB");
first_call = 0;
/* Otherwise, we need to resolve the name.
*
- * The wrapper gets first choice on all symbols. */
- symbol = dlsym (wrapper_handle, (char *) name);
+ * The wrappers get first choice on all symbols. */
+ symbol = find_symbol_in_glaze_wrappers ((char *) name);
if (symbol)
return symbol;
const char *wrapper = NULL;
int opt;
- const char *short_options="h";
+ /* The initial '+' means that getopt will stop looking for
+ * options after the first non-option argument. This means
+ * that a command such as:
+ *
+ * glaze glenv --renderer=Foo glxgears
+ *
+ * Will do what is intended, (namely, have glaze invoke "glenv
+ * --renderer=Foo glxgears" rather than trying to interpret
+ * --renderer=Foo as an option to glaze itself.
+ */
+ const char *short_options="+h";
const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"wrapper", required_argument, 0, WRAPPER_OPT},
#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;
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