Home | History | Annotate | Line # | Download | only in drm-shim
      1 /*
      2  * Copyright  2018 Broadcom
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21  * DEALINGS IN THE SOFTWARE.
     22  */
     23 
     24 /**
     25  * @file
     26  *
     27  * Implements wrappers of libc functions to fake having a DRM device that
     28  * isn't actually present in the kernel.
     29  */
     30 
     31 /* Prevent glibc from defining open64 when we want to alias it. */
     32 #undef _FILE_OFFSET_BITS
     33 #define _LARGEFILE64_SOURCE
     34 
     35 #include <stdbool.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <unistd.h>
     40 #include <sys/ioctl.h>
     41 #include <sys/mman.h>
     42 #include <sys/stat.h>
     43 #include <sys/sysmacros.h>
     44 #include <stdarg.h>
     45 #include <fcntl.h>
     46 #include <dlfcn.h>
     47 #include <dirent.h>
     48 #include <c11/threads.h>
     49 #include <drm-uapi/drm.h>
     50 
     51 #include "util/set.h"
     52 #include "util/u_debug.h"
     53 #include "drm_shim.h"
     54 
     55 #define REAL_FUNCTION_POINTER(x) typeof(x) *real_##x
     56 
     57 static mtx_t shim_lock = _MTX_INITIALIZER_NP;
     58 struct set *opendir_set;
     59 bool drm_shim_debug;
     60 
     61 /* If /dev/dri doesn't exist, we'll need an arbitrary pointer that wouldn't be
     62  * returned by any other opendir() call so we can return just our fake node.
     63  */
     64 DIR *fake_dev_dri = (void *)&opendir_set;
     65 
     66 /* XXX: implement REAL_FUNCTION_POINTER(close); */
     67 REAL_FUNCTION_POINTER(closedir);
     68 REAL_FUNCTION_POINTER(dup);
     69 REAL_FUNCTION_POINTER(fcntl);
     70 REAL_FUNCTION_POINTER(fopen);
     71 REAL_FUNCTION_POINTER(ioctl);
     72 REAL_FUNCTION_POINTER(mmap);
     73 REAL_FUNCTION_POINTER(open);
     74 REAL_FUNCTION_POINTER(opendir);
     75 REAL_FUNCTION_POINTER(readdir);
     76 REAL_FUNCTION_POINTER(readdir64);
     77 REAL_FUNCTION_POINTER(readlink);
     78 REAL_FUNCTION_POINTER(realpath);
     79 
     80 #define HAS_XSTAT __GLIBC__ == 2 && __GLIBC_MINOR__ < 33
     81 
     82 #if HAS_XSTAT
     83 REAL_FUNCTION_POINTER(__xstat);
     84 REAL_FUNCTION_POINTER(__xstat64);
     85 REAL_FUNCTION_POINTER(__fxstat);
     86 REAL_FUNCTION_POINTER(__fxstat64);
     87 #else
     88 REAL_FUNCTION_POINTER(stat);
     89 REAL_FUNCTION_POINTER(stat64);
     90 REAL_FUNCTION_POINTER(fstat);
     91 REAL_FUNCTION_POINTER(fstat64);
     92 #endif
     93 
     94 /* Full path of /dev/dri/renderD* */
     95 static char *render_node_path;
     96 /* renderD* */
     97 static char *render_node_dirent_name;
     98 /* /sys/dev/char/major:minor/device */
     99 static char *device_path;
    100 /* /sys/dev/char/major:minor/device/subsystem */
    101 static char *subsystem_path;
    102 int render_node_minor = -1;
    103 
    104 struct file_override {
    105    const char *path;
    106    char *contents;
    107 };
    108 static struct file_override file_overrides[10];
    109 static int file_overrides_count;
    110 extern bool drm_shim_driver_prefers_first_render_node;
    111 
    112 #define nfasprintf(...)                         \
    113    {                                            \
    114       UNUSED int __ret = asprintf(__VA_ARGS__); \
    115       assert(__ret >= 0);                       \
    116    }
    117 #define nfvasprintf(...)                         \
    118    {                                             \
    119       UNUSED int __ret = vasprintf(__VA_ARGS__); \
    120       assert(__ret >= 0);                        \
    121    }
    122 
    123 /* Pick the minor and filename for our shimmed render node.  This can be
    124  * either a new one that didn't exist on the system, or if the driver wants,
    125  * it can replace the first render node.
    126  */
    127 static void
    128 get_dri_render_node_minor(void)
    129 {
    130    for (int i = 0; i < 10; i++) {
    131       UNUSED int minor = 128 + i;
    132       nfasprintf(&render_node_dirent_name, "renderD%d", minor);
    133       nfasprintf(&render_node_path, "/dev/dri/%s",
    134                  render_node_dirent_name);
    135       struct stat st;
    136       if (drm_shim_driver_prefers_first_render_node ||
    137           stat(render_node_path, &st) == -1) {
    138 
    139          render_node_minor = minor;
    140          return;
    141       }
    142    }
    143 
    144    fprintf(stderr, "Couldn't find a spare render node slot\n");
    145 }
    146 
    147 static void *get_function_pointer(const char *name)
    148 {
    149    void *func = dlsym(RTLD_NEXT, name);
    150    if (!func) {
    151       fprintf(stderr, "Failed to resolve %s\n", name);
    152       abort();
    153    }
    154    return func;
    155 }
    156 
    157 #define GET_FUNCTION_POINTER(x) real_##x = get_function_pointer(#x)
    158 
    159 void
    160 drm_shim_override_file(const char *contents, const char *path_format, ...)
    161 {
    162    assert(file_overrides_count < ARRAY_SIZE(file_overrides));
    163 
    164    char *path;
    165    va_list ap;
    166    va_start(ap, path_format);
    167    nfvasprintf(&path, path_format, ap);
    168    va_end(ap);
    169 
    170    struct file_override *override = &file_overrides[file_overrides_count++];
    171    override->path = path;
    172    override->contents = strdup(contents);
    173 }
    174 
    175 #if defined(HAVE_NOATEXIT)
    176 static void __attribute__((__destructor__))
    177 #else
    178 static void
    179 #endif
    180 destroy_shim(void)
    181 {
    182    _mesa_set_destroy(opendir_set, NULL);
    183    free(render_node_path);
    184    free(render_node_dirent_name);
    185    free(subsystem_path);
    186 }
    187 
    188 /* Initialization, which will be called from the first general library call
    189  * that might need to be wrapped with the shim.
    190  */
    191 static void
    192 init_shim(void)
    193 {
    194    static bool inited = false;
    195    drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false);
    196 
    197    /* We can't lock this, because we recurse during initialization. */
    198    if (inited)
    199       return;
    200 
    201    /* This comes first (and we're locked), to make sure we don't recurse
    202     * during initialization.
    203     */
    204    inited = true;
    205 
    206    opendir_set = _mesa_set_create(NULL,
    207                                   _mesa_hash_string,
    208                                   _mesa_key_string_equal);
    209 
    210    GET_FUNCTION_POINTER(closedir);
    211    GET_FUNCTION_POINTER(dup);
    212    GET_FUNCTION_POINTER(fcntl);
    213    GET_FUNCTION_POINTER(fopen);
    214    GET_FUNCTION_POINTER(ioctl);
    215    GET_FUNCTION_POINTER(mmap);
    216    GET_FUNCTION_POINTER(open);
    217    GET_FUNCTION_POINTER(opendir);
    218    GET_FUNCTION_POINTER(readdir);
    219    GET_FUNCTION_POINTER(readdir64);
    220    GET_FUNCTION_POINTER(readlink);
    221    GET_FUNCTION_POINTER(realpath);
    222 
    223 #if HAS_XSTAT
    224    GET_FUNCTION_POINTER(__xstat);
    225    GET_FUNCTION_POINTER(__xstat64);
    226    GET_FUNCTION_POINTER(__fxstat);
    227    GET_FUNCTION_POINTER(__fxstat64);
    228 #else
    229    GET_FUNCTION_POINTER(stat);
    230    GET_FUNCTION_POINTER(stat64);
    231    GET_FUNCTION_POINTER(fstat);
    232    GET_FUNCTION_POINTER(fstat64);
    233 #endif
    234 
    235    get_dri_render_node_minor();
    236 
    237    if (drm_shim_debug) {
    238       fprintf(stderr, "Initializing DRM shim on %s\n",
    239               render_node_path);
    240    }
    241 
    242    nfasprintf(&device_path,
    243               "/sys/dev/char/%d:%d/device",
    244               DRM_MAJOR, render_node_minor);
    245 
    246    nfasprintf(&subsystem_path,
    247               "/sys/dev/char/%d:%d/device/subsystem",
    248               DRM_MAJOR, render_node_minor);
    249 
    250    drm_shim_device_init();
    251 
    252 #if !defined(HAVE_NOATEXIT)
    253    atexit(destroy_shim);
    254 #endif
    255 }
    256 
    257 /* Override libdrm's reading of various sysfs files for device enumeration. */
    258 PUBLIC FILE *fopen(const char *path, const char *mode)
    259 {
    260    init_shim();
    261 
    262    for (int i = 0; i < file_overrides_count; i++) {
    263       if (strcmp(file_overrides[i].path, path) == 0) {
    264          int fds[2];
    265          pipe(fds);
    266          write(fds[1], file_overrides[i].contents,
    267                strlen(file_overrides[i].contents));
    268          close(fds[1]);
    269          return fdopen(fds[0], "r");
    270       }
    271    }
    272 
    273    return real_fopen(path, mode);
    274 }
    275 PUBLIC FILE *fopen64(const char *path, const char *mode)
    276    __attribute__((alias("fopen")));
    277 
    278 /* Intercepts open(render_node_path) to redirect it to the simulator. */
    279 PUBLIC int open(const char *path, int flags, ...)
    280 {
    281    init_shim();
    282 
    283    va_list ap;
    284    va_start(ap, flags);
    285    mode_t mode = va_arg(ap, mode_t);
    286    va_end(ap);
    287 
    288    if (strcmp(path, render_node_path) != 0)
    289       return real_open(path, flags, mode);
    290 
    291    int fd = real_open("/dev/null", O_RDWR, 0);
    292 
    293    drm_shim_fd_register(fd, NULL);
    294 
    295    return fd;
    296 }
    297 PUBLIC int open64(const char*, int, ...) __attribute__((alias("open")));
    298 
    299 #if HAS_XSTAT
    300 /* Fakes stat to return character device stuff for our fake render node. */
    301 PUBLIC int __xstat(int ver, const char *path, struct stat *st)
    302 {
    303    init_shim();
    304 
    305    /* Note: call real stat if we're in the process of probing for a free
    306     * render node!
    307     */
    308    if (render_node_minor == -1)
    309       return real___xstat(ver, path, st);
    310 
    311    /* Fool libdrm's probe of whether the /sys dir for this char dev is
    312     * there.
    313     */
    314    char *sys_dev_drm_dir;
    315    nfasprintf(&sys_dev_drm_dir,
    316               "/sys/dev/char/%d:%d/device/drm",
    317               DRM_MAJOR, render_node_minor);
    318    if (strcmp(path, sys_dev_drm_dir) == 0) {
    319       free(sys_dev_drm_dir);
    320       return 0;
    321    }
    322    free(sys_dev_drm_dir);
    323 
    324    if (strcmp(path, render_node_path) != 0)
    325       return real___xstat(ver, path, st);
    326 
    327    memset(st, 0, sizeof(*st));
    328    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
    329    st->st_mode = S_IFCHR;
    330 
    331    return 0;
    332 }
    333 
    334 /* Fakes stat to return character device stuff for our fake render node. */
    335 PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st)
    336 {
    337    init_shim();
    338 
    339    /* Note: call real stat if we're in the process of probing for a free
    340     * render node!
    341     */
    342    if (render_node_minor == -1)
    343       return real___xstat64(ver, path, st);
    344 
    345    /* Fool libdrm's probe of whether the /sys dir for this char dev is
    346     * there.
    347     */
    348    char *sys_dev_drm_dir;
    349    nfasprintf(&sys_dev_drm_dir,
    350               "/sys/dev/char/%d:%d/device/drm",
    351               DRM_MAJOR, render_node_minor);
    352    if (strcmp(path, sys_dev_drm_dir) == 0) {
    353       free(sys_dev_drm_dir);
    354       return 0;
    355    }
    356    free(sys_dev_drm_dir);
    357 
    358    if (strcmp(path, render_node_path) != 0)
    359       return real___xstat64(ver, path, st);
    360 
    361    memset(st, 0, sizeof(*st));
    362    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
    363    st->st_mode = S_IFCHR;
    364 
    365    return 0;
    366 }
    367 
    368 /* Fakes fstat to return character device stuff for our fake render node. */
    369 PUBLIC int __fxstat(int ver, int fd, struct stat *st)
    370 {
    371    init_shim();
    372 
    373    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
    374 
    375    if (!shim_fd)
    376       return real___fxstat(ver, fd, st);
    377 
    378    memset(st, 0, sizeof(*st));
    379    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
    380    st->st_mode = S_IFCHR;
    381 
    382    return 0;
    383 }
    384 
    385 PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st)
    386 {
    387    init_shim();
    388 
    389    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
    390 
    391    if (!shim_fd)
    392       return real___fxstat64(ver, fd, st);
    393 
    394    memset(st, 0, sizeof(*st));
    395    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
    396    st->st_mode = S_IFCHR;
    397 
    398    return 0;
    399 }
    400 
    401 #else
    402 
    403 PUBLIC int stat(const char* path, struct stat* stat_buf)
    404 {
    405    init_shim();
    406 
    407    /* Note: call real stat if we're in the process of probing for a free
    408     * render node!
    409     */
    410    if (render_node_minor == -1)
    411       return real_stat(path, stat_buf);
    412 
    413    /* Fool libdrm's probe of whether the /sys dir for this char dev is
    414     * there.
    415     */
    416    char *sys_dev_drm_dir;
    417    nfasprintf(&sys_dev_drm_dir,
    418               "/sys/dev/char/%d:%d/device/drm",
    419               DRM_MAJOR, render_node_minor);
    420    if (strcmp(path, sys_dev_drm_dir) == 0) {
    421       free(sys_dev_drm_dir);
    422       return 0;
    423    }
    424    free(sys_dev_drm_dir);
    425 
    426    if (strcmp(path, render_node_path) != 0)
    427       return real_stat(path, stat_buf);
    428 
    429    memset(stat_buf, 0, sizeof(*stat_buf));
    430    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
    431    stat_buf->st_mode = S_IFCHR;
    432 
    433    return 0;
    434 }
    435 
    436 PUBLIC int stat64(const char* path, struct stat64* stat_buf)
    437 {
    438    init_shim();
    439 
    440    /* Note: call real stat if we're in the process of probing for a free
    441     * render node!
    442     */
    443    if (render_node_minor == -1)
    444       return real_stat64(path, stat_buf);
    445 
    446    /* Fool libdrm's probe of whether the /sys dir for this char dev is
    447     * there.
    448     */
    449    char *sys_dev_drm_dir;
    450    nfasprintf(&sys_dev_drm_dir,
    451               "/sys/dev/char/%d:%d/device/drm",
    452               DRM_MAJOR, render_node_minor);
    453    if (strcmp(path, sys_dev_drm_dir) == 0) {
    454       free(sys_dev_drm_dir);
    455       return 0;
    456    }
    457    free(sys_dev_drm_dir);
    458 
    459    if (strcmp(path, render_node_path) != 0)
    460       return real_stat64(path, stat_buf);
    461 
    462    memset(stat_buf, 0, sizeof(*stat_buf));
    463    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
    464    stat_buf->st_mode = S_IFCHR;
    465 
    466    return 0;
    467 }
    468 
    469 PUBLIC int fstat(int fd, struct stat* stat_buf)
    470 {
    471    init_shim();
    472 
    473    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
    474 
    475    if (!shim_fd)
    476       return real_fstat(fd, stat_buf);
    477 
    478    memset(stat_buf, 0, sizeof(*stat_buf));
    479    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
    480    stat_buf->st_mode = S_IFCHR;
    481 
    482    return 0;
    483 }
    484 
    485 PUBLIC int fstat64(int fd, struct stat64* stat_buf)
    486 {
    487    init_shim();
    488 
    489    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
    490 
    491    if (!shim_fd)
    492       return real_fstat64(fd, stat_buf);
    493 
    494    memset(stat_buf, 0, sizeof(*stat_buf));
    495    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
    496    stat_buf->st_mode = S_IFCHR;
    497 
    498    return 0;
    499 }
    500 #endif
    501 
    502 /* Tracks if the opendir was on /dev/dri. */
    503 PUBLIC DIR *
    504 opendir(const char *name)
    505 {
    506    init_shim();
    507 
    508    DIR *dir = real_opendir(name);
    509    if (strcmp(name, "/dev/dri") == 0) {
    510       if (!dir) {
    511          /* If /dev/dri didn't exist, we still want to be able to return our
    512           * fake /dev/dri/render* even though we probably can't
    513           * mkdir("/dev/dri").  Return a fake DIR pointer for that.
    514           */
    515          dir = fake_dev_dri;
    516       }
    517 
    518       mtx_lock(&shim_lock);
    519       _mesa_set_add(opendir_set, dir);
    520       mtx_unlock(&shim_lock);
    521    }
    522 
    523    return dir;
    524 }
    525 
    526 /* If we've reached the end of the real directory list and we're
    527  * looking at /dev/dri, add our render node to the list.
    528  */
    529 PUBLIC struct dirent *
    530 readdir(DIR *dir)
    531 {
    532    init_shim();
    533 
    534    struct dirent *ent = NULL;
    535 
    536    if (dir != fake_dev_dri)
    537       ent = real_readdir(dir);
    538    static struct dirent render_node_dirent = { 0 };
    539 
    540    if (!ent) {
    541       mtx_lock(&shim_lock);
    542       if (_mesa_set_search(opendir_set, dir)) {
    543          strcpy(render_node_dirent.d_name,
    544                 render_node_dirent_name);
    545          ent = &render_node_dirent;
    546          _mesa_set_remove_key(opendir_set, dir);
    547       }
    548       mtx_unlock(&shim_lock);
    549    }
    550 
    551    return ent;
    552 }
    553 
    554 /* If we've reached the end of the real directory list and we're
    555  * looking at /dev/dri, add our render node to the list.
    556  */
    557 PUBLIC struct dirent64 *
    558 readdir64(DIR *dir)
    559 {
    560    init_shim();
    561 
    562    struct dirent64 *ent = NULL;
    563    if (dir != fake_dev_dri)
    564       ent = real_readdir64(dir);
    565    static struct dirent64 render_node_dirent = { 0 };
    566 
    567    if (!ent) {
    568       mtx_lock(&shim_lock);
    569       if (_mesa_set_search(opendir_set, dir)) {
    570          strcpy(render_node_dirent.d_name,
    571                 render_node_dirent_name);
    572          ent = &render_node_dirent;
    573          _mesa_set_remove_key(opendir_set, dir);
    574       }
    575       mtx_unlock(&shim_lock);
    576    }
    577 
    578    return ent;
    579 }
    580 
    581 /* Cleans up tracking of opendir("/dev/dri") */
    582 PUBLIC int
    583 closedir(DIR *dir)
    584 {
    585    init_shim();
    586 
    587    mtx_lock(&shim_lock);
    588    _mesa_set_remove_key(opendir_set, dir);
    589    mtx_unlock(&shim_lock);
    590 
    591    if (dir != fake_dev_dri)
    592       return real_closedir(dir);
    593    else
    594       return 0;
    595 }
    596 
    597 /* Handles libdrm's readlink to figure out what kind of device we have. */
    598 PUBLIC ssize_t
    599 readlink(const char *path, char *buf, size_t size)
    600 {
    601    init_shim();
    602 
    603    if (strcmp(path, subsystem_path) != 0)
    604       return real_readlink(path, buf, size);
    605 
    606    static const struct {
    607       const char *name;
    608       int bus_type;
    609    } bus_types[] = {
    610       { "/pci", DRM_BUS_PCI },
    611       { "/usb", DRM_BUS_USB },
    612       { "/platform", DRM_BUS_PLATFORM },
    613       { "/spi", DRM_BUS_PLATFORM },
    614       { "/host1x", DRM_BUS_HOST1X },
    615    };
    616 
    617    for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) {
    618       if (bus_types[i].bus_type != shim_device.bus_type)
    619          continue;
    620 
    621       strncpy(buf, bus_types[i].name, size);
    622       buf[size - 1] = 0;
    623       break;
    624    }
    625 
    626    return strlen(buf) + 1;
    627 }
    628 
    629 /* Handles libdrm's realpath to figure out what kind of device we have. */
    630 PUBLIC char *
    631 realpath(const char *path, char *resolved_path)
    632 {
    633    init_shim();
    634 
    635    if (strcmp(path, device_path) != 0)
    636       return real_realpath(path, resolved_path);
    637 
    638    strcpy(resolved_path, path);
    639 
    640    return resolved_path;
    641 }
    642 
    643 /* Main entrypoint to DRM drivers: the ioctl syscall.  We send all ioctls on
    644  * our DRM fd to drm_shim_ioctl().
    645  */
    646 PUBLIC int
    647 ioctl(int fd, unsigned long request, ...)
    648 {
    649    init_shim();
    650 
    651    va_list ap;
    652    va_start(ap, request);
    653    void *arg = va_arg(ap, void *);
    654    va_end(ap);
    655 
    656    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
    657    if (!shim_fd)
    658       return real_ioctl(fd, request, arg);
    659 
    660    return drm_shim_ioctl(fd, request, arg);
    661 }
    662 
    663 /* Gallium uses this to dup the incoming fd on gbm screen creation */
    664 PUBLIC int
    665 fcntl(int fd, int cmd, ...)
    666 {
    667    init_shim();
    668 
    669    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
    670 
    671    va_list ap;
    672    va_start(ap, cmd);
    673    void *arg = va_arg(ap, void *);
    674    va_end(ap);
    675 
    676    int ret = real_fcntl(fd, cmd, arg);
    677 
    678    if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC))
    679       drm_shim_fd_register(ret, shim_fd);
    680 
    681    return ret;
    682 }
    683 PUBLIC int fcntl64(int, int, ...)
    684    __attribute__((alias("fcntl")));
    685 
    686 /* I wrote this when trying to fix gallium screen creation, leaving it around
    687  * since it's probably good to have.
    688  */
    689 PUBLIC int
    690 dup(int fd)
    691 {
    692    init_shim();
    693 
    694    int ret = real_dup(fd);
    695 
    696    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
    697    if (shim_fd && ret >= 0)
    698       drm_shim_fd_register(ret, shim_fd);
    699 
    700    return ret;
    701 }
    702 
    703 PUBLIC void *
    704 mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
    705 {
    706    init_shim();
    707 
    708    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
    709    if (shim_fd)
    710       return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
    711 
    712    return real_mmap(addr, length, prot, flags, fd, offset);
    713 }
    714 PUBLIC void *mmap64(void*, size_t, int, int, int, off_t)
    715    __attribute__((alias("mmap")));
    716