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
52 void *libfips_handle;
53
54 typedef void *(*fips_dlopen_t)(const char *filename, int flag);
55 typedef void *(*fips_dlsym_t)(void *handle, const char *symbol);
56
57 void *override_EGL_eglGetProcAddress(const char *name);
58 void *override_GL_glXGetProcAddress(const char *name);
59 void *override_GL_glXGetProcAddressARB(const char *name);
60 void __dlclose(void *handle);
61
62 static 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. */
78 static struct libwrap *
find_wrapped_library(const char * filename)79 find_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 */
101 void *
dlopen(const char * filename,int flag)102 dlopen(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 */
128 void
__dlclose(void * handle)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
140 void *
dlwrap_real_dlopen(const char * filename,int flag)141 dlwrap_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 */
160 static void *
wrapped_dlsym(const char * prefix,const char * name)161 wrapped_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. */
183 void *
dlsym(void * handle,const char * name)184 dlsym(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
213 void *
dlwrap_real_dlsym(void * handle,const char * name)214 dlwrap_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
280 void *
override_GL_glXGetProcAddress(const char * name)281 override_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
293 void *
override_GL_glXGetProcAddressARB(const char * name)294 override_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
306 void *
override_EGL_eglGetProcAddress(const char * name)307 override_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