dri megadriver_stub: add compatibility for older DRI loaders
authorJordan Justen <jordan.l.justen@intel.com>
Fri, 6 Dec 2013 10:21:17 +0000 (02:21 -0800)
committerJordan Justen <jordan.l.justen@intel.com>
Tue, 10 Dec 2013 00:33:45 +0000 (16:33 -0800)
To help the transition period when DRI loaders are being updated
to support the newer __driDriverExtensions_foo mechanism,
we populate __driDriverExtensions with the extensions returned
by __driDriverExtensions_foo during a library contructor
function.

We find the driver foo's name by using the dladdr function
which gives the path of the dynamic library's name that
was being loaded.

Signed-off-by: Jordan Justen <jordan.l.justen@intel.com>
Reviewed-by: Matt Turner <mattst88@gmail.com>
Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Keith Packard <keithp@keithp.com>
Cc: "10.0" <mesa-stable@lists.freedesktop.org>
src/mesa/drivers/dri/common/megadriver_stub.c

index 6bf5d73..a821770 100644 (file)
 
 #include <stdio.h>
 #include "dri_util.h"
+#include <dlfcn.h>
+#include "main/macros.h"
+
+/* We need GNU extensions to dlfcn.h in order to provide backward
+ * compatibility for the older DRI driver loader mechanism. (dladdr,
+ * Dl_info, and RTLD_DEFAULT are only defined when _GNU_SOURCE is
+ * defined.)
+ */
+#ifdef _GNU_SOURCE
+
+#define MEGADRIVER_STUB_MAX_EXTENSIONS 10
+#define LIB_PATH_SUFFIX "_dri.so"
+#define LIB_PATH_SUFFIX_LENGTH (sizeof(LIB_PATH_SUFFIX)-1)
+
+/* This is the table of extensions that the loader will dlsym() for.
+ *
+ * Initially it is empty for the megadriver stub, but the library
+ * constructor may initialize it based on the name of the library that
+ * is being loaded.
+ */
+PUBLIC const __DRIextension *
+__driDriverExtensions[MEGADRIVER_STUB_MAX_EXTENSIONS] = {
+   NULL
+};
+
+/**
+ * This is a constructor function for the megadriver dynamic library.
+ *
+ * When the driver is dlopen'ed, this function will run. It will
+ * search for the name of the foo_dri.so file that was opened using
+ * the dladdr function.
+ *
+ * After finding foo's name, it will call __driDriverGetExtensions_foo
+ * and use the return to update __driDriverExtensions to enable
+ * compatibility with older DRI driver loaders.
+ */
+__attribute__((constructor)) static void
+megadriver_stub_init(void)
+{
+   Dl_info info;
+   char *driver_name;
+   size_t name_len;
+   char *get_extensions_name;
+   const __DRIextension **(*get_extensions)(void);
+   const __DRIextension **extensions;
+   int i;
+
+   /* Call dladdr on __driDriverExtensions. We are really
+    * interested in the returned info.dli_fname so we can
+    * figure out the path name of the library being loaded.
+    */
+   i = dladdr((void*) __driDriverExtensions, &info);
+   if (i == 0)
+      return;
+
+   /* Search for the last '/' character in the path. */
+   driver_name = strrchr(info.dli_fname, '/');
+   if (driver_name != NULL) {
+      /* Skip '/' character */
+      driver_name++;
+   } else {
+      /* Try using the start of the path */
+      driver_name = (char*) info.dli_fname;
+   }
+
+   /* Make sure the path ends with _dri.so */
+   name_len = strlen(driver_name);
+   i = name_len - LIB_PATH_SUFFIX_LENGTH;
+   if (i < 0 || strcmp(driver_name + i, LIB_PATH_SUFFIX) != 0)
+      return;
+
+   /* Duplicate the string so we can modify it.
+    * So far we've been using info.dli_fname.
+    */
+   driver_name = strdup(driver_name);
+   if (!driver_name)
+      return;
+
+   /* The path ends with _dri.so. Chop this part of the
+    * string off. Then we'll have the driver's final name.
+    */
+   driver_name[i] = '\0';
+
+   i = asprintf(&get_extensions_name, "%s_%s",
+                __DRI_DRIVER_GET_EXTENSIONS, driver_name);
+   free(driver_name);
+   if (i == -1)
+      return;
+
+   /* dlsym to get the driver's get extensions function. We
+    * don't have the dlopen handle, so we have to use
+    * RTLD_DEFAULT. It seems unlikely that the symbol will
+    * be found in another library, but this isn't optimal.
+    */
+   get_extensions = dlsym(RTLD_DEFAULT, get_extensions_name);
+   free(get_extensions_name);
+   if (!get_extensions)
+      return;
+
+   /* Use the newer DRI loader entrypoint to find extensions.
+    * We will then expose these extensions via the older
+    * __driDriverExtensions symbol.
+    */
+   extensions = get_extensions();
+
+   /* Copy the extensions into the __driDriverExtensions array
+    * we declared.
+    */
+   for (i = 0; i < ARRAY_SIZE(__driDriverExtensions); i++) {
+      __driDriverExtensions[i] = extensions[i];
+      if (extensions[i] == NULL)
+         break;
+   }
+
+   /* If the driver had more extensions than we reserved, then
+    * bail out.
+    */
+   if (i == ARRAY_SIZE(__driDriverExtensions)) {
+      __driDriverExtensions[0] = NULL;
+      fprintf(stderr, "Megadriver stub did not reserve enough extension "
+              "slots.\n");
+      return;
+   }
+}
+
+#endif /* _GNU_SOURCE */
 
 static const
 __DRIconfig **stub_error_init_screen(__DRIscreen *psp)