From: Carl Worth Date: Mon, 23 Sep 2013 19:57:49 +0000 (-0700) Subject: Add support for multiple Glaze wrappers to be used at the same time. X-Git-Url: https://git.cworth.org/git?p=glaze;a=commitdiff_plain;h=34d258542013821639bf8c4f60b0ae2c47e17091 Add support for multiple Glaze wrappers to be used at the same time. We simply use a colon-separated specification of the wrappers in GLAZE_WRAPPER and hold on to an array of dlopened handles. It's a simple matter to use the array of handles in several places. The one place that involves some delicacy is glaze_lookup. Here we must ensure that one wrapper's wrapped implementation of a function chains to the subsequent wrapper and not to itself (which would lead to infinite recursion). To get this right, we inspect the backtrace and refuse to let glaze_lookup return a function that already appears in the backtrace. --- diff --git a/glaze-gl.c b/glaze-gl.c index 04f288e..8c3a82d 100644 --- a/glaze-gl.c +++ b/glaze-gl.c @@ -29,7 +29,8 @@ #include void *libgl_handle; -void *wrapper_handle; +void **wrapper_handles; +int num_wrapper_handles; #define STRNCMP_LITERAL(str, literal) strncmp (str, literal, sizeof (literal) - 1) @@ -70,12 +71,34 @@ open_libgl_handle (void) } } +/* 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"); @@ -84,13 +107,37 @@ open_wrapper_handle (void) 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 @@ -100,7 +147,59 @@ 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 * @@ -113,24 +212,13 @@ resolve (const char *name) 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; @@ -151,8 +239,8 @@ void /* 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; @@ -165,8 +253,8 @@ void /* 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; @@ -190,8 +278,8 @@ void /* 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; @@ -204,8 +292,8 @@ void /* 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; diff --git a/glaze.c b/glaze.c index 9a3e055..02a3927 100644 --- a/glaze.c +++ b/glaze.c @@ -44,7 +44,17 @@ main (int argc, char *argv[]) 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}, diff --git a/libglaze.c b/libglaze.c index 17c4736..bf83669 100644 --- a/libglaze.c +++ b/libglaze.c @@ -23,6 +23,8 @@ #define _GNU_SOURCE #include +#include +#include #include "glaze.h" @@ -39,12 +41,159 @@ /* For PATH_MAX */ #include +#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