dlwrap.c revision e52adb7b
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 }, 70e52adb7bSmrg}; 71e52adb7bSmrg 72e52adb7bSmrg/* Match 'filename' against an internal list of libraries for which 73e52adb7bSmrg * libfips has wrappers. 74e52adb7bSmrg * 75e52adb7bSmrg * Returns true and sets *index_ret if a match is found. 76e52adb7bSmrg * Returns false if no match is found. */ 77e52adb7bSmrgstatic struct libwrap * 78e52adb7bSmrgfind_wrapped_library(const char *filename) 79e52adb7bSmrg{ 80e52adb7bSmrg unsigned i; 81e52adb7bSmrg 82e52adb7bSmrg if (!filename) 83e52adb7bSmrg return NULL; 84e52adb7bSmrg 85e52adb7bSmrg for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) { 86e52adb7bSmrg if (strncmp(wrapped_libs[i].filename, filename, 87e52adb7bSmrg strlen(wrapped_libs[i].filename)) == 0) { 88e52adb7bSmrg return &wrapped_libs[i]; 89e52adb7bSmrg } 90e52adb7bSmrg } 91e52adb7bSmrg 92e52adb7bSmrg return NULL; 93e52adb7bSmrg} 94e52adb7bSmrg 95e52adb7bSmrg/* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking 96e52adb7bSmrg * against it directly, which means they would not be seeing our 97e52adb7bSmrg * wrapped GL symbols via LD_PRELOAD. So we catch the dlopen in a 98e52adb7bSmrg * wrapper here and redirect it to our library. 99e52adb7bSmrg */ 100e52adb7bSmrgvoid * 101e52adb7bSmrgdlopen(const char *filename, int flag) 102e52adb7bSmrg{ 103e52adb7bSmrg void *ret; 104e52adb7bSmrg struct libwrap *wrap; 105e52adb7bSmrg 106e52adb7bSmrg /* Before deciding whether to redirect this dlopen to our own 107e52adb7bSmrg * library, we call the real dlopen. This assures that any 108e52adb7bSmrg * expected side-effects from loading the intended library are 109e52adb7bSmrg * resolved. Below, we may still return a handle pointing to 110e52adb7bSmrg * our own library, and not what is opened here. */ 111e52adb7bSmrg ret = dlwrap_real_dlopen(filename, flag); 112e52adb7bSmrg 113e52adb7bSmrg /* If filename is not a wrapped library, just return real dlopen */ 114e52adb7bSmrg wrap = find_wrapped_library(filename); 115e52adb7bSmrg if (!wrap) 116e52adb7bSmrg return ret; 117e52adb7bSmrg 118e52adb7bSmrg wrap->handle = ret; 119e52adb7bSmrg 120e52adb7bSmrg /* We use wrapped_libs as our handles to libraries. */ 121e52adb7bSmrg return wrap; 122e52adb7bSmrg} 123e52adb7bSmrg 124e52adb7bSmrg/** 125e52adb7bSmrg * Wraps dlclose to hide our faked handles from it. 126e52adb7bSmrg */ 127e52adb7bSmrgvoid 128e52adb7bSmrg__dlclose(void *handle) 129e52adb7bSmrg{ 130e52adb7bSmrg struct libwrap *wrap = handle; 131e52adb7bSmrg 132e52adb7bSmrg if (wrap < wrapped_libs || 133e52adb7bSmrg wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) { 134e52adb7bSmrg void (*real_dlclose)(void *handle) = dlwrap_real_dlsym(RTLD_NEXT, "__dlclose"); 135e52adb7bSmrg real_dlclose(handle); 136e52adb7bSmrg } 137e52adb7bSmrg} 138e52adb7bSmrg 139e52adb7bSmrgvoid * 140e52adb7bSmrgdlwrap_real_dlopen(const char *filename, int flag) 141e52adb7bSmrg{ 142e52adb7bSmrg static fips_dlopen_t real_dlopen = NULL; 143e52adb7bSmrg 144e52adb7bSmrg if (!real_dlopen) { 145e52adb7bSmrg real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym(RTLD_NEXT, "dlopen"); 146e52adb7bSmrg if (!real_dlopen) { 147e52adb7bSmrg fprintf(stderr, "Error: Failed to find symbol for dlopen.\n"); 148e52adb7bSmrg exit(1); 149e52adb7bSmrg } 150e52adb7bSmrg } 151e52adb7bSmrg 152e52adb7bSmrg return real_dlopen(filename, flag); 153e52adb7bSmrg} 154e52adb7bSmrg 155e52adb7bSmrg/** 156e52adb7bSmrg * Return the dlsym() on the application's namespace for 157e52adb7bSmrg * "override_<prefix>_<name>" 158e52adb7bSmrg */ 159e52adb7bSmrgstatic void * 160e52adb7bSmrgwrapped_dlsym(const char *prefix, const char *name) 161e52adb7bSmrg{ 162e52adb7bSmrg char *wrap_name; 163e52adb7bSmrg void *symbol; 164e52adb7bSmrg 165e52adb7bSmrg asprintf(&wrap_name, "override_%s_%s", prefix, name); 166e52adb7bSmrg symbol = dlwrap_real_dlsym(RTLD_DEFAULT, wrap_name); 167e52adb7bSmrg free(wrap_name); 168e52adb7bSmrg return symbol; 169e52adb7bSmrg} 170e52adb7bSmrg 171e52adb7bSmrg/* Since we redirect dlopens of libGL.so and libEGL.so to libfips we 172e52adb7bSmrg * need to ensure that dlysm succeeds for all functions that might be 173e52adb7bSmrg * defined in the real, underlying libGL library. But we're far too 174e52adb7bSmrg * lazy to implement wrappers for function that would simply 175e52adb7bSmrg * pass-through, so instead we also wrap dlysm and arrange for it to 176e52adb7bSmrg * pass things through with RTLD_next if libfips does not have the 177e52adb7bSmrg * function desired. */ 178e52adb7bSmrgvoid * 179e52adb7bSmrgdlsym(void *handle, const char *name) 180e52adb7bSmrg{ 181e52adb7bSmrg struct libwrap *wrap = handle; 182e52adb7bSmrg 183e52adb7bSmrg /* Make sure that handle is actually one of our wrapped libs. */ 184e52adb7bSmrg if (wrap < wrapped_libs || 185e52adb7bSmrg wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) { 186e52adb7bSmrg wrap = NULL; 187e52adb7bSmrg } 188e52adb7bSmrg 189e52adb7bSmrg /* Failing that, anything specifically requested from the 190e52adb7bSmrg * libfips library should be redirected to a real GL 191e52adb7bSmrg * library. */ 192e52adb7bSmrg 193e52adb7bSmrg if (wrap) { 194e52adb7bSmrg void *symbol = wrapped_dlsym(wrap->symbol_prefix, name); 195e52adb7bSmrg if (symbol) 196e52adb7bSmrg return symbol; 197e52adb7bSmrg else 198e52adb7bSmrg return dlwrap_real_dlsym(wrap->handle, name); 199e52adb7bSmrg } 200e52adb7bSmrg 201e52adb7bSmrg /* And anything else is some unrelated dlsym. Just pass it 202e52adb7bSmrg * through. (This also covers the cases of lookups with 203e52adb7bSmrg * special handles such as RTLD_DEFAULT or RTLD_NEXT.) 204e52adb7bSmrg */ 205e52adb7bSmrg return dlwrap_real_dlsym(handle, name); 206e52adb7bSmrg} 207e52adb7bSmrg 208e52adb7bSmrgvoid * 209e52adb7bSmrgdlwrap_real_dlsym(void *handle, const char *name) 210e52adb7bSmrg{ 211e52adb7bSmrg static fips_dlsym_t real_dlsym = NULL; 212e52adb7bSmrg 213e52adb7bSmrg if (!real_dlsym) { 214e52adb7bSmrg /* FIXME: This brute-force, hard-coded searching for a versioned 215e52adb7bSmrg * symbol is really ugly. The only reason I'm doing this is because 216e52adb7bSmrg * I need some way to lookup the "dlsym" function in libdl, but 217e52adb7bSmrg * I can't use 'dlsym' to do it. So dlvsym works, but forces me 218e52adb7bSmrg * to guess what the right version is. 219e52adb7bSmrg * 220e52adb7bSmrg * Potential fixes here: 221e52adb7bSmrg * 222e52adb7bSmrg * 1. Use libelf to actually inspect libdl.so and 223e52adb7bSmrg * find the right version, (finding the right 224e52adb7bSmrg * libdl.so can be made easier with 225e52adb7bSmrg * dl_iterate_phdr). 226e52adb7bSmrg * 227e52adb7bSmrg * 2. Use libelf to find the offset of the 'dlsym' 228e52adb7bSmrg * symbol within libdl.so, (and then add this to 229e52adb7bSmrg * the base address at which libdl.so is loaded 230e52adb7bSmrg * as reported by dl_iterate_phdr). 231e52adb7bSmrg * 232e52adb7bSmrg * In the meantime, I'll just keep augmenting this 233e52adb7bSmrg * hard-coded version list as people report bugs. */ 234e52adb7bSmrg const char *version[] = { 235e52adb7bSmrg "GLIBC_2.2.5", 236e52adb7bSmrg "GLIBC_2.0" 237e52adb7bSmrg }; 238e52adb7bSmrg int num_versions = sizeof(version) / sizeof(version[0]); 239e52adb7bSmrg int i; 240e52adb7bSmrg for (i = 0; i < num_versions; i++) { 241e52adb7bSmrg real_dlsym = (fips_dlsym_t) dlvsym(RTLD_NEXT, "dlsym", version[i]); 242e52adb7bSmrg if (real_dlsym) 243e52adb7bSmrg break; 244e52adb7bSmrg } 245e52adb7bSmrg if (i == num_versions) { 246e52adb7bSmrg fprintf(stderr, "Internal error: Failed to find real dlsym\n"); 247e52adb7bSmrg fprintf(stderr, 248e52adb7bSmrg "This may be a simple matter of fips not knowing about the version of GLIBC that\n" 249e52adb7bSmrg "your program is using. Current known versions are:\n\n\t"); 250e52adb7bSmrg for (i = 0; i < num_versions; i++) 251e52adb7bSmrg fprintf(stderr, "%s ", version[i]); 252e52adb7bSmrg fprintf(stderr, 253e52adb7bSmrg "\n\nYou can inspect your version by first finding libdl.so.2:\n" 254e52adb7bSmrg "\n" 255e52adb7bSmrg "\tldd <your-program> | grep libdl.so\n" 256e52adb7bSmrg "\n" 257e52adb7bSmrg "And then inspecting the version attached to the dlsym symbol:\n" 258e52adb7bSmrg "\n" 259e52adb7bSmrg "\treadelf -s /path/to/libdl.so.2 | grep dlsym\n" 260e52adb7bSmrg "\n" 261e52adb7bSmrg "And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n"); 262e52adb7bSmrg 263e52adb7bSmrg exit(1); 264e52adb7bSmrg } 265e52adb7bSmrg } 266e52adb7bSmrg 267e52adb7bSmrg return real_dlsym(handle, name); 268e52adb7bSmrg} 269e52adb7bSmrg 270e52adb7bSmrgvoid * 271e52adb7bSmrgoverride_GL_glXGetProcAddress(const char *name) 272e52adb7bSmrg{ 273e52adb7bSmrg void *symbol; 274e52adb7bSmrg 275e52adb7bSmrg symbol = wrapped_dlsym("GL", name); 276e52adb7bSmrg if (symbol) 277e52adb7bSmrg return symbol; 278e52adb7bSmrg 279e52adb7bSmrg return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddress, 280e52adb7bSmrg "glXGetProcAddress", (name)); 281e52adb7bSmrg} 282e52adb7bSmrg 283e52adb7bSmrgvoid * 284e52adb7bSmrgoverride_GL_glXGetProcAddressARB(const char *name) 285e52adb7bSmrg{ 286e52adb7bSmrg void *symbol; 287e52adb7bSmrg 288e52adb7bSmrg symbol = wrapped_dlsym("GL", name); 289e52adb7bSmrg if (symbol) 290e52adb7bSmrg return symbol; 291e52adb7bSmrg 292e52adb7bSmrg return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddressARB, 293e52adb7bSmrg "glXGetProcAddressARB", (name)); 294e52adb7bSmrg} 295e52adb7bSmrg 296e52adb7bSmrgvoid * 297e52adb7bSmrgoverride_EGL_eglGetProcAddress(const char *name) 298e52adb7bSmrg{ 299e52adb7bSmrg void *symbol; 300e52adb7bSmrg 301e52adb7bSmrg if (!STRNCMP_LITERAL(name, "gl")) { 302e52adb7bSmrg symbol = wrapped_dlsym("GLES2", name); 303e52adb7bSmrg if (symbol) 304e52adb7bSmrg return symbol; 305e52adb7bSmrg } 306e52adb7bSmrg 307e52adb7bSmrg if (!STRNCMP_LITERAL(name, "egl")) { 308e52adb7bSmrg symbol = wrapped_dlsym("EGL", name); 309e52adb7bSmrg if (symbol) 310e52adb7bSmrg return symbol; 311e52adb7bSmrg } 312e52adb7bSmrg 313e52adb7bSmrg return DEFER_TO_GL("libEGL.so.1", override_EGL_eglGetProcAddress, 314e52adb7bSmrg "eglGetProcAddress", (name)); 315e52adb7bSmrg} 316