• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
3  * Copyright (C) 2014-2016 Emil Velikov <emil.l.velikov@gmail.com>
4  * Copyright (C) 2016 Intel Corporation
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  *
25  * Authors:
26  *    Rob Clark <robclark@freedesktop.org>
27  */
28 
29 #include <dlfcn.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdbool.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <limits.h>
40 #include <sys/param.h>
41 #ifdef MAJOR_IN_MKDEV
42 #include <sys/mkdev.h>
43 #endif
44 #ifdef MAJOR_IN_SYSMACROS
45 #include <sys/sysmacros.h>
46 #endif
47 #include <GL/gl.h>
48 #include <GL/internal/dri_interface.h>
49 #include "loader.h"
50 #include "util/os_file.h"
51 
52 #ifdef HAVE_LIBDRM
53 #include <xf86drm.h>
54 #define MAX_DRM_DEVICES 64
55 #ifdef USE_DRICONF
56 #include "util/xmlconfig.h"
57 #include "util/driconf.h"
58 #endif
59 #endif
60 
61 #include "util/macros.h"
62 
63 #define __IS_LOADER
64 #include "pci_id_driver_map.h"
65 
66 /* For systems like Hurd */
67 #ifndef PATH_MAX
68 #define PATH_MAX 4096
69 #endif
70 
71 #include "ohos_log.h"
72 
default_logger(int level,const char * fmt,...)73 static void default_logger(int level, const char *fmt, ...)
74 {
75    const int MAX_BUFFER_LEN = 1024;
76    char log_string[MAX_BUFFER_LEN];
77    if (level <= _LOADER_WARNING) {
78       va_list args;
79       va_start(args, fmt);
80       vfprintf(stderr, fmt, args);
81       va_end(args);
82    }
83    DISPLAY_LOGI();
84 }
85 
ohos_logger(int level,const char * fmt,...)86 static void ohos_logger(int level, const char *fmt, ...)
87 {
88     const int MAX_BUFFER_LEN = 1024;
89     char log_string[MAX_BUFFER_LEN];
90     va_list args;
91     va_start(args, fmt);
92     (void)snprintf(log_string, MAX_BUFFER_LEN, fmt, args);
93     va_end(args);
94     switch (level) {
95         case _LOADER_WARNING:
96             DISPLAY_LOGW("%{public}s", log_string);
97             break;
98         case _LOADER_DEBUG:
99             DISPLAY_LOGD("%{public}s", log_string);
100             break;
101         case _LOADER_FATAL:
102             DISPLAY_LOGE("%{public}s", log_string);
103             break;
104         case _LOADER_INFO:
105             DISPLAY_LOGI("%{public}s", log_string);
106             break;
107         default:
108             break;
109     }
110     DISPLAY_LOGI();
111 }
112 
113 static loader_logger *log_ = ohos_logger;
114 
115 int
loader_open_device(const char * device_name)116 loader_open_device(const char *device_name)
117 {
118    log_(_LOADER_WARNING, "loader_open_device %s", device_name);
119    int fd;
120 #ifdef O_CLOEXEC
121    fd = open(device_name, O_RDWR | O_CLOEXEC);
122 #endif
123    if (fd == -1 && errno == EINVAL)
124    {
125       fd = open(device_name, O_RDWR);
126       if (fd != -1)
127          fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
128    }
129    if (fd == -1 && errno == EACCES) {
130       log_(_LOADER_WARNING, "failed to open %s: %s\n",
131            device_name, strerror(errno));
132    }
133    return fd;
134 }
135 
loader_get_kernel_driver_name(int fd)136 static char *loader_get_kernel_driver_name(int fd)
137 {
138 #if HAVE_LIBDRM
139    char *driver;
140    drmVersionPtr version = drmGetVersion(fd);
141 
142    if (!version) {
143       log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
144       return NULL;
145    }
146 
147    driver = strndup(version->name, version->name_len);
148    log_(driver ? _LOADER_DEBUG : _LOADER_WARNING, "using driver %s for %d\n",
149         driver, fd);
150 
151    drmFreeVersion(version);
152    return driver;
153 #else
154    return NULL;
155 #endif
156 }
157 
158 bool
is_kernel_i915(int fd)159 is_kernel_i915(int fd)
160 {
161    char *kernel_driver = loader_get_kernel_driver_name(fd);
162    bool is_i915 = kernel_driver && strcmp(kernel_driver, "i915") == 0;
163 
164    free(kernel_driver);
165    return is_i915;
166 }
167 
168 #if defined(HAVE_LIBDRM)
169 int
loader_open_render_node(const char * name)170 loader_open_render_node(const char *name)
171 {
172    drmDevicePtr devices[MAX_DRM_DEVICES], device;
173    int i, num_devices, fd = -1;
174 
175    num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
176    if (num_devices <= 0)
177       return -ENOENT;
178 
179    for (i = 0; i < num_devices; i++) {
180       device = devices[i];
181 
182       if ((device->available_nodes & (1 << DRM_NODE_RENDER)) &&
183           (device->bustype == DRM_BUS_PLATFORM)) {
184          drmVersionPtr version;
185 
186          fd = loader_open_device(device->nodes[DRM_NODE_RENDER]);
187          if (fd < 0)
188             continue;
189 
190          version = drmGetVersion(fd);
191          if (!version) {
192             close(fd);
193             continue;
194          }
195 
196          if (strcmp(version->name, name) != 0) {
197             drmFreeVersion(version);
198             close(fd);
199             continue;
200          }
201 
202          drmFreeVersion(version);
203          break;
204       }
205    }
206    drmFreeDevices(devices, num_devices);
207 
208    if (i == num_devices)
209       return -ENOENT;
210 
211    return fd;
212 }
213 
214 char *
loader_get_render_node(dev_t device)215 loader_get_render_node(dev_t device)
216 {
217    char *render_node = NULL;
218    drmDevicePtr dev_ptr;
219 
220    if (drmGetDeviceFromDevId(device, 0, &dev_ptr) < 0)
221       return NULL;
222 
223    if (dev_ptr->available_nodes & (1 << DRM_NODE_RENDER)) {
224       render_node = strdup(dev_ptr->nodes[DRM_NODE_RENDER]);
225       if (!render_node)
226          log_(_LOADER_DEBUG, "MESA-LOADER: failed to allocate memory for render node\n");
227    }
228 
229    drmFreeDevice(&dev_ptr);
230 
231    return render_node;
232 }
233 
234 #ifdef USE_DRICONF
235 static const driOptionDescription __driConfigOptionsLoader[] = {
236     DRI_CONF_SECTION_INITIALIZATION
237         DRI_CONF_DEVICE_ID_PATH_TAG()
238         DRI_CONF_DRI_DRIVER()
239     DRI_CONF_SECTION_END
240 };
241 
loader_get_dri_config_driver(int fd)242 static char *loader_get_dri_config_driver(int fd)
243 {
244    driOptionCache defaultInitOptions;
245    driOptionCache userInitOptions;
246    char *dri_driver = NULL;
247    char *kernel_driver = loader_get_kernel_driver_name(fd);
248 
249    driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader,
250                       ARRAY_SIZE(__driConfigOptionsLoader));
251    driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0,
252                        "loader", kernel_driver, NULL, NULL, 0, NULL, 0);
253    if (driCheckOption(&userInitOptions, "dri_driver", DRI_STRING)) {
254       char *opt = driQueryOptionstr(&userInitOptions, "dri_driver");
255       /* not an empty string */
256       if (*opt)
257          dri_driver = strdup(opt);
258    }
259    driDestroyOptionCache(&userInitOptions);
260    driDestroyOptionInfo(&defaultInitOptions);
261 
262    free(kernel_driver);
263    return dri_driver;
264 }
265 
loader_get_dri_config_device_id(void)266 static char *loader_get_dri_config_device_id(void)
267 {
268    driOptionCache defaultInitOptions;
269    driOptionCache userInitOptions;
270    char *prime = NULL;
271 
272    driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader,
273                       ARRAY_SIZE(__driConfigOptionsLoader));
274    driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0,
275                        "loader", NULL, NULL, NULL, 0, NULL, 0);
276    if (driCheckOption(&userInitOptions, "device_id", DRI_STRING))
277       prime = strdup(driQueryOptionstr(&userInitOptions, "device_id"));
278    driDestroyOptionCache(&userInitOptions);
279    driDestroyOptionInfo(&defaultInitOptions);
280 
281    return prime;
282 }
283 #endif
284 
drm_construct_id_path_tag(drmDevicePtr device)285 static char *drm_construct_id_path_tag(drmDevicePtr device)
286 {
287    char *tag = NULL;
288 
289    if (device->bustype == DRM_BUS_PCI) {
290       if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u",
291                    device->businfo.pci->domain,
292                    device->businfo.pci->bus,
293                    device->businfo.pci->dev,
294                    device->businfo.pci->func) < 0) {
295          return NULL;
296       }
297    } else if (device->bustype == DRM_BUS_PLATFORM ||
298               device->bustype == DRM_BUS_HOST1X) {
299       char *fullname, *name, *address;
300 
301       if (device->bustype == DRM_BUS_PLATFORM)
302          fullname = device->businfo.platform->fullname;
303       else
304          fullname = device->businfo.host1x->fullname;
305 
306       name = strrchr(fullname, '/');
307       if (!name)
308          name = strdup(fullname);
309       else
310          name = strdup(name + 1);
311 
312       address = strchr(name, '@');
313       if (address) {
314          *address++ = '\0';
315 
316          if (asprintf(&tag, "platform-%s_%s", address, name) < 0)
317             tag = NULL;
318       } else {
319          if (asprintf(&tag, "platform-%s", name) < 0)
320             tag = NULL;
321       }
322 
323       free(name);
324    }
325    return tag;
326 }
327 
drm_device_matches_tag(drmDevicePtr device,const char * prime_tag)328 static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag)
329 {
330    char *tag = drm_construct_id_path_tag(device);
331    int ret;
332 
333    if (tag == NULL)
334       return false;
335 
336    ret = strcmp(tag, prime_tag);
337 
338    free(tag);
339    return ret == 0;
340 }
341 
drm_get_id_path_tag_for_fd(int fd)342 static char *drm_get_id_path_tag_for_fd(int fd)
343 {
344    drmDevicePtr device;
345    char *tag;
346 
347    if (drmGetDevice2(fd, 0, &device) != 0)
348        return NULL;
349 
350    tag = drm_construct_id_path_tag(device);
351    drmFreeDevice(&device);
352    return tag;
353 }
354 
loader_get_user_preferred_fd(int default_fd,bool * different_device)355 int loader_get_user_preferred_fd(int default_fd, bool *different_device)
356 {
357    const char *dri_prime = getenv("DRI_PRIME");
358    char *default_tag, *prime = NULL;
359    drmDevicePtr devices[MAX_DRM_DEVICES];
360    int i, num_devices, fd = -1;
361 
362 #ifdef USE_DRICONF
363    if (dri_prime)
364       prime = strdup(dri_prime);
365    else
366       prime = loader_get_dri_config_device_id();
367 #else
368    if (dri_prime)
369       prime = strdup(dri_prime);
370 #endif
371 
372    if (prime == NULL) {
373       *different_device = false;
374       return default_fd;
375    }
376 
377    default_tag = drm_get_id_path_tag_for_fd(default_fd);
378    if (default_tag == NULL)
379       goto err;
380 
381    num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
382    if (num_devices <= 0)
383       goto err;
384 
385    for (i = 0; i < num_devices; i++) {
386       if (!(devices[i]->available_nodes & 1 << DRM_NODE_RENDER))
387          continue;
388 
389       /* two formats of DRI_PRIME are supported:
390        * "1": choose any other card than the card used by default.
391        * id_path_tag: (for example "pci-0000_02_00_0") choose the card
392        * with this id_path_tag.
393        */
394       if (!strcmp(prime,"1")) {
395          if (drm_device_matches_tag(devices[i], default_tag))
396             continue;
397       } else {
398          if (!drm_device_matches_tag(devices[i], prime))
399             continue;
400       }
401 
402       fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]);
403       break;
404    }
405    drmFreeDevices(devices, num_devices);
406 
407    if (i == num_devices)
408       goto err;
409 
410    if (fd < 0)
411       goto err;
412 
413    close(default_fd);
414 
415    *different_device = !!strcmp(default_tag, prime);
416 
417    free(default_tag);
418    free(prime);
419    return fd;
420 
421  err:
422    *different_device = false;
423 
424    free(default_tag);
425    free(prime);
426    return default_fd;
427 }
428 #else
429 int
loader_open_render_node(const char * name)430 loader_open_render_node(const char *name)
431 {
432    return -1;
433 }
434 
435 char *
loader_get_render_node(dev_t device)436 loader_get_render_node(dev_t device)
437 {
438    return NULL;
439 }
440 
loader_get_user_preferred_fd(int default_fd,bool * different_device)441 int loader_get_user_preferred_fd(int default_fd, bool *different_device)
442 {
443    *different_device = false;
444    return default_fd;
445 }
446 #endif
447 
448 #if defined(HAVE_LIBDRM)
449 
450 static bool
drm_get_pci_id_for_fd(int fd,int * vendor_id,int * chip_id)451 drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
452 {
453    drmDevicePtr device;
454 
455    if (drmGetDevice2(fd, 0, &device) != 0) {
456       log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n");
457       return false;
458    }
459 
460    if (device->bustype != DRM_BUS_PCI) {
461       drmFreeDevice(&device);
462       log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n");
463       return false;
464    }
465 
466    *vendor_id = device->deviceinfo.pci->vendor_id;
467    *chip_id = device->deviceinfo.pci->device_id;
468    drmFreeDevice(&device);
469    return true;
470 }
471 #endif
472 
473 #ifdef __linux__
loader_get_linux_pci_field(int maj,int min,const char * field)474 static int loader_get_linux_pci_field(int maj, int min, const char *field)
475 {
476    char path[PATH_MAX + 1];
477    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device/%s", maj, min, field);
478 
479    char *field_str = os_read_file(path, NULL);
480    if (!field_str) {
481       /* Probably non-PCI device. */
482       return 0;
483    }
484 
485    int value = (int)strtoll(field_str, NULL, 16);
486    free(field_str);
487 
488    return value;
489 }
490 
491 static bool
loader_get_linux_pci_id_for_fd(int fd,int * vendor_id,int * chip_id)492 loader_get_linux_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
493 {
494    struct stat sbuf;
495    if (fstat(fd, &sbuf) != 0) {
496       log_(_LOADER_DEBUG, "MESA-LOADER: failed to fstat fd\n");
497       return false;
498    }
499 
500    int maj = major(sbuf.st_rdev);
501    int min = minor(sbuf.st_rdev);
502 
503    *vendor_id = loader_get_linux_pci_field(maj, min, "vendor");
504    *chip_id = loader_get_linux_pci_field(maj, min, "device");
505 
506    return *vendor_id && *chip_id;
507 }
508 #endif /* __linux__ */
509 
510 bool
loader_get_pci_id_for_fd(int fd,int * vendor_id,int * chip_id)511 loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
512 {
513 #ifdef __linux__
514    /* Implementation without causing full enumeration of DRM devices. */
515    if (loader_get_linux_pci_id_for_fd(fd, vendor_id, chip_id))
516       return true;
517 #endif
518 
519 #if HAVE_LIBDRM
520    return drm_get_pci_id_for_fd(fd, vendor_id, chip_id);
521 #endif
522    return false;
523 }
524 
525 char *
loader_get_device_name_for_fd(int fd)526 loader_get_device_name_for_fd(int fd)
527 {
528    char *result = NULL;
529 
530 #if HAVE_LIBDRM
531    result = drmGetDeviceNameFromFd2(fd);
532 #endif
533 
534    return result;
535 }
536 
537 static char *
loader_get_pci_driver(int fd)538 loader_get_pci_driver(int fd)
539 {
540    int vendor_id, chip_id, i, j;
541    char *driver = NULL;
542 
543    if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id))
544       return NULL;
545 
546    for (i = 0; i < ARRAY_SIZE(driver_map); i++) {
547       if (vendor_id != driver_map[i].vendor_id)
548          continue;
549 
550       if (driver_map[i].predicate && !driver_map[i].predicate(fd))
551          continue;
552 
553       if (driver_map[i].num_chips_ids == -1) {
554          driver = strdup(driver_map[i].driver);
555          goto out;
556       }
557 
558       for (j = 0; j < driver_map[i].num_chips_ids; j++)
559          if (driver_map[i].chip_ids[j] == chip_id) {
560             driver = strdup(driver_map[i].driver);
561             goto out;
562          }
563    }
564 
565 out:
566    log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
567          "pci id for fd %d: %04x:%04x, driver %s\n",
568          fd, vendor_id, chip_id, driver);
569    return driver;
570 }
571 
572 char *
loader_get_driver_for_fd(int fd)573 loader_get_driver_for_fd(int fd)
574 {
575    char *driver;
576 
577    /* Allow an environment variable to force choosing a different driver
578     * binary.  If that driver binary can't survive on this FD, that's the
579     * user's problem, but this allows vc4 simulator to run on an i965 host,
580     * and may be useful for some touch testing of i915 on an i965 host.
581     */
582    if (geteuid() == getuid()) {
583       driver = getenv("MESA_LOADER_DRIVER_OVERRIDE");
584       if (driver)
585          return strdup(driver);
586    }
587 
588 #if defined(HAVE_LIBDRM) && defined(USE_DRICONF)
589    driver = loader_get_dri_config_driver(fd);
590    if (driver)
591       return driver;
592 #endif
593 
594    driver = loader_get_pci_driver(fd);
595    if (!driver)
596       driver = loader_get_kernel_driver_name(fd);
597 
598    return driver;
599 }
600 
601 void
loader_set_logger(loader_logger * logger)602 loader_set_logger(loader_logger *logger)
603 {
604    log_ = logger;
605 }
606 
607 char *
loader_get_extensions_name(const char * driver_name)608 loader_get_extensions_name(const char *driver_name)
609 {
610    char *name = NULL;
611 
612    if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0)
613       return NULL;
614 
615    const size_t len = strlen(name);
616    for (size_t i = 0; i < len; i++) {
617       if (name[i] == '-')
618          name[i] = '_';
619    }
620 
621    return name;
622 }
623 
624 /**
625  * Opens a driver or backend using its name, returning the library handle.
626  *
627  * \param driverName - a name like "i965", "radeon", "nouveau", etc.
628  * \param lib_suffix - a suffix to append to the driver name to generate the
629  * full library name.
630  * \param search_path_vars - NULL-terminated list of env vars that can be used
631  * \param default_search_path - a colon-separted list of directories used if
632  * search_path_vars is NULL or none of the vars are set in the environment.
633  * \param warn_on_fail - Log a warning if the driver is not found.
634  */
635 void *
loader_open_driver_lib(const char * driver_name,const char * lib_suffix,const char ** search_path_vars,const char * default_search_path,bool warn_on_fail)636 loader_open_driver_lib(const char *driver_name,
637                        const char *lib_suffix,
638                        const char **search_path_vars,
639                        const char *default_search_path,
640                        bool warn_on_fail)
641 {
642    char path[PATH_MAX];
643    const char *search_paths, *next, *end;
644 
645    search_paths = NULL;
646    if (geteuid() == getuid() && search_path_vars) {
647       for (int i = 0; search_path_vars[i] != NULL; i++) {
648          search_paths = getenv(search_path_vars[i]);
649          if (search_paths)
650             break;
651       }
652    }
653    if (search_paths == NULL)
654       search_paths = default_search_path;
655 
656    void *driver = NULL;
657    const char *dl_error = NULL;
658    end = search_paths + strlen(search_paths);
659    for (const char *p = search_paths; p < end; p = next + 1) {
660       int len;
661       next = strchr(p, ':');
662       if (next == NULL)
663          next = end;
664 
665       len = next - p;
666       snprintf(path, sizeof(path), "%.*s/tls/%s%s.so", len,
667                p, driver_name, lib_suffix);
668       driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
669       if (driver == NULL) {
670          snprintf(path, sizeof(path), "%.*s/%s%s.so", len,
671                   p, driver_name, lib_suffix);
672          driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
673          if (driver == NULL) {
674             dl_error = dlerror();
675             log_(_LOADER_DEBUG, "MESA-LOADER: failed to open %s: %s\n",
676                  path, dl_error);
677          }
678       }
679       /* not need continue to loop all paths once the driver is found */
680       if (driver != NULL)
681          break;
682    }
683 
684    if (driver == NULL) {
685       if (warn_on_fail) {
686          log_(_LOADER_WARNING,
687               "MESA-LOADER: failed to open %s: %s (search paths %s, suffix %s)\n",
688               driver_name, dl_error, search_paths, lib_suffix);
689       }
690       return NULL;
691    }
692 
693    log_(_LOADER_DEBUG, "MESA-LOADER: dlopen(%s)\n", path);
694 
695    return driver;
696 }
697 
698 /**
699  * Opens a DRI driver using its driver name, returning the __DRIextension
700  * entrypoints.
701  *
702  * \param driverName - a name like "i965", "radeon", "nouveau", etc.
703  * \param out_driver - Address where the dlopen() return value will be stored.
704  * \param search_path_vars - NULL-terminated list of env vars that can be used
705  * to override the DEFAULT_DRIVER_DIR search path.
706  */
707 const struct __DRIextensionRec **
loader_open_driver(const char * driver_name,void ** out_driver_handle,const char ** search_path_vars)708 loader_open_driver(const char *driver_name,
709                    void **out_driver_handle,
710                    const char **search_path_vars)
711 {
712    char *get_extensions_name;
713    const struct __DRIextensionRec **extensions = NULL;
714    const struct __DRIextensionRec **(*get_extensions)(void);
715    void *driver = loader_open_driver_lib(driver_name, "_dri", search_path_vars,
716                                          DEFAULT_DRIVER_DIR, true);
717 
718    if (!driver)
719       goto failed;
720 
721    get_extensions_name = loader_get_extensions_name(driver_name);
722    if (get_extensions_name) {
723       get_extensions = dlsym(driver, get_extensions_name);
724       if (get_extensions) {
725          extensions = get_extensions();
726       } else {
727          log_(_LOADER_DEBUG, "MESA-LOADER: driver does not expose %s(): %s\n",
728               get_extensions_name, dlerror());
729       }
730       free(get_extensions_name);
731    }
732 
733    if (!extensions)
734       extensions = dlsym(driver, __DRI_DRIVER_EXTENSIONS);
735    if (extensions == NULL) {
736       log_(_LOADER_WARNING,
737            "MESA-LOADER: driver exports no extensions (%s)\n", dlerror());
738       dlclose(driver);
739       driver = NULL;
740    }
741 
742 failed:
743    *out_driver_handle = driver;
744    return extensions;
745 }
746