dlwrap.c revision ca86eba8
1e52adb7bSmrg/* Copyright © 2013, Intel Corporation 2e52adb7bSmrg * 3e52adb7bSmrg * Permission is hereby granted, free of charge, to any person obtaining a copy 4e52adb7bSmrg * of this software and associated documentation files (the "Software"), to deal 5e52adb7bSmrg * in the Software without restriction, including without limitation the rights 6e52adb7bSmrg * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7e52adb7bSmrg * copies of the Software, and to permit persons to whom the Software is 8e52adb7bSmrg * furnished to do so, subject to the following conditions: 9e52adb7bSmrg * 10e52adb7bSmrg * The above copyright notice and this permission notice shall be included in 11e52adb7bSmrg * all copies or substantial portions of the Software. 12e52adb7bSmrg * 13e52adb7bSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14e52adb7bSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15e52adb7bSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16e52adb7bSmrg * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17e52adb7bSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18e52adb7bSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19e52adb7bSmrg * THE SOFTWARE. 20e52adb7bSmrg */ 21e52adb7bSmrg 22e52adb7bSmrg/** @file dlwrap.c 23e52adb7bSmrg * 24e52adb7bSmrg * Implements a wrapper for dlopen() and dlsym() so that epoxy will 25e52adb7bSmrg * end up finding symbols from the testcases named 26e52adb7bSmrg * "override_EGL_eglWhatever()" or "override_GLES2_glWhatever()" or 27e52adb7bSmrg * "override_GL_glWhatever()" when it tries to dlopen() and dlsym() 28e52adb7bSmrg * the real GL or EGL functions in question. 29e52adb7bSmrg * 30e52adb7bSmrg * This lets us simulate some target systems in the test suite, or 31e52adb7bSmrg * just stub out GL functions so we can be sure of what's being 32e52adb7bSmrg * called. 33e52adb7bSmrg */ 34e52adb7bSmrg 35e52adb7bSmrg/* dladdr is a glibc extension */ 36e52adb7bSmrg#define _GNU_SOURCE 37e52adb7bSmrg#include <dlfcn.h> 38e52adb7bSmrg 39e52adb7bSmrg#include <stdbool.h> 40e52adb7bSmrg#include <stdio.h> 41e52adb7bSmrg#include <stdlib.h> 42e52adb7bSmrg#include <string.h> 43e52adb7bSmrg#include <assert.h> 44e52adb7bSmrg 45e52adb7bSmrg#include "dlwrap.h" 46e52adb7bSmrg 47e52adb7bSmrg#define STRNCMP_LITERAL(var, literal) \ 48e52adb7bSmrg strncmp ((var), (literal), sizeof (literal) - 1) 49e52adb7bSmrg 50e52adb7bSmrg#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) 51e52adb7bSmrg 52e52adb7bSmrgvoid *libfips_handle; 53e52adb7bSmrg 54e52adb7bSmrgtypedef void *(*fips_dlopen_t)(const char *filename, int flag); 55e52adb7bSmrgtypedef void *(*fips_dlsym_t)(void *handle, const char *symbol); 56e52adb7bSmrg 57e52adb7bSmrgvoid *override_EGL_eglGetProcAddress(const char *name); 58e52adb7bSmrgvoid *override_GL_glXGetProcAddress(const char *name); 59e52adb7bSmrgvoid *override_GL_glXGetProcAddressARB(const char *name); 60e52adb7bSmrgvoid __dlclose(void *handle); 61e52adb7bSmrg 62e52adb7bSmrgstatic struct libwrap { 63e52adb7bSmrg const char *filename; 64e52adb7bSmrg const char *symbol_prefix; 65e52adb7bSmrg void *handle; 66e52adb7bSmrg} wrapped_libs[] = { 67e52adb7bSmrg { "libGL.so", "GL", NULL }, 68e52adb7bSmrg { "libEGL.so", "EGL", NULL }, 69e52adb7bSmrg { "libGLESv2.so", "GLES2", NULL }, 70ca86eba8Smrg { "libOpenGL.so", "GL", NULL}, 71e52adb7bSmrg}; 72e52adb7bSmrg 73e52adb7bSmrg/* Match 'filename' against an internal list of libraries for which 74e52adb7bSmrg * libfips has wrappers. 75e52adb7bSmrg * 76e52adb7bSmrg * Returns true and sets *index_ret if a match is found. 77e52adb7bSmrg * Returns false if no match is found. */ 78e52adb7bSmrgstatic struct libwrap * 79e52adb7bSmrgfind_wrapped_library(const char *filename) 80e52adb7bSmrg{ 81e52adb7bSmrg unsigned i; 82e52adb7bSmrg 83e52adb7bSmrg if (!filename) 84e52adb7bSmrg return NULL; 85e52adb7bSmrg 86e52adb7bSmrg for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) { 87e52adb7bSmrg if (strncmp(wrapped_libs[i].filename, filename, 88e52adb7bSmrg strlen(wrapped_libs[i].filename)) == 0) { 89e52adb7bSmrg return &wrapped_libs[i]; 90e52adb7bSmrg } 91e52adb7bSmrg } 92e52adb7bSmrg 93e52adb7bSmrg return NULL; 94e52adb7bSmrg} 95e52adb7bSmrg 96e52adb7bSmrg/* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking 97e52adb7bSmrg * against it directly, which means they would not be seeing our 98e52adb7bSmrg * wrapped GL symbols via LD_PRELOAD. So we catch the dlopen in a 99e52adb7bSmrg * wrapper here and redirect it to our library. 100e52adb7bSmrg */ 101e52adb7bSmrgvoid * 102e52adb7bSmrgdlopen(const char *filename, int flag) 103e52adb7bSmrg{ 104e52adb7bSmrg void *ret; 105e52adb7bSmrg struct libwrap *wrap; 106e52adb7bSmrg 107e52adb7bSmrg /* Before deciding whether to redirect this dlopen to our own 108e52adb7bSmrg * library, we call the real dlopen. This assures that any 109e52adb7bSmrg * expected side-effects from loading the intended library are 110e52adb7bSmrg * resolved. Below, we may still return a handle pointing to 111e52adb7bSmrg * our own library, and not what is opened here. */ 112e52adb7bSmrg ret = dlwrap_real_dlopen(filename, flag); 113e52adb7bSmrg 114e52adb7bSmrg /* If filename is not a wrapped library, just return real dlopen */ 115e52adb7bSmrg wrap = find_wrapped_library(filename); 116e52adb7bSmrg if (!wrap) 117e52adb7bSmrg return ret; 118e52adb7bSmrg 119e52adb7bSmrg wrap->handle = ret; 120e52adb7bSmrg 121e52adb7bSmrg /* We use wrapped_libs as our handles to libraries. */ 122e52adb7bSmrg return wrap; 123e52adb7bSmrg} 124e52adb7bSmrg 125e52adb7bSmrg/** 126e52adb7bSmrg * Wraps dlclose to hide our faked handles from it. 127e52adb7bSmrg */ 128e52adb7bSmrgvoid 129e52adb7bSmrg__dlclose(void *handle) 130e52adb7bSmrg{ 131e52adb7bSmrg struct libwrap *wrap = handle; 132e52adb7bSmrg 133e52adb7bSmrg if (wrap < wrapped_libs || 134e52adb7bSmrg wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) { 135e52adb7bSmrg void (*real_dlclose)(void *handle) = dlwrap_real_dlsym(RTLD_NEXT, "__dlclose"); 136e52adb7bSmrg real_dlclose(handle); 137e52adb7bSmrg } 138e52adb7bSmrg} 139e52adb7bSmrg 140e52adb7bSmrgvoid * 141e52adb7bSmrgdlwrap_real_dlopen(const char *filename, int flag) 142e52adb7bSmrg{ 143e52adb7bSmrg static fips_dlopen_t real_dlopen = NULL; 144e52adb7bSmrg 145e52adb7bSmrg if (!real_dlopen) { 146e52adb7bSmrg real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym(RTLD_NEXT, "dlopen"); 147e52adb7bSmrg if (!real_dlopen) { 148ca86eba8Smrg fputs("Error: Failed to find symbol for dlopen.\n", stderr); 149e52adb7bSmrg exit(1); 150e52adb7bSmrg } 151e52adb7bSmrg } 152e52adb7bSmrg 153e52adb7bSmrg return real_dlopen(filename, flag); 154e52adb7bSmrg} 155e52adb7bSmrg 156e52adb7bSmrg/** 157e52adb7bSmrg * Return the dlsym() on the application's namespace for 158e52adb7bSmrg * "override_<prefix>_<name>" 159e52adb7bSmrg */ 160e52adb7bSmrgstatic void * 161e52adb7bSmrgwrapped_dlsym(const char *prefix, const char *name) 162e52adb7bSmrg{ 163e52adb7bSmrg char *wrap_name; 164e52adb7bSmrg void *symbol; 165e52adb7bSmrg 166ca86eba8Smrg if (asprintf(&wrap_name, "override_%s_%s", prefix, name) < 0) { 167ca86eba8Smrg fputs("Error: Failed to allocate memory.\n", stderr); 168ca86eba8Smrg abort(); 169ca86eba8Smrg } 170ca86eba8Smrg 171e52adb7bSmrg symbol = dlwrap_real_dlsym(RTLD_DEFAULT, wrap_name); 172e52adb7bSmrg free(wrap_name); 173e52adb7bSmrg return symbol; 174e52adb7bSmrg} 175e52adb7bSmrg 176e52adb7bSmrg/* Since we redirect dlopens of libGL.so and libEGL.so to libfips we 177e52adb7bSmrg * need to ensure that dlysm succeeds for all functions that might be 178e52adb7bSmrg * defined in the real, underlying libGL library. But we're far too 179e52adb7bSmrg * lazy to implement wrappers for function that would simply 180e52adb7bSmrg * pass-through, so instead we also wrap dlysm and arrange for it to 181e52adb7bSmrg * pass things through with RTLD_next if libfips does not have the 182e52adb7bSmrg * function desired. */ 183e52adb7bSmrgvoid * 184e52adb7bSmrgdlsym(void *handle, const char *name) 185e52adb7bSmrg{ 186e52adb7bSmrg struct libwrap *wrap = handle; 187e52adb7bSmrg 188e52adb7bSmrg /* Make sure that handle is actually one of our wrapped libs. */ 189e52adb7bSmrg if (wrap < wrapped_libs || 190e52adb7bSmrg wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) { 191e52adb7bSmrg wrap = NULL; 192e52adb7bSmrg } 193e52adb7bSmrg 194e52adb7bSmrg /* Failing that, anything specifically requested from the 195e52adb7bSmrg * libfips library should be redirected to a real GL 196e52adb7bSmrg * library. */ 197e52adb7bSmrg 198e52adb7bSmrg if (wrap) { 199e52adb7bSmrg void *symbol = wrapped_dlsym(wrap->symbol_prefix, name); 200e52adb7bSmrg if (symbol) 201e52adb7bSmrg return symbol; 202e52adb7bSmrg else 203e52adb7bSmrg return dlwrap_real_dlsym(wrap->handle, name); 204e52adb7bSmrg } 205e52adb7bSmrg 206e52adb7bSmrg /* And anything else is some unrelated dlsym. Just pass it 207e52adb7bSmrg * through. (This also covers the cases of lookups with 208e52adb7bSmrg * special handles such as RTLD_DEFAULT or RTLD_NEXT.) 209e52adb7bSmrg */ 210e52adb7bSmrg return dlwrap_real_dlsym(handle, name); 211e52adb7bSmrg} 212e52adb7bSmrg 213e52adb7bSmrgvoid * 214e52adb7bSmrgdlwrap_real_dlsym(void *handle, const char *name) 215e52adb7bSmrg{ 216e52adb7bSmrg static fips_dlsym_t real_dlsym = NULL; 217e52adb7bSmrg 218e52adb7bSmrg if (!real_dlsym) { 219e52adb7bSmrg /* FIXME: This brute-force, hard-coded searching for a versioned 220e52adb7bSmrg * symbol is really ugly. The only reason I'm doing this is because 221e52adb7bSmrg * I need some way to lookup the "dlsym" function in libdl, but 222e52adb7bSmrg * I can't use 'dlsym' to do it. So dlvsym works, but forces me 223e52adb7bSmrg * to guess what the right version is. 224e52adb7bSmrg * 225e52adb7bSmrg * Potential fixes here: 226e52adb7bSmrg * 227e52adb7bSmrg * 1. Use libelf to actually inspect libdl.so and 228e52adb7bSmrg * find the right version, (finding the right 229e52adb7bSmrg * libdl.so can be made easier with 230e52adb7bSmrg * dl_iterate_phdr). 231e52adb7bSmrg * 232e52adb7bSmrg * 2. Use libelf to find the offset of the 'dlsym' 233e52adb7bSmrg * symbol within libdl.so, (and then add this to 234e52adb7bSmrg * the base address at which libdl.so is loaded 235e52adb7bSmrg * as reported by dl_iterate_phdr). 236e52adb7bSmrg * 237e52adb7bSmrg * In the meantime, I'll just keep augmenting this 238e52adb7bSmrg * hard-coded version list as people report bugs. */ 239e52adb7bSmrg const char *version[] = { 240f71742dfSmrg "GLIBC_2.17", 241f71742dfSmrg "GLIBC_2.4", 242f71742dfSmrg "GLIBC_2.3", 243e52adb7bSmrg "GLIBC_2.2.5", 244f71742dfSmrg "GLIBC_2.2", 245ca86eba8Smrg "GLIBC_2.0", 246ca86eba8Smrg "FBSD_1.0" 247e52adb7bSmrg }; 248e52adb7bSmrg int num_versions = sizeof(version) / sizeof(version[0]); 249e52adb7bSmrg int i; 250e52adb7bSmrg for (i = 0; i < num_versions; i++) { 251e52adb7bSmrg real_dlsym = (fips_dlsym_t) dlvsym(RTLD_NEXT, "dlsym", version[i]); 252e52adb7bSmrg if (real_dlsym) 253e52adb7bSmrg break; 254e52adb7bSmrg } 255e52adb7bSmrg if (i == num_versions) { 256ca86eba8Smrg fputs("Internal error: Failed to find real dlsym\n", stderr); 257ca86eba8Smrg fputs("This may be a simple matter of fips not knowing about the version of GLIBC that\n" 258ca86eba8Smrg "your program is using. Current known versions are:\n\n\t", 259ca86eba8Smrg stderr); 260e52adb7bSmrg for (i = 0; i < num_versions; i++) 261e52adb7bSmrg fprintf(stderr, "%s ", version[i]); 262ca86eba8Smrg fputs("\n\nYou can inspect your version by first finding libdl.so.2:\n" 263ca86eba8Smrg "\n" 264ca86eba8Smrg "\tldd <your-program> | grep libdl.so\n" 265ca86eba8Smrg "\n" 266ca86eba8Smrg "And then inspecting the version attached to the dlsym symbol:\n" 267ca86eba8Smrg "\n" 268ca86eba8Smrg "\treadelf -s /path/to/libdl.so.2 | grep dlsym\n" 269ca86eba8Smrg "\n" 270ca86eba8Smrg "And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n", 271ca86eba8Smrg stderr); 272e52adb7bSmrg 273e52adb7bSmrg exit(1); 274e52adb7bSmrg } 275e52adb7bSmrg } 276e52adb7bSmrg 277e52adb7bSmrg return real_dlsym(handle, name); 278e52adb7bSmrg} 279e52adb7bSmrg 280e52adb7bSmrgvoid * 281e52adb7bSmrgoverride_GL_glXGetProcAddress(const char *name) 282e52adb7bSmrg{ 283e52adb7bSmrg void *symbol; 284e52adb7bSmrg 285e52adb7bSmrg symbol = wrapped_dlsym("GL", name); 286e52adb7bSmrg if (symbol) 287e52adb7bSmrg return symbol; 288e52adb7bSmrg 289e52adb7bSmrg return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddress, 290e52adb7bSmrg "glXGetProcAddress", (name)); 291e52adb7bSmrg} 292e52adb7bSmrg 293e52adb7bSmrgvoid * 294e52adb7bSmrgoverride_GL_glXGetProcAddressARB(const char *name) 295e52adb7bSmrg{ 296e52adb7bSmrg void *symbol; 297e52adb7bSmrg 298e52adb7bSmrg symbol = wrapped_dlsym("GL", name); 299e52adb7bSmrg if (symbol) 300e52adb7bSmrg return symbol; 301e52adb7bSmrg 302e52adb7bSmrg return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddressARB, 303e52adb7bSmrg "glXGetProcAddressARB", (name)); 304e52adb7bSmrg} 305e52adb7bSmrg 306e52adb7bSmrgvoid * 307e52adb7bSmrgoverride_EGL_eglGetProcAddress(const char *name) 308e52adb7bSmrg{ 309e52adb7bSmrg void *symbol; 310e52adb7bSmrg 311e52adb7bSmrg if (!STRNCMP_LITERAL(name, "gl")) { 312e52adb7bSmrg symbol = wrapped_dlsym("GLES2", name); 313e52adb7bSmrg if (symbol) 314e52adb7bSmrg return symbol; 315e52adb7bSmrg } 316e52adb7bSmrg 317e52adb7bSmrg if (!STRNCMP_LITERAL(name, "egl")) { 318e52adb7bSmrg symbol = wrapped_dlsym("EGL", name); 319e52adb7bSmrg if (symbol) 320e52adb7bSmrg return symbol; 321e52adb7bSmrg } 322e52adb7bSmrg 323e52adb7bSmrg return DEFER_TO_GL("libEGL.so.1", override_EGL_eglGetProcAddress, 324e52adb7bSmrg "eglGetProcAddress", (name)); 325e52adb7bSmrg} 326