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