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