1 #include "libeglvendor.h"
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <dlfcn.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <fnmatch.h>
11 #include <dirent.h>
12 
13 #include "glvnd_pthread.h"
14 #include "libeglcurrent.h"
15 #include "libeglmapping.h"
16 #include "utils_misc.h"
17 #include "glvnd_list.h"
18 #include "cJSON.h"
19 #include "egldispatchstubs.h"
20 
21 #define FILE_FORMAT_VERSION_MAJOR 1
22 #define FILE_FORMAT_VERSION_MINOR 0
23 
24 static void LoadVendors(void);
25 static void TeardownVendor(__EGLvendorInfo *vendor);
26 static __EGLvendorInfo *LoadVendor(const char *filename, const char *jsonPath);
27 
28 static void LoadVendorsFromConfigDir(const char *dirName);
29 static __EGLvendorInfo *LoadVendorFromConfigFile(const char *filename);
30 static cJSON *ReadJSONFile(const char *filename);
31 
32 static glvnd_once_t loadVendorsOnceControl = GLVND_ONCE_INIT;
33 static struct glvnd_list __eglVendorList;
34 
LoadVendors(void)35 void LoadVendors(void)
36 {
37     const char *env = NULL;
38     char **tokens;
39     int i;
40 
41     // First, check to see if a list of vendors was specified.
42     if (getuid() == geteuid() && getgid() == getegid()) {
43         env = getenv("__EGL_VENDOR_LIBRARY_FILENAMES");
44     }
45     if (env != NULL) {
46         tokens = SplitString(env, NULL, ":");
47         if (tokens != NULL) {
48             for (i=0; tokens[i] != NULL; i++) {
49                 LoadVendorFromConfigFile(tokens[i]);
50             }
51             free(tokens);
52         }
53         return;
54     }
55 
56     // We didn't get a list of vendors, so look through the vendor config
57     // directories.
58     if (getuid() == geteuid() && getgid() == getegid()) {
59         env = getenv("__EGL_VENDOR_LIBRARY_DIRS");
60     }
61     if (env == NULL) {
62         env = DEFAULT_EGL_VENDOR_CONFIG_DIRS;
63     }
64 
65     tokens = SplitString(env, NULL, ":");
66     if (tokens != NULL) {
67         for (i=0; tokens[i] != NULL; i++) {
68             LoadVendorsFromConfigDir(tokens[i]);
69         }
70         free(tokens);
71     }
72 }
73 
ScandirFilter(const struct dirent * ent)74 static int ScandirFilter(const struct dirent *ent)
75 {
76 #if defined(HAVE_DIRENT_DTYPE)
77     // Ignore the entry if we know that it's not a regular file or symlink.
78     if (ent->d_type != DT_REG && ent->d_type != DT_LNK && ent->d_type != DT_UNKNOWN) {
79         return 0;
80     }
81 #endif
82 
83     // Otherwise, select any JSON files.
84     if (fnmatch("*.json", ent->d_name, 0) == 0) {
85         return 1;
86     } else {
87         return 0;
88     }
89 }
90 
CompareFilenames(const struct dirent ** ent1,const struct dirent ** ent2)91 static int CompareFilenames(const struct dirent **ent1, const struct dirent **ent2)
92 {
93     return strcmp((*ent1)->d_name, (*ent2)->d_name);
94 }
95 
LoadVendorsFromConfigDir(const char * dirName)96 void LoadVendorsFromConfigDir(const char *dirName)
97 {
98     struct dirent **entries = NULL;
99     size_t dirnameLen;
100     const char *pathSep;
101     int count;
102     int i;
103 
104     count = scandir(dirName, &entries, ScandirFilter, CompareFilenames);
105     if (count <= 0) {
106         return;
107     }
108 
109     // Check if dirName ends with a "/" character. If it doesn't, then we need
110     // to add one when we construct the full file paths below.
111     dirnameLen = strlen(dirName);
112     if (dirnameLen > 0 && dirName[dirnameLen - 1] != '/') {
113         pathSep = "/";
114     } else {
115         pathSep = "";
116     }
117 
118     for (i=0; i<count; i++) {
119         char *path = NULL;
120         if (glvnd_asprintf(&path, "%s%s%s", dirName, pathSep, entries[i]->d_name) > 0) {
121             LoadVendorFromConfigFile(path);
122             free(path);
123         } else {
124             fprintf(stderr, "ERROR: Could not allocate vendor library path name\n");
125         }
126         free(entries[i]);
127     }
128 
129     free(entries);
130 }
131 
__eglInitVendors(void)132 void __eglInitVendors(void)
133 {
134     glvnd_list_init(&__eglVendorList);
135 }
136 
__eglLoadVendors(void)137 struct glvnd_list *__eglLoadVendors(void)
138 {
139     __glvndPthreadFuncs.once(&loadVendorsOnceControl, LoadVendors);
140     return &__eglVendorList;
141 }
142 
__eglTeardownVendors(void)143 void __eglTeardownVendors(void)
144 {
145     __EGLvendorInfo *vendor;
146     __EGLvendorInfo *vendorTemp;
147 
148     glvnd_list_for_each_entry_safe(vendor, vendorTemp, &__eglVendorList, entry) {
149         glvnd_list_del(&vendor->entry);
150         __glDispatchForceUnpatch(vendor->vendorID);
151         TeardownVendor(vendor);
152     }
153 }
154 
155 const __EGLapiExports __eglExportsTable = {
156     __eglThreadInitialize, // threadInit
157     __eglQueryAPI, // getCurrentApi
158     __eglGetCurrentVendor, // getCurrentVendor
159     __eglGetCurrentContext, // getCurrentContext
160     __eglGetCurrentDisplay, // getCurrentDisplay
161     __eglGetCurrentSurface, // getCurrentSurface
162     __eglFetchDispatchEntry, // fetchDispatchEntry
163     __eglSetError, // setEGLError
164     __eglSetLastVendor, // setLastVendor
165     __eglGetVendorFromDisplay, // getVendorFromDisplay
166     __eglGetVendorFromDevice, // getVendorFromDevice
167     __eglAddDevice, // setVendorForDevice
168 };
169 
TeardownVendor(__EGLvendorInfo * vendor)170 void TeardownVendor(__EGLvendorInfo *vendor)
171 {
172     if (vendor->glDispatch) {
173         __glDispatchDestroyTable(vendor->glDispatch);
174     }
175 
176     /* Clean up the dynamic dispatch table */
177     if (vendor->dynDispatch != NULL) {
178         __glvndWinsysVendorDispatchDestroy(vendor->dynDispatch);
179         vendor->dynDispatch = NULL;
180     }
181 
182     if (vendor->dlhandle != NULL) {
183         dlclose(vendor->dlhandle);
184     }
185 
186     free(vendor);
187 }
188 
LookupVendorEntrypoints(__EGLvendorInfo * vendor)189 static GLboolean LookupVendorEntrypoints(__EGLvendorInfo *vendor)
190 {
191     memset(&vendor->staticDispatch, 0, sizeof(vendor->staticDispatch));
192 
193     // TODO: A lot of these should be implemented (and probably generated) as
194     // normal EGL dispatch functions, instead of having to special-case them.
195 
196 #define LOADENTRYPOINT(ptr, name) do { \
197     vendor->staticDispatch.ptr = vendor->eglvc.getProcAddress(name); \
198     if (vendor->staticDispatch.ptr == NULL) { return GL_FALSE; } \
199     } while(0)
200 
201     LOADENTRYPOINT(initialize,                    "eglInitialize"                    );
202     LOADENTRYPOINT(chooseConfig,                  "eglChooseConfig"                  );
203     LOADENTRYPOINT(copyBuffers,                   "eglCopyBuffers"                   );
204     LOADENTRYPOINT(createContext,                 "eglCreateContext"                 );
205     LOADENTRYPOINT(createPbufferSurface,          "eglCreatePbufferSurface"          );
206     LOADENTRYPOINT(createPixmapSurface,           "eglCreatePixmapSurface"           );
207     LOADENTRYPOINT(createWindowSurface,           "eglCreateWindowSurface"           );
208     LOADENTRYPOINT(destroyContext,                "eglDestroyContext"                );
209     LOADENTRYPOINT(destroySurface,                "eglDestroySurface"                );
210     LOADENTRYPOINT(getConfigAttrib,               "eglGetConfigAttrib"               );
211     LOADENTRYPOINT(getConfigs,                    "eglGetConfigs"                    );
212     LOADENTRYPOINT(makeCurrent,                   "eglMakeCurrent"                   );
213     LOADENTRYPOINT(queryContext,                  "eglQueryContext"                  );
214     LOADENTRYPOINT(queryString,                   "eglQueryString"                   );
215     LOADENTRYPOINT(querySurface,                  "eglQuerySurface"                  );
216     LOADENTRYPOINT(swapBuffers,                   "eglSwapBuffers"                   );
217     LOADENTRYPOINT(terminate,                     "eglTerminate"                     );
218     LOADENTRYPOINT(waitGL,                        "eglWaitGL"                        );
219     LOADENTRYPOINT(waitNative,                    "eglWaitNative"                    );
220     LOADENTRYPOINT(bindTexImage,                  "eglBindTexImage"                  );
221     LOADENTRYPOINT(releaseTexImage,               "eglReleaseTexImage"               );
222     LOADENTRYPOINT(surfaceAttrib,                 "eglSurfaceAttrib"                 );
223     LOADENTRYPOINT(swapInterval,                  "eglSwapInterval"                  );
224     LOADENTRYPOINT(createPbufferFromClientBuffer, "eglCreatePbufferFromClientBuffer" );
225     LOADENTRYPOINT(releaseThread,                 "eglReleaseThread"                 );
226     LOADENTRYPOINT(waitClient,                    "eglWaitClient"                    );
227     LOADENTRYPOINT(getError,                      "eglGetError"                      );
228 #undef LOADENTRYPOINT
229 
230     // The remaining functions here are optional.
231 #define LOADENTRYPOINT(ptr, name) \
232     vendor->staticDispatch.ptr = vendor->eglvc.getProcAddress(name);
233 
234     LOADENTRYPOINT(bindAPI,                       "eglBindAPI"                       );
235     LOADENTRYPOINT(createSync,                    "eglCreateSync"                    );
236     LOADENTRYPOINT(destroySync,                   "eglDestroySync"                   );
237     LOADENTRYPOINT(clientWaitSync,                "eglClientWaitSync"                );
238     LOADENTRYPOINT(getSyncAttrib,                 "eglGetSyncAttrib"                 );
239     LOADENTRYPOINT(createImage,                   "eglCreateImage"                   );
240     LOADENTRYPOINT(destroyImage,                  "eglDestroyImage"                  );
241     LOADENTRYPOINT(createPlatformWindowSurface,   "eglCreatePlatformWindowSurface"   );
242     LOADENTRYPOINT(createPlatformPixmapSurface,   "eglCreatePlatformPixmapSurface"   );
243     LOADENTRYPOINT(waitSync,                      "eglWaitSync"                      );
244     LOADENTRYPOINT(queryDevicesEXT,               "eglQueryDevicesEXT"               );
245 
246     LOADENTRYPOINT(debugMessageControlKHR,        "eglDebugMessageControlKHR"        );
247     LOADENTRYPOINT(queryDebugKHR,                 "eglQueryDebugKHR"                 );
248     LOADENTRYPOINT(labelObjectKHR,                "eglLabelObjectKHR"                );
249 #undef LOADENTRYPOINT
250 
251     // eglQueryDisplayAttrib has KHR, EXT, and NV versions. They're all
252     // interchangeable, but the vendor might not support all of them.
253     vendor->staticDispatch.queryDisplayAttrib =
254         vendor->eglvc.getProcAddress("eglQueryDisplayAttribKHR");
255     if (vendor->staticDispatch.queryDisplayAttrib == NULL) {
256         vendor->staticDispatch.queryDisplayAttrib =
257             vendor->eglvc.getProcAddress("eglQueryDisplayAttribEXT");
258     }
259     if (vendor->staticDispatch.queryDisplayAttrib == NULL) {
260         vendor->staticDispatch.queryDisplayAttrib =
261             vendor->eglvc.getProcAddress("eglQueryDisplayAttribNV");
262     }
263 
264     return GL_TRUE;
265 }
266 
VendorGetProcAddressCallback(const char * procName,void * param)267 static void *VendorGetProcAddressCallback(const char *procName, void *param)
268 {
269     __EGLvendorInfo *vendor = (__EGLvendorInfo *) param;
270     return vendor->eglvc.getProcAddress(procName);
271 }
272 
CheckFormatVersion(const char * versionStr)273 static EGLBoolean CheckFormatVersion(const char *versionStr)
274 {
275     int major, minor, rev;
276     int len;
277 
278     major = minor = rev = -1;
279     len = sscanf(versionStr, "%d.%d.%d", &major, &minor, &rev);
280     if (len < 1) {
281         return EGL_FALSE;
282     }
283     if (len < 2) {
284         minor = 0;
285     }
286     if (len < 3) {
287         rev = 0;
288     }
289     if (major != FILE_FORMAT_VERSION_MAJOR) {
290         return EGL_FALSE;
291     }
292 
293     // The minor version number will be incremented if we ever add an optional
294     // value to the JSON format that libEGL has to pay attention to. That is,
295     // an older vendor library will still work, but a vendor library with a
296     // newer format than this library understands should fail.
297     if (minor > FILE_FORMAT_VERSION_MINOR) {
298         return EGL_FALSE;
299     }
300     return EGL_TRUE;
301 }
302 
LoadVendorFromConfigFile(const char * filename)303 static __EGLvendorInfo *LoadVendorFromConfigFile(const char *filename)
304 {
305     __EGLvendorInfo *vendor = NULL;
306     cJSON *root;
307     cJSON *node;
308     cJSON *icdNode;
309     const char *libraryPath;
310 
311     root = ReadJSONFile(filename);
312     if (root == NULL) {
313         goto done;
314     }
315 
316     node = cJSON_GetObjectItem(root, "file_format_version");
317     if (node == NULL || node->type != cJSON_String) {
318         goto done;
319     }
320     if (!CheckFormatVersion(node->valuestring)) {
321         goto done;
322     }
323 
324     icdNode = cJSON_GetObjectItem(root, "ICD");
325     if (icdNode == NULL || icdNode->type != cJSON_Object) {
326         goto done;
327     }
328 
329     node = cJSON_GetObjectItem(icdNode, "library_path");
330     if (node == NULL || node->type != cJSON_String) {
331         goto done;
332     }
333     libraryPath = node->valuestring;
334     vendor = LoadVendor(libraryPath, filename);
335 
336 done:
337     if (root != NULL) {
338         cJSON_Delete(root);
339     }
340     if (vendor != NULL) {
341         glvnd_list_append(&vendor->entry, &__eglVendorList);
342     }
343     return vendor;
344 }
345 
ReadJSONFile(const char * filename)346 static cJSON *ReadJSONFile(const char *filename)
347 {
348     FILE *in = NULL;
349     char *buf = NULL;
350     cJSON *root = NULL;
351     struct stat st;
352 
353     in = fopen(filename, "r");
354     if (in == NULL) {
355         goto done;
356     }
357 
358     if (fstat(fileno(in), &st) != 0) {
359         goto done;
360     }
361 
362     buf = (char *) malloc(st.st_size + 1);
363     if (buf == NULL) {
364         goto done;
365     }
366 
367     if (fread(buf, st.st_size, 1, in) != 1) {
368         goto done;
369     }
370     buf[st.st_size] = '\0';
371 
372     root = cJSON_Parse(buf);
373 
374 done:
375     if (in != NULL) {
376         fclose(in);
377     }
378     if (buf != NULL) {
379         free(buf);
380     }
381     return root;
382 }
383 
CheckVendorExtensionString(__EGLvendorInfo * vendor,const char * str)384 static void CheckVendorExtensionString(__EGLvendorInfo *vendor, const char *str)
385 {
386     static const char NAME_DEVICE_BASE[] = "EGL_EXT_device_base";
387     static const char NAME_DEVICE_ENUM[] = "EGL_EXT_device_enumeration";
388     static const char NAME_PLATFORM_DEVICE[] = "EGL_EXT_platform_device";
389     static const char NAME_MESA_PLATFORM_GBM[] = "EGL_MESA_platform_gbm";
390     static const char NAME_KHR_PLATFORM_GBM[] = "EGL_KHR_platform_gbm";
391     static const char NAME_EXT_PLATFORM_WAYLAND[] = "EGL_EXT_platform_wayland";
392     static const char NAME_KHR_PLATFORM_WAYLAND[] = "EGL_KHR_platform_wayland";
393     static const char NAME_EXT_PLATFORM_X11[] = "EGL_EXT_platform_x11";
394     static const char NAME_KHR_PLATFORM_X11[] = "EGL_KHR_platform_x11";
395 
396     if (str == NULL || str[0] == '\x00') {
397         return;
398     }
399 
400     if (!vendor->supportsDevice) {
401         if (IsTokenInString(str, NAME_DEVICE_BASE, sizeof(NAME_DEVICE_BASE) - 1, " ")
402                 || IsTokenInString(str, NAME_DEVICE_ENUM, sizeof(NAME_DEVICE_ENUM) - 1, " ")) {
403             vendor->supportsDevice = EGL_TRUE;
404         }
405     }
406 
407     if (!vendor->supportsPlatformDevice) {
408         if (IsTokenInString(str, NAME_PLATFORM_DEVICE, sizeof(NAME_PLATFORM_DEVICE) - 1, " ")) {
409             vendor->supportsPlatformDevice = EGL_TRUE;
410         }
411     }
412 
413     if (!vendor->supportsPlatformGbm) {
414         if (IsTokenInString(str, NAME_MESA_PLATFORM_GBM, sizeof(NAME_MESA_PLATFORM_GBM) - 1, " ")
415                 || IsTokenInString(str, NAME_KHR_PLATFORM_GBM, sizeof(NAME_KHR_PLATFORM_GBM) - 1, " ")) {
416             vendor->supportsPlatformGbm = EGL_TRUE;
417         }
418     }
419 
420     if (!vendor->supportsPlatformWayland) {
421         if (IsTokenInString(str, NAME_EXT_PLATFORM_WAYLAND, sizeof(NAME_EXT_PLATFORM_WAYLAND) - 1, " ")
422                 || IsTokenInString(str, NAME_KHR_PLATFORM_WAYLAND, sizeof(NAME_KHR_PLATFORM_WAYLAND) - 1, " ")) {
423             vendor->supportsPlatformWayland = EGL_TRUE;
424         }
425     }
426 
427     if (!vendor->supportsPlatformX11) {
428         if (IsTokenInString(str, NAME_EXT_PLATFORM_X11, sizeof(NAME_EXT_PLATFORM_X11) - 1, " ")
429                 || IsTokenInString(str, NAME_KHR_PLATFORM_X11, sizeof(NAME_KHR_PLATFORM_X11) - 1, " ")) {
430             vendor->supportsPlatformX11 = EGL_TRUE;
431         }
432     }
433 }
434 
CheckVendorExtensions(__EGLvendorInfo * vendor)435 static void CheckVendorExtensions(__EGLvendorInfo *vendor)
436 {
437     CheckVendorExtensionString(vendor,
438             vendor->staticDispatch.queryString(EGL_NO_DISPLAY, EGL_EXTENSIONS));
439 
440     if (vendor->eglvc.getVendorString != NULL) {
441         CheckVendorExtensionString(vendor,
442                 vendor->eglvc.getVendorString(__EGL_VENDOR_STRING_PLATFORM_EXTENSIONS));
443     }
444 
445     if (vendor->staticDispatch.queryDevicesEXT == NULL) {
446         vendor->supportsDevice = EGL_FALSE;
447     }
448 
449     if (!vendor->supportsDevice) {
450         vendor->supportsPlatformDevice = EGL_FALSE;
451     }
452 }
453 
LoadVendor(const char * filename,const char * jsonPath)454 static __EGLvendorInfo *LoadVendor(const char *filename, const char *jsonPath)
455 {
456     char *absolutePath = NULL;
457     char *jsonDir = NULL;
458     const char *dlopenName;
459     __PFNEGLMAINPROC eglMainProc;
460     __EGLvendorInfo *vendor = NULL;
461     __EGLvendorInfo *otherVendor;
462     int i;
463 
464     // Allocate the vendor structure, plus enough room for a copy of its name.
465     vendor = (__EGLvendorInfo *) calloc(1, sizeof(__EGLvendorInfo));
466     if (vendor == NULL) {
467         return NULL;
468     }
469 
470     if (filename == NULL) {
471         // Clearly not going to work
472         goto fail;
473     }
474     else if (filename[0] == '/') {
475         // filename is an absolute path, no special handling needed
476         // e.g. /usr/lib/libEGL_myvendor.so.0
477         dlopenName = filename;
478     }
479     else if (strchr(filename, '/') == NULL) {
480         // filename is a bare SONAME, no special handling needed
481         // e.g. libEGL_myvendor.so.0
482         dlopenName = filename;
483     }
484     else {
485         char *slash;
486 
487         // filename is a relative path; we have to interpret it as
488         // relative to *somewhere*. dlopen() would interpret it as relative
489         // to the current working directory, but that seems unlikely to be
490         // useful. Instead, follow Vulkan by interpreting it as relative
491         // to the directory where we found the ICD.
492         // e.g. it might be ../../../$LIB/libEGL_myvendor.so.0
493 
494         // Resolve symlinks and relative components in jsonPath. This assumes
495         // a POSIX.1-2008-compliant realpath(), similar to the implementations
496         // in glibc and musl.
497         jsonDir = realpath(jsonPath, NULL);
498         if (jsonDir == NULL) {
499             goto fail;
500         }
501 
502         // Truncate jsonDir at the last slash to get the directory,
503         // e.g. /usr/share/glvnd/egl_vendor.d
504         slash = strrchr(jsonDir, '/');
505         if (slash == NULL) {
506             // Shouldn't happen, because the output of realpath() is absolute;
507             // recover by just not loading it
508             goto fail;
509         }
510         *slash = '\0';
511 
512         // Concatenate jsonDir and filename
513         // e.g. /usr/share/glvnd/egl_vendor.d/../../../$LIB/libEGL_myvendor.so.0
514         if (glvnd_asprintf(&absolutePath, "%s/%s", jsonDir, filename) < 0) {
515             goto fail;
516         }
517 
518         dlopenName = absolutePath;
519     }
520 
521     vendor->dlhandle = dlopen(dlopenName, RTLD_LAZY);
522     if (vendor->dlhandle == NULL) {
523         goto fail;
524     }
525 
526     // Check if this vendor was already loaded under a different name.
527     glvnd_list_for_each_entry(otherVendor, &__eglVendorList, entry) {
528         if (otherVendor->dlhandle == vendor->dlhandle) {
529             goto fail;
530         }
531     }
532 
533     eglMainProc = dlsym(vendor->dlhandle, __EGL_MAIN_PROTO_NAME);
534     if (!eglMainProc) {
535         goto fail;
536     }
537 
538     if (!(*eglMainProc)(EGL_VENDOR_ABI_VERSION,
539                               &__eglExportsTable,
540                               vendor, &vendor->eglvc)) {
541         goto fail;
542     }
543 
544     // Make sure all the required functions are there.
545     if (vendor->eglvc.getPlatformDisplay == NULL
546             || vendor->eglvc.getSupportsAPI == NULL
547             || vendor->eglvc.getProcAddress == NULL
548             || vendor->eglvc.getDispatchAddress == NULL
549             || vendor->eglvc.setDispatchIndex == NULL) {
550         goto fail;
551     }
552 
553     if (vendor->eglvc.isPatchSupported != NULL
554             && vendor->eglvc.initiatePatch != NULL) {
555         vendor->patchCallbacks.isPatchSupported = vendor->eglvc.isPatchSupported;
556         vendor->patchCallbacks.initiatePatch = vendor->eglvc.initiatePatch;
557         vendor->patchCallbacks.releasePatch = vendor->eglvc.releasePatch;
558         vendor->patchCallbacks.threadAttach = vendor->eglvc.patchThreadAttach;
559         vendor->patchSupported = EGL_TRUE;
560     }
561 
562     if (!LookupVendorEntrypoints(vendor)) {
563         goto fail;
564     }
565 
566     vendor->supportsGL = vendor->eglvc.getSupportsAPI(EGL_OPENGL_API);
567     vendor->supportsGLES = vendor->eglvc.getSupportsAPI(EGL_OPENGL_ES_API);
568     if (!(vendor->supportsGL || vendor->supportsGLES)) {
569         goto fail;
570     }
571 
572     vendor->vendorID = __glDispatchNewVendorID();
573     assert(vendor->vendorID >= 0);
574 
575     // TODO: Allow per-context dispatch tables?
576     vendor->glDispatch = __glDispatchCreateTable(VendorGetProcAddressCallback, vendor);
577     if (!vendor->glDispatch) {
578         goto fail;
579     }
580 
581     CheckVendorExtensions(vendor);
582 
583     // Create and initialize the EGL dispatch table.
584     // This is called before trying to look up any vendor-supplied EGL dispatch
585     // functions, so we only need to add the EGL dispatch functions that are
586     // defined in libEGL itself.
587     vendor->dynDispatch = __glvndWinsysVendorDispatchCreate();
588     if (!vendor->dynDispatch) {
589         goto fail;
590     }
591     for (i=0; i<__EGL_DISPATCH_FUNC_COUNT; i++) {
592         vendor->eglvc.setDispatchIndex(
593                 __EGL_DISPATCH_FUNC_NAMES[i],
594                 __EGL_DISPATCH_FUNC_INDICES[i]);
595     }
596 
597     free(absolutePath);
598     free(jsonDir);
599     return vendor;
600 
601 fail:
602     if (vendor != NULL) {
603         TeardownVendor(vendor);
604     }
605     free(absolutePath);
606     free(jsonDir);
607     return NULL;
608 }
609 
610