X-Git-Url: https://git.cworth.org/git?p=glaze;a=blobdiff_plain;f=libglaze.c;h=bf83669e16e3f2420f9572f633916e1840405f13;hp=17c473657f132b61bade34319ec268b9ce8f3c15;hb=HEAD;hpb=85faa290c2777d8e89af05648c229b0da395d4f7 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