X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;ds=sidebyside;f=libglaze.c;h=bf83669e16e3f2420f9572f633916e1840405f13;hb=HEAD;hp=8377d846a149321640e8b3dfc0bcae3c0d3ed1dc;hpb=f5afe7a34b5f7adc8451f01db92be1de5f0830af;p=glaze diff --git a/libglaze.c b/libglaze.c index 8377d84..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; @@ -197,20 +346,39 @@ read_process_output_one_line (const char *program) * If "wrapper" is an absolute path, return it directly. * * Otherwise, ("wrapper" is relative), look for an existing file named - * "wrapper" in the same directory as the currently executing binary, - * (as determined by /proc/self/exe). If that file exists, return its - * path. + * "wrapper" in the following locations in order: + * + * 1. The current working directory + * + * 2. The same directory as the currently executing binary, (as + * determined by /proc/self/exe). + * + * If either of those files exist, return its path. * * Otherwise, return the original, relative "wrapper". */ static const char * resolve_wrapper_path (void *ctx, const char *wrapper) { - char *bin_path, *lib_path; + char *cwd_path, *bin_path, *lib_path; + /* Return absolute wrapper path immediately. */ if (*wrapper == '/') return wrapper; + /* Look for wrapper in current working directory. */ + cwd_path = get_current_dir_name (); + + lib_path = talloc_asprintf (ctx, "%s/%s", cwd_path, wrapper); + + free (cwd_path); + + if (exists (lib_path)) + return lib_path; + + talloc_free (lib_path); + + /* Look for wrapper next to current executable. */ bin_path = get_bin_name (ctx); chop_trailing_path_component (bin_path); @@ -224,6 +392,7 @@ resolve_wrapper_path (void *ctx, const char *wrapper) talloc_free (lib_path); + /* Allow relative wrapper path to remain as-is. */ return wrapper; } @@ -253,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