Edit

IABSD.fr/xenocara/dist/libepoxy/test/dlwrap.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2018-05-25 06:24:59
    Hash : 9d6c7364
    Message : Update to libepoxy 1.5.2. ok aja@

  • dist/libepoxy/test/dlwrap.c
  • /* Copyright © 2013, Intel Corporation
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    /** @file dlwrap.c
     *
     * Implements a wrapper for dlopen() and dlsym() so that epoxy will
     * end up finding symbols from the testcases named
     * "override_EGL_eglWhatever()" or "override_GLES2_glWhatever()" or
     * "override_GL_glWhatever()" when it tries to dlopen() and dlsym()
     * the real GL or EGL functions in question.
     *
     * This lets us simulate some target systems in the test suite, or
     * just stub out GL functions so we can be sure of what's being
     * called.
     */
    
    /* dladdr is a glibc extension */
    #define _GNU_SOURCE
    #include <dlfcn.h>
    
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    
    #include "dlwrap.h"
    
    #define STRNCMP_LITERAL(var, literal) \
        strncmp ((var), (literal), sizeof (literal) - 1)
    
    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
    
    void *libfips_handle;
    
    typedef void *(*fips_dlopen_t)(const char *filename, int flag);
    typedef void *(*fips_dlsym_t)(void *handle, const char *symbol);
    
    void *override_EGL_eglGetProcAddress(const char *name);
    void *override_GL_glXGetProcAddress(const char *name);
    void *override_GL_glXGetProcAddressARB(const char *name);
    void __dlclose(void *handle);
    
    static struct libwrap {
        const char *filename;
        const char *symbol_prefix;
        void *handle;
    } wrapped_libs[] = {
        { "libGL.so", "GL", NULL },
        { "libEGL.so", "EGL", NULL },
        { "libGLESv2.so", "GLES2", NULL },
        { "libOpenGL.so", "GL", NULL},
    };
    
    /* Match 'filename' against an internal list of libraries for which
     * libfips has wrappers.
     *
     * Returns true and sets *index_ret if a match is found.
     * Returns false if no match is found. */
    static struct libwrap *
    find_wrapped_library(const char *filename)
    {
        unsigned i;
    
        if (!filename)
            return NULL;
    
        for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) {
            if (strncmp(wrapped_libs[i].filename, filename,
                        strlen(wrapped_libs[i].filename)) == 0) {
                return &wrapped_libs[i];
            }
        }
    
        return NULL;
    }
    
    /* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking
     * against it directly, which means they would not be seeing our
     * wrapped GL symbols via LD_PRELOAD. So we catch the dlopen in a
     * wrapper here and redirect it to our library.
     */
    void *
    dlopen(const char *filename, int flag)
    {
        void *ret;
        struct libwrap *wrap;
    
        /* Before deciding whether to redirect this dlopen to our own
         * library, we call the real dlopen. This assures that any
         * expected side-effects from loading the intended library are
         * resolved. Below, we may still return a handle pointing to
         * our own library, and not what is opened here. */
        ret = dlwrap_real_dlopen(filename, flag);
    
        /* If filename is not a wrapped library, just return real dlopen */
        wrap = find_wrapped_library(filename);
        if (!wrap)
            return ret;
    
        wrap->handle = ret;
    
        /* We use wrapped_libs as our handles to libraries. */
        return wrap;
    }
    
    /**
     * Wraps dlclose to hide our faked handles from it.
     */
    void
    __dlclose(void *handle)
    {
        struct libwrap *wrap = handle;
    
        if (wrap < wrapped_libs ||
            wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) {
            void (*real_dlclose)(void *handle) = dlwrap_real_dlsym(RTLD_NEXT, "__dlclose");
            real_dlclose(handle);
        }
    }
    
    void *
    dlwrap_real_dlopen(const char *filename, int flag)
    {
        static fips_dlopen_t real_dlopen = NULL;
    
        if (!real_dlopen) {
            real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym(RTLD_NEXT, "dlopen");
            if (!real_dlopen) {
                fputs("Error: Failed to find symbol for dlopen.\n", stderr);
                exit(1);
            }
        }
    
        return real_dlopen(filename, flag);
    }
    
    /**
     * Return the dlsym() on the application's namespace for
     * "override_<prefix>_<name>"
     */
    static void *
    wrapped_dlsym(const char *prefix, const char *name)
    {
        char *wrap_name;
        void *symbol;
    
        if (asprintf(&wrap_name, "override_%s_%s", prefix, name) < 0) {
            fputs("Error: Failed to allocate memory.\n", stderr);
            abort();
        }
    
        symbol = dlwrap_real_dlsym(RTLD_DEFAULT, wrap_name);
        free(wrap_name);
        return symbol;
    }
    
    /* Since we redirect dlopens of libGL.so and libEGL.so to libfips we
     * need to ensure that dlysm succeeds for all functions that might be
     * defined in the real, underlying libGL library. But we're far too
     * lazy to implement wrappers for function that would simply
     * pass-through, so instead we also wrap dlysm and arrange for it to
     * pass things through with RTLD_next if libfips does not have the
     * function desired.  */
    void *
    dlsym(void *handle, const char *name)
    {
        struct libwrap *wrap = handle;
    
        /* Make sure that handle is actually one of our wrapped libs. */
        if (wrap < wrapped_libs ||
            wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) {
            wrap = NULL;
        }
    
        /* Failing that, anything specifically requested from the
         * libfips library should be redirected to a real GL
         * library. */
    
        if (wrap) {
            void *symbol = wrapped_dlsym(wrap->symbol_prefix, name);
            if (symbol)
                return symbol;
            else
                return dlwrap_real_dlsym(wrap->handle, name);
        }
    
        /* And anything else is some unrelated dlsym. Just pass it
         * through.  (This also covers the cases of lookups with
         * special handles such as RTLD_DEFAULT or RTLD_NEXT.)
         */
        return dlwrap_real_dlsym(handle, name);
    }
    
    void *
    dlwrap_real_dlsym(void *handle, const char *name)
    {
        static fips_dlsym_t real_dlsym = NULL;
    
        if (!real_dlsym) {
            /* FIXME: This brute-force, hard-coded searching for a versioned
             * symbol is really ugly. The only reason I'm doing this is because
             * I need some way to lookup the "dlsym" function in libdl, but
             * I can't use 'dlsym' to do it. So dlvsym works, but forces me
             * to guess what the right version is.
             *
             * Potential fixes here:
             *
             *   1. Use libelf to actually inspect libdl.so and
             *      find the right version, (finding the right
             *      libdl.so can be made easier with
             *      dl_iterate_phdr).
             *
             *   2. Use libelf to find the offset of the 'dlsym'
             *      symbol within libdl.so, (and then add this to
             *      the base address at which libdl.so is loaded
             *      as reported by dl_iterate_phdr).
             *
             * In the meantime, I'll just keep augmenting this
             * hard-coded version list as people report bugs. */
            const char *version[] = {
                "GLIBC_2.17",
                "GLIBC_2.4",
                "GLIBC_2.3",
                "GLIBC_2.2.5",
                "GLIBC_2.2",
                "GLIBC_2.0",
                "FBSD_1.0"
            };
            int num_versions = sizeof(version) / sizeof(version[0]);
            int i;
            for (i = 0; i < num_versions; i++) {
                real_dlsym = (fips_dlsym_t) dlvsym(RTLD_NEXT, "dlsym", version[i]);
                if (real_dlsym)
                    break;
            }
            if (i == num_versions) {
                fputs("Internal error: Failed to find real dlsym\n", stderr);
                fputs("This may be a simple matter of fips not knowing about the version of GLIBC that\n"
                      "your program is using. Current known versions are:\n\n\t",
                      stderr);
                for (i = 0; i < num_versions; i++)
                    fprintf(stderr, "%s ", version[i]);
                fputs("\n\nYou can inspect your version by first finding libdl.so.2:\n"
                      "\n"
                      "\tldd <your-program> | grep libdl.so\n"
                      "\n"
                      "And then inspecting the version attached to the dlsym symbol:\n"
                      "\n"
                      "\treadelf -s /path/to/libdl.so.2 | grep dlsym\n"
                      "\n"
                      "And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n",
                      stderr);
    
                exit(1);
            }
        }
    
        return real_dlsym(handle, name);
    }
    
    void *
    override_GL_glXGetProcAddress(const char *name)
    {
        void *symbol;
    
        symbol = wrapped_dlsym("GL", name);
        if (symbol)
            return symbol;
    
        return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddress,
                           "glXGetProcAddress", (name));
    }
    
    void *
    override_GL_glXGetProcAddressARB(const char *name)
    {
        void *symbol;
    
        symbol = wrapped_dlsym("GL", name);
        if (symbol)
            return symbol;
    
        return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddressARB,
                           "glXGetProcAddressARB", (name));
    }
    
    void *
    override_EGL_eglGetProcAddress(const char *name)
    {
        void *symbol;
    
        if (!STRNCMP_LITERAL(name, "gl")) {
            symbol = wrapped_dlsym("GLES2", name);
            if (symbol)
                return symbol;
        }
    
        if (!STRNCMP_LITERAL(name, "egl")) {
            symbol = wrapped_dlsym("EGL", name);
            if (symbol)
                return symbol;
        }
    
        return DEFER_TO_GL("libEGL.so.1", override_EGL_eglGetProcAddress,
                           "eglGetProcAddress", (name));
    }