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