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
get_dri_render_node_minor(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 
get_function_pointer(const char * name)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
drm_shim_override_file(const char * contents,const char * path_format,...)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 static void
destroy_shim(void)176 destroy_shim(void)
177 {
178    _mesa_set_destroy(opendir_set, NULL);
179    free(render_node_path);
180    free(render_node_dirent_name);
181    free(subsystem_path);
182 }
183 
184 /* Initialization, which will be called from the first general library call
185  * that might need to be wrapped with the shim.
186  */
187 static void
init_shim(void)188 init_shim(void)
189 {
190    static bool inited = false;
191    drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false);
192 
193    /* We can't lock this, because we recurse during initialization. */
194    if (inited)
195       return;
196 
197    /* This comes first (and we're locked), to make sure we don't recurse
198     * during initialization.
199     */
200    inited = true;
201 
202    opendir_set = _mesa_set_create(NULL,
203                                   _mesa_hash_string,
204                                   _mesa_key_string_equal);
205 
206    GET_FUNCTION_POINTER(closedir);
207    GET_FUNCTION_POINTER(dup);
208    GET_FUNCTION_POINTER(fcntl);
209    GET_FUNCTION_POINTER(fopen);
210    GET_FUNCTION_POINTER(ioctl);
211    GET_FUNCTION_POINTER(mmap);
212    GET_FUNCTION_POINTER(open);
213    GET_FUNCTION_POINTER(opendir);
214    GET_FUNCTION_POINTER(readdir);
215    GET_FUNCTION_POINTER(readdir64);
216    GET_FUNCTION_POINTER(readlink);
217    GET_FUNCTION_POINTER(realpath);
218 
219 #if HAS_XSTAT
220    GET_FUNCTION_POINTER(__xstat);
221    GET_FUNCTION_POINTER(__xstat64);
222    GET_FUNCTION_POINTER(__fxstat);
223    GET_FUNCTION_POINTER(__fxstat64);
224 #else
225    GET_FUNCTION_POINTER(stat);
226    GET_FUNCTION_POINTER(stat64);
227    GET_FUNCTION_POINTER(fstat);
228    GET_FUNCTION_POINTER(fstat64);
229 #endif
230 
231    get_dri_render_node_minor();
232 
233    if (drm_shim_debug) {
234       fprintf(stderr, "Initializing DRM shim on %s\n",
235               render_node_path);
236    }
237 
238    nfasprintf(&device_path,
239               "/sys/dev/char/%d:%d/device",
240               DRM_MAJOR, render_node_minor);
241 
242    nfasprintf(&subsystem_path,
243               "/sys/dev/char/%d:%d/device/subsystem",
244               DRM_MAJOR, render_node_minor);
245 
246    drm_shim_device_init();
247 
248    atexit(destroy_shim);
249 }
250 
251 /* Override libdrm's reading of various sysfs files for device enumeration. */
fopen(const char * path,const char * mode)252 PUBLIC FILE *fopen(const char *path, const char *mode)
253 {
254    init_shim();
255 
256    for (int i = 0; i < file_overrides_count; i++) {
257       if (strcmp(file_overrides[i].path, path) == 0) {
258          int fds[2];
259          pipe(fds);
260          write(fds[1], file_overrides[i].contents,
261                strlen(file_overrides[i].contents));
262          close(fds[1]);
263          return fdopen(fds[0], "r");
264       }
265    }
266 
267    return real_fopen(path, mode);
268 }
269 PUBLIC FILE *fopen64(const char *path, const char *mode)
270    __attribute__((alias("fopen")));
271 
272 /* Intercepts open(render_node_path) to redirect it to the simulator. */
open(const char * path,int flags,...)273 PUBLIC int open(const char *path, int flags, ...)
274 {
275    init_shim();
276 
277    va_list ap;
278    va_start(ap, flags);
279    mode_t mode = va_arg(ap, mode_t);
280    va_end(ap);
281 
282    if (strcmp(path, render_node_path) != 0)
283       return real_open(path, flags, mode);
284 
285    int fd = real_open("/dev/null", O_RDWR, 0);
286 
287    drm_shim_fd_register(fd, NULL);
288 
289    return fd;
290 }
291 PUBLIC int open64(const char*, int, ...) __attribute__((alias("open")));
292 
293 #if HAS_XSTAT
294 /* Fakes stat to return character device stuff for our fake render node. */
__xstat(int ver,const char * path,struct stat * st)295 PUBLIC int __xstat(int ver, const char *path, struct stat *st)
296 {
297    init_shim();
298 
299    /* Note: call real stat if we're in the process of probing for a free
300     * render node!
301     */
302    if (render_node_minor == -1)
303       return real___xstat(ver, path, st);
304 
305    /* Fool libdrm's probe of whether the /sys dir for this char dev is
306     * there.
307     */
308    char *sys_dev_drm_dir;
309    nfasprintf(&sys_dev_drm_dir,
310               "/sys/dev/char/%d:%d/device/drm",
311               DRM_MAJOR, render_node_minor);
312    if (strcmp(path, sys_dev_drm_dir) == 0) {
313       free(sys_dev_drm_dir);
314       return 0;
315    }
316    free(sys_dev_drm_dir);
317 
318    if (strcmp(path, render_node_path) != 0)
319       return real___xstat(ver, path, st);
320 
321    memset(st, 0, sizeof(*st));
322    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
323    st->st_mode = S_IFCHR;
324 
325    return 0;
326 }
327 
328 /* Fakes stat to return character device stuff for our fake render node. */
__xstat64(int ver,const char * path,struct stat64 * st)329 PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st)
330 {
331    init_shim();
332 
333    /* Note: call real stat if we're in the process of probing for a free
334     * render node!
335     */
336    if (render_node_minor == -1)
337       return real___xstat64(ver, path, st);
338 
339    /* Fool libdrm's probe of whether the /sys dir for this char dev is
340     * there.
341     */
342    char *sys_dev_drm_dir;
343    nfasprintf(&sys_dev_drm_dir,
344               "/sys/dev/char/%d:%d/device/drm",
345               DRM_MAJOR, render_node_minor);
346    if (strcmp(path, sys_dev_drm_dir) == 0) {
347       free(sys_dev_drm_dir);
348       return 0;
349    }
350    free(sys_dev_drm_dir);
351 
352    if (strcmp(path, render_node_path) != 0)
353       return real___xstat64(ver, path, st);
354 
355    memset(st, 0, sizeof(*st));
356    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
357    st->st_mode = S_IFCHR;
358 
359    return 0;
360 }
361 
362 /* Fakes fstat to return character device stuff for our fake render node. */
__fxstat(int ver,int fd,struct stat * st)363 PUBLIC int __fxstat(int ver, int fd, struct stat *st)
364 {
365    init_shim();
366 
367    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
368 
369    if (!shim_fd)
370       return real___fxstat(ver, fd, st);
371 
372    memset(st, 0, sizeof(*st));
373    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
374    st->st_mode = S_IFCHR;
375 
376    return 0;
377 }
378 
__fxstat64(int ver,int fd,struct stat64 * st)379 PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st)
380 {
381    init_shim();
382 
383    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
384 
385    if (!shim_fd)
386       return real___fxstat64(ver, fd, st);
387 
388    memset(st, 0, sizeof(*st));
389    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
390    st->st_mode = S_IFCHR;
391 
392    return 0;
393 }
394 
395 #else
396 
stat(const char * path,struct stat * stat_buf)397 PUBLIC int stat(const char* path, struct stat* stat_buf)
398 {
399    init_shim();
400 
401    /* Note: call real stat if we're in the process of probing for a free
402     * render node!
403     */
404    if (render_node_minor == -1)
405       return real_stat(path, stat_buf);
406 
407    /* Fool libdrm's probe of whether the /sys dir for this char dev is
408     * there.
409     */
410    char *sys_dev_drm_dir;
411    nfasprintf(&sys_dev_drm_dir,
412               "/sys/dev/char/%d:%d/device/drm",
413               DRM_MAJOR, render_node_minor);
414    if (strcmp(path, sys_dev_drm_dir) == 0) {
415       free(sys_dev_drm_dir);
416       return 0;
417    }
418    free(sys_dev_drm_dir);
419 
420    if (strcmp(path, render_node_path) != 0)
421       return real_stat(path, stat_buf);
422 
423    memset(stat_buf, 0, sizeof(*stat_buf));
424    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
425    stat_buf->st_mode = S_IFCHR;
426 
427    return 0;
428 }
429 
stat64(const char * path,struct stat64 * stat_buf)430 PUBLIC int stat64(const char* path, struct stat64* stat_buf)
431 {
432    init_shim();
433 
434    /* Note: call real stat if we're in the process of probing for a free
435     * render node!
436     */
437    if (render_node_minor == -1)
438       return real_stat64(path, stat_buf);
439 
440    /* Fool libdrm's probe of whether the /sys dir for this char dev is
441     * there.
442     */
443    char *sys_dev_drm_dir;
444    nfasprintf(&sys_dev_drm_dir,
445               "/sys/dev/char/%d:%d/device/drm",
446               DRM_MAJOR, render_node_minor);
447    if (strcmp(path, sys_dev_drm_dir) == 0) {
448       free(sys_dev_drm_dir);
449       return 0;
450    }
451    free(sys_dev_drm_dir);
452 
453    if (strcmp(path, render_node_path) != 0)
454       return real_stat64(path, stat_buf);
455 
456    memset(stat_buf, 0, sizeof(*stat_buf));
457    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
458    stat_buf->st_mode = S_IFCHR;
459 
460    return 0;
461 }
462 
fstat(int fd,struct stat * stat_buf)463 PUBLIC int fstat(int fd, struct stat* stat_buf)
464 {
465    init_shim();
466 
467    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
468 
469    if (!shim_fd)
470       return real_fstat(fd, stat_buf);
471 
472    memset(stat_buf, 0, sizeof(*stat_buf));
473    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
474    stat_buf->st_mode = S_IFCHR;
475 
476    return 0;
477 }
478 
fstat64(int fd,struct stat64 * stat_buf)479 PUBLIC int fstat64(int fd, struct stat64* stat_buf)
480 {
481    init_shim();
482 
483    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
484 
485    if (!shim_fd)
486       return real_fstat64(fd, stat_buf);
487 
488    memset(stat_buf, 0, sizeof(*stat_buf));
489    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
490    stat_buf->st_mode = S_IFCHR;
491 
492    return 0;
493 }
494 #endif
495 
496 /* Tracks if the opendir was on /dev/dri. */
497 PUBLIC DIR *
opendir(const char * name)498 opendir(const char *name)
499 {
500    init_shim();
501 
502    DIR *dir = real_opendir(name);
503    if (strcmp(name, "/dev/dri") == 0) {
504       if (!dir) {
505          /* If /dev/dri didn't exist, we still want to be able to return our
506           * fake /dev/dri/render* even though we probably can't
507           * mkdir("/dev/dri").  Return a fake DIR pointer for that.
508           */
509          dir = fake_dev_dri;
510       }
511 
512       mtx_lock(&shim_lock);
513       _mesa_set_add(opendir_set, dir);
514       mtx_unlock(&shim_lock);
515    }
516 
517    return dir;
518 }
519 
520 /* If we've reached the end of the real directory list and we're
521  * looking at /dev/dri, add our render node to the list.
522  */
523 PUBLIC struct dirent *
readdir(DIR * dir)524 readdir(DIR *dir)
525 {
526    init_shim();
527 
528    struct dirent *ent = NULL;
529 
530    if (dir != fake_dev_dri)
531       ent = real_readdir(dir);
532    static struct dirent render_node_dirent = { 0 };
533 
534    if (!ent) {
535       mtx_lock(&shim_lock);
536       if (_mesa_set_search(opendir_set, dir)) {
537          strcpy(render_node_dirent.d_name,
538                 render_node_dirent_name);
539          ent = &render_node_dirent;
540          _mesa_set_remove_key(opendir_set, dir);
541       }
542       mtx_unlock(&shim_lock);
543    }
544 
545    return ent;
546 }
547 
548 /* If we've reached the end of the real directory list and we're
549  * looking at /dev/dri, add our render node to the list.
550  */
551 PUBLIC struct dirent64 *
readdir64(DIR * dir)552 readdir64(DIR *dir)
553 {
554    init_shim();
555 
556    struct dirent64 *ent = NULL;
557    if (dir != fake_dev_dri)
558       ent = real_readdir64(dir);
559    static struct dirent64 render_node_dirent = { 0 };
560 
561    if (!ent) {
562       mtx_lock(&shim_lock);
563       if (_mesa_set_search(opendir_set, dir)) {
564          strcpy(render_node_dirent.d_name,
565                 render_node_dirent_name);
566          ent = &render_node_dirent;
567          _mesa_set_remove_key(opendir_set, dir);
568       }
569       mtx_unlock(&shim_lock);
570    }
571 
572    return ent;
573 }
574 
575 /* Cleans up tracking of opendir("/dev/dri") */
576 PUBLIC int
closedir(DIR * dir)577 closedir(DIR *dir)
578 {
579    init_shim();
580 
581    mtx_lock(&shim_lock);
582    _mesa_set_remove_key(opendir_set, dir);
583    mtx_unlock(&shim_lock);
584 
585    if (dir != fake_dev_dri)
586       return real_closedir(dir);
587    else
588       return 0;
589 }
590 
591 /* Handles libdrm's readlink to figure out what kind of device we have. */
592 PUBLIC ssize_t
readlink(const char * path,char * buf,size_t size)593 readlink(const char *path, char *buf, size_t size)
594 {
595    init_shim();
596 
597    if (strcmp(path, subsystem_path) != 0)
598       return real_readlink(path, buf, size);
599 
600    static const struct {
601       const char *name;
602       int bus_type;
603    } bus_types[] = {
604       { "/pci", DRM_BUS_PCI },
605       { "/usb", DRM_BUS_USB },
606       { "/platform", DRM_BUS_PLATFORM },
607       { "/spi", DRM_BUS_PLATFORM },
608       { "/host1x", DRM_BUS_HOST1X },
609    };
610 
611    for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) {
612       if (bus_types[i].bus_type != shim_device.bus_type)
613          continue;
614 
615       strncpy(buf, bus_types[i].name, size);
616       buf[size - 1] = 0;
617       break;
618    }
619 
620    return strlen(buf) + 1;
621 }
622 
623 /* Handles libdrm's realpath to figure out what kind of device we have. */
624 PUBLIC char *
realpath(const char * path,char * resolved_path)625 realpath(const char *path, char *resolved_path)
626 {
627    init_shim();
628 
629    if (strcmp(path, device_path) != 0)
630       return real_realpath(path, resolved_path);
631 
632    strcpy(resolved_path, path);
633 
634    return resolved_path;
635 }
636 
637 /* Main entrypoint to DRM drivers: the ioctl syscall.  We send all ioctls on
638  * our DRM fd to drm_shim_ioctl().
639  */
640 PUBLIC int
ioctl(int fd,unsigned long request,...)641 ioctl(int fd, unsigned long request, ...)
642 {
643    init_shim();
644 
645    va_list ap;
646    va_start(ap, request);
647    void *arg = va_arg(ap, void *);
648    va_end(ap);
649 
650    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
651    if (!shim_fd)
652       return real_ioctl(fd, request, arg);
653 
654    return drm_shim_ioctl(fd, request, arg);
655 }
656 
657 /* Gallium uses this to dup the incoming fd on gbm screen creation */
658 PUBLIC int
fcntl(int fd,int cmd,...)659 fcntl(int fd, int cmd, ...)
660 {
661    init_shim();
662 
663    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
664 
665    va_list ap;
666    va_start(ap, cmd);
667    void *arg = va_arg(ap, void *);
668    va_end(ap);
669 
670    int ret = real_fcntl(fd, cmd, arg);
671 
672    if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC))
673       drm_shim_fd_register(ret, shim_fd);
674 
675    return ret;
676 }
677 PUBLIC int fcntl64(int, int, ...)
678    __attribute__((alias("fcntl")));
679 
680 /* I wrote this when trying to fix gallium screen creation, leaving it around
681  * since it's probably good to have.
682  */
683 PUBLIC int
dup(int fd)684 dup(int fd)
685 {
686    init_shim();
687 
688    int ret = real_dup(fd);
689 
690    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
691    if (shim_fd && ret >= 0)
692       drm_shim_fd_register(ret, shim_fd);
693 
694    return ret;
695 }
696 
697 PUBLIC void *
mmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset)698 mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
699 {
700    init_shim();
701 
702    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
703    if (shim_fd)
704       return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
705 
706    return real_mmap(addr, length, prot, flags, fd, offset);
707 }
708 PUBLIC void *mmap64(void*, size_t, int, int, int, off_t)
709    __attribute__((alias("mmap")));
710