dlwrap.c 9.95 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
/* 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 },
70
    { "libOpenGL.so", "GL", NULL},
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
};

/* 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) {
148
            fputs("Error: Failed to find symbol for dlopen.\n", stderr);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
            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;

166 167 168 169 170
    if (asprintf(&wrap_name, "override_%s_%s", prefix, name) < 0) {
        fputs("Error: Failed to allocate memory.\n", stderr);
        abort();
    }

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    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",
245 246
            "GLIBC_2.0",
            "FBSD_1.0"
247 248 249 250 251 252 253 254 255
        };
        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) {
256 257 258 259
            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);
260 261
            for (i = 0; i < num_versions; i++)
                fprintf(stderr, "%s ", version[i]);
262 263 264 265 266 267 268 269 270 271
            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);
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325

            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));
}