diff options
Diffstat (limited to 'test/dlwrap.c')
-rw-r--r-- | test/dlwrap.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/test/dlwrap.c b/test/dlwrap.c new file mode 100644 index 0000000..c0c24c2 --- /dev/null +++ b/test/dlwrap.c @@ -0,0 +1,325 @@ +/* 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)); +} |