#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;
* 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);
talloc_free (lib_path);
+ /* Allow relative wrapper path to remain as-is. */
return 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