diff --git a/src/GLX/Makefile.am b/src/GLX/Makefile.am index 49a352c..c1d7f20 100644 --- a/src/GLX/Makefile.am +++ b/src/GLX/Makefile.am @@ -57,6 +57,7 @@ libGLX_la_LIBADD += $(UTIL_DIR)/libglvnd_pthread.la libGLX_la_LIBADD += $(UTIL_DIR)/libutils_misc.la libGLX_la_LIBADD += $(UTIL_DIR)/libapp_error_check.la libGLX_la_LIBADD += $(UTIL_DIR)/libwinsys_dispatch.la +libGLX_la_LIBADD += $(UTIL_DIR)/libcJSON.la libGLX_la_LDFLAGS = -shared -Wl,-Bsymbolic -version-info 0 $(LINKER_FLAG_NO_UNDEFINED) diff --git a/src/GLX/libglx.c b/src/GLX/libglx.c index 1ef83eb..69c845e 100644 --- a/src/GLX/libglx.c +++ b/src/GLX/libglx.c @@ -2102,10 +2102,20 @@ void _init(void) * Check if we need to pre-load any vendors specified via environment * variable. */ - const char *preloadedVendor = getenv("__GLX_VENDOR_LIBRARY_NAME"); + const char *preloadedVendor = getenv("__GLX_VENDOR_LIBRARY_NAME"); if (preloadedVendor) { __glXLookupVendorByName(preloadedVendor); + } else { + const char *preloadedVendorFilenames = getenv("__GLX_VENDOR_LIBRARY_FILENAMES"); + char **tokens; + tokens = SplitString(preloadedVendorFilenames, NULL, ":"); + if (tokens != NULL) { + for (int i = 0; tokens[i] != NULL; ++i) { + __glXLookupVendorFromConfigFile(tokens[i]); + } + free(tokens); + } } } diff --git a/src/GLX/libglxmapping.c b/src/GLX/libglxmapping.c index ff2c3a6..d9db0fb 100644 --- a/src/GLX/libglxmapping.c +++ b/src/GLX/libglxmapping.c @@ -29,6 +29,9 @@ #include #include +#include +#include + #if defined(HASH_DEBUG) # include #endif @@ -44,6 +47,11 @@ #include "lkdhash.h" +#include "cJSON.h" + +#define FILE_FORMAT_VERSION_MAJOR 1 +#define FILE_FORMAT_VERSION_MINOR 0 + #define _GNU_SOURCE 1 #if !defined(FALLBACK_VENDOR_NAME) @@ -380,6 +388,261 @@ static void *VendorGetProcAddressCallback(const char *procName, void *param) return vendor->glxvc->getProcAddress((const GLubyte *) procName); } +static bool CheckFormatVersion(const char *versionStr) +{ + int major, minor, rev; + int len; + + major = minor = rev = -1; + len = sscanf(versionStr, "%d.%d.%d", &major, &minor, &rev); + if (len < 1) { + return false; + } + if (len < 2) { + minor = 0; + } + if (len < 3) { + rev = 0; + } + if (major != FILE_FORMAT_VERSION_MAJOR) { + return false; + } + + if (minor > FILE_FORMAT_VERSION_MINOR) { + return false; + } + return true; +} + +static cJSON *ReadJSONFile(const char *filename) +{ + FILE *in = NULL; + char *buf = NULL; + cJSON *root = NULL; + struct stat st; + + in = fopen(filename, "r"); + if (in == NULL) { + goto done; + } + + if (fstat(fileno(in), &st) != 0) { + goto done; + } + + buf = (char *) malloc(st.st_size + 1); + if (buf == NULL) { + goto done; + } + + if (fread(buf, st.st_size, 1, in) != 1) { + goto done; + } + buf[st.st_size] = '\0'; + + root = cJSON_Parse(buf); + +done: + if (in != NULL) { + fclose(in); + } + if (buf != NULL) { + free(buf); + } + return root; +} + +static char* constructVendorName(const char* libraryPath) +{ + int start = strrchr(libraryPath, '/') - libraryPath + strlen("libGLX_") + 1; + int vendorNameLen = strlen(strstr(libraryPath + start, ".so")); + char *vendorName = malloc(vendorNameLen); + strncpy(vendorName, libraryPath + start, vendorNameLen); + vendorName[vendorNameLen] = '\0'; + + return vendorName; +} + +__GLXvendorInfo* __glXLookupVendorFromConfigFile(const char *filename) +{ + __GLXvendorInfo *vendor = NULL; + cJSON *root; + cJSON *node; + cJSON *icdNode; + const char *libraryPath; + + root = ReadJSONFile(filename); + if (root == NULL) { + goto done; + } + + node = cJSON_GetObjectItem(root, "file_format_version"); + if (node == NULL || node->type != cJSON_String) { + goto done; + } + if (!CheckFormatVersion(node->valuestring)) { + goto done; + } + + icdNode = cJSON_GetObjectItem(root, "ICD"); + if (icdNode == NULL || icdNode->type != cJSON_Object) { + goto done; + } + + node = cJSON_GetObjectItem(icdNode, "library_path"); + if (node == NULL || node->type != cJSON_String) { + goto done; + } + libraryPath = node->valuestring; + + char* vendorName = constructVendorName(libraryPath); + + vendor = __glXLookupVendorByLibraryPath(libraryPath, vendorName); + + free(vendorName); + +done: + if (root != NULL) { + cJSON_Delete(root); + } + return vendor; +} + + +__GLXvendorInfo *__glXLookupVendorByLibraryPath(const char *libraryPath, const char *vendorName) +{ + __GLXvendorNameHash *pEntry = NULL; + Bool locked = False; + size_t vendorNameLen; + + // We'll use the vendor name to construct a DSO name, so make sure it + // doesn't contain any '/' characters. + if (strchr(vendorName, '/') != NULL) { + return NULL; + } + + vendorNameLen = strlen(vendorName); + + LKDHASH_RDLOCK(__glXVendorNameHash); + HASH_FIND(hh, _LH(__glXVendorNameHash), vendorName, vendorNameLen, pEntry); + + LKDHASH_UNLOCK(__glXVendorNameHash); + + if (!pEntry) { + LKDHASH_WRLOCK(__glXVendorNameHash); + locked = True; + // Do another lookup to check uniqueness + HASH_FIND(hh, _LH(__glXVendorNameHash), vendorName, vendorNameLen, pEntry); + if (!pEntry) { + __GLXvendorInfo *vendor; + __PFNGLXMAINPROC glxMainProc; + int i, count; + Bool success; + + // Previously unseen vendor. dlopen() the new vendor and add it to the + // hash table. + pEntry = calloc(1, sizeof(*pEntry) + vendorNameLen + 1); + if (!pEntry) { + goto fail; + } + vendor = &pEntry->vendor; + + vendor->glxvc = &pEntry->imports; + vendor->name = (char *) (pEntry + 1); + memcpy(vendor->name, vendorName, vendorNameLen + 1); + + if (libraryPath) { + vendor->dlhandle = dlopen(libraryPath, RTLD_LAZY); + } + + if (vendor->dlhandle == NULL) { + goto fail; + } + + glxMainProc = dlsym(vendor->dlhandle, __GLX_MAIN_PROTO_NAME); + if (!glxMainProc) { + goto fail; + } + + vendor->vendorID = __glDispatchNewVendorID(); + assert(vendor->vendorID >= 0); + + vendor->glDispatch = (__GLdispatchTable *) + __glDispatchCreateTable( + VendorGetProcAddressCallback, + vendor + ); + if (!vendor->glDispatch) { + goto fail; + } + + /* Initialize the dynamic dispatch table */ + vendor->dynDispatch = __glvndWinsysVendorDispatchCreate(); + if (vendor->dynDispatch == NULL) { + goto fail; + } + + success = (*glxMainProc)(GLX_VENDOR_ABI_VERSION, + &glxExportsTable, + vendor, &pEntry->imports); + if (!success) { + goto fail; + } + + // Make sure all the required functions are there. + if (pEntry->imports.isScreenSupported == NULL + || pEntry->imports.getProcAddress == NULL + || pEntry->imports.getDispatchAddress == NULL + || pEntry->imports.setDispatchIndex == NULL) + { + goto fail; + } + + if (!LookupVendorEntrypoints(vendor)) { + goto fail; + } + + // Check to see whether this vendor library can support entrypoint + // patching. + if (pEntry->imports.isPatchSupported != NULL + && pEntry->imports.initiatePatch != NULL) { + pEntry->patchCallbacks.isPatchSupported = pEntry->imports.isPatchSupported; + pEntry->patchCallbacks.initiatePatch = pEntry->imports.initiatePatch; + pEntry->patchCallbacks.releasePatch = pEntry->imports.releasePatch; + pEntry->patchCallbacks.threadAttach = pEntry->imports.patchThreadAttach; + pEntry->vendor.patchCallbacks = &pEntry->patchCallbacks; + } + + HASH_ADD_KEYPTR(hh, _LH(__glXVendorNameHash), vendor->name, + strlen(vendor->name), pEntry); + + // Look up the dispatch functions for any GLX extensions that we + // generated entrypoints for. + glvndUpdateEntrypoints(GLXEntrypointUpdateCallback, vendor); + + // Tell the vendor the index of all of the GLX dispatch stubs. + count = __glvndWinsysDispatchGetCount(); + for (i=0; iglxvc->setDispatchIndex((const GLubyte *) procName, i); + } + } + LKDHASH_UNLOCK(__glXVendorNameHash); + } + + return &pEntry->vendor; + +fail: + if (locked) { + LKDHASH_UNLOCK(__glXVendorNameHash); + } + if (pEntry != NULL) { + CleanupVendorNameEntry(NULL, pEntry); + free(pEntry); + } + return NULL; +} + __GLXvendorInfo *__glXLookupVendorByName(const char *vendorName) { __GLXvendorNameHash *pEntry = NULL; @@ -558,6 +821,19 @@ __GLXvendorInfo *__glXLookupVendorByScreen(Display *dpy, const int screen) if (specifiedVendorName) { vendor = __glXLookupVendorByName(specifiedVendorName); + } else { + specifiedVendorName = getenv("__GLX_VENDOR_LIBRARY_FILENAMES"); + + if (specifiedVendorName) { + char **tokens; + tokens = SplitString(specifiedVendorName, NULL, ":"); + if (tokens != NULL) { + for (int i = 0; tokens[i] != NULL; ++i) { + vendor = __glXLookupVendorFromConfigFile(tokens[i]); + } + free(tokens); + } + } } if (!vendor) { diff --git a/src/GLX/libglxmapping.h b/src/GLX/libglxmapping.h index 3ad1886..e195379 100644 --- a/src/GLX/libglxmapping.h +++ b/src/GLX/libglxmapping.h @@ -124,6 +124,14 @@ __GLXextFuncPtr __glXGetGLXDispatchAddress(const GLubyte *procName); __GLXvendorInfo *__glXLookupVendorByName(const char *vendorName); __GLXvendorInfo *__glXLookupVendorByScreen(Display *dpy, const int screen); +/*! + * Looks up the vendor by configuration file. This has the side effect of + * loading the vendor library if it has not been previously loaded. + * The functionality is similar to __EGL_VENDOR_LIBRARY_FILENAMES. + */ +__GLXvendorInfo *__glXLookupVendorFromConfigFile(const char *filename); +__GLXvendorInfo *__glXLookupVendorByLibraryPath(const char *libraryPath, const char *vendorName); + /*! * Looks up the __GLXdisplayInfo structure for a display, creating it if * necessary. diff --git a/src/GLX/meson.build b/src/GLX/meson.build index d0e93bb..830d57e 100644 --- a/src/GLX/meson.build +++ b/src/GLX/meson.build @@ -41,6 +41,7 @@ libGLX = shared_library( include_directories : [inc_include], link_args : '-Wl,-Bsymbolic', dependencies : [ + idep_cjson, dep_dl, dep_x11, dep_xext, dep_glproto, idep_gldispatch, idep_trace, idep_glvnd_pthread, idep_utils_misc,