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 * |
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(s1: wrapped_libs[i].filename, s2: filename, |
88 | n: strlen(s: 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 * |
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 |
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, symbol: "__dlclose" ); |
136 | real_dlclose(handle); |
137 | } |
138 | } |
139 | |
140 | void * |
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, symbol: "dlopen" ); |
147 | if (!real_dlopen) { |
148 | fputs(s: "Error: Failed to find symbol for dlopen.\n" , stderr); |
149 | exit(status: 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 * |
161 | wrapped_dlsym(const char *prefix, const char *name) |
162 | { |
163 | char *wrap_name; |
164 | void *symbol; |
165 | |
166 | if (asprintf(ptr: &wrap_name, fmt: "override_%s_%s" , prefix, name) < 0) { |
167 | fputs(s: "Error: Failed to allocate memory.\n" , stderr); |
168 | abort(); |
169 | } |
170 | |
171 | symbol = dlwrap_real_dlsym(RTLD_DEFAULT, symbol: wrap_name); |
172 | free(ptr: 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 * |
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(prefix: wrap->symbol_prefix, name); |
200 | if (symbol) |
201 | return symbol; |
202 | else |
203 | return dlwrap_real_dlsym(handle: wrap->handle, symbol: 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, symbol: name); |
211 | } |
212 | |
213 | void * |
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, name: "dlsym" , version: version[i]); |
252 | if (real_dlsym) |
253 | break; |
254 | } |
255 | if (i == num_versions) { |
256 | fputs(s: "Internal error: Failed to find real dlsym\n" , stderr); |
257 | fputs(s: "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, format: "%s " , version[i]); |
262 | fputs(s: "\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(status: 1); |
274 | } |
275 | } |
276 | |
277 | return real_dlsym(handle, name); |
278 | } |
279 | |
280 | void * |
281 | override_GL_glXGetProcAddress(const char *name) |
282 | { |
283 | void *symbol; |
284 | |
285 | symbol = wrapped_dlsym(prefix: "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 * |
294 | override_GL_glXGetProcAddressARB(const char *name) |
295 | { |
296 | void *symbol; |
297 | |
298 | symbol = wrapped_dlsym(prefix: "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 * |
307 | override_EGL_eglGetProcAddress(const char *name) |
308 | { |
309 | void *symbol; |
310 | |
311 | if (!STRNCMP_LITERAL(name, "gl" )) { |
312 | symbol = wrapped_dlsym(prefix: "GLES2" , name); |
313 | if (symbol) |
314 | return symbol; |
315 | } |
316 | |
317 | if (!STRNCMP_LITERAL(name, "egl" )) { |
318 | symbol = wrapped_dlsym(prefix: "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 | |