xref: /aosp_15_r20/external/libepoxy/test/dlwrap.c (revision 706d0b42ae4182339789e08d473a0b312ecdc60f)
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