]> git.cworth.org Git - fips/blob - dlwrap.c
dlwrap: Add "libGLESv2.so" to the list of supported wrapped libraries
[fips] / dlwrap.c
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 /* dladdr is a glibc extension */
23 #define _GNU_SOURCE
24 #include <dlfcn.h>
25
26 #include <assert.h>
27
28 #include "fips.h"
29
30 #include "dlwrap.h"
31
32 #include "glwrap.h"
33
34 void *libfips_handle;
35
36 typedef void * (* fips_dlopen_t)(const char * filename, int flag);
37 typedef void * (* fips_dlsym_t)(void *handle, const char *symbol);
38
39 static const char *wrapped_libs[] = {
40         "libGL.so",
41         "libEGL.so",
42         "libGLESv2.so"
43 };
44
45 static void *orig_handles[ARRAY_SIZE(wrapped_libs)];
46
47 /* Match 'filename' against an internal list of libraries for which
48  * libfips has wrappers.
49  *
50  * Returns true and sets *index_ret if a match is found.
51  * Returns false if no match is found. */
52 static bool
53 find_wrapped_library_index (const char *filename, unsigned *index_ret)
54 {
55         unsigned i;
56
57         for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) {
58                 if (strncmp(wrapped_libs[i], filename,
59                             strlen (wrapped_libs[i])) == 0)
60                 {
61                         *index_ret = i;
62                         return true;
63                 }
64         }
65
66         return false;
67 }
68
69 /* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking
70  * against it directly, which means they would not be seeing our
71  * wrapped GL symbols via LD_PRELOAD. So we catch the dlopen in a
72  * wrapper here and redirect it to our library.
73  */
74 void *
75 dlopen (const char *filename, int flag)
76 {
77         Dl_info info;
78         void *ret;
79         unsigned index;
80
81         /* Before deciding whether to redirect this dlopen to our own
82          * library, we call the real dlopen. This assures that any
83          * expected side-effects from loading the intended library are
84          * resolved. Below, we may still return a handle pointing to
85          * our own library, and not what is opened here. */
86         ret = dlwrap_real_dlopen (filename, flag);
87
88         /* If filename is not a wrapped library, just return real dlopen */
89         if (! find_wrapped_library_index (filename, &index))
90                 return ret;
91
92         /* When the application dlopens any wrapped library starting
93          * with 'libGL', (whether libGL.so.1 or libGLESv2.so.2), let's
94          * continue to use that library handle for future lookups of
95          * OpenGL functions. */
96         if (STRNCMP_LITERAL (filename, "libGL") == 0)
97                 glwrap_set_gl_handle (ret);
98
99         assert (index < ARRAY_SIZE(orig_handles));
100         orig_handles[index] = ret;
101
102         /* Otherwise, we return our own handle so that we can intercept
103          * future calls to dlsym. We encode the index in the return value
104          * so that we can later map back to the originally requested
105          * dlopen-handle if necessary. */
106         if (libfips_handle)
107                 return libfips_handle + index;
108
109         /* We find our own filename by looking up this very function
110          * (that is, this "dlopen"), with dladdr).*/
111         if (dladdr (dlopen, &info) == 0) {
112                 fprintf (stderr, "Error: Failed to redirect dlopen of %s:\n",
113                          filename);
114                 exit (1);
115         }
116
117         libfips_handle = dlwrap_real_dlopen (info.dli_fname, flag);
118
119         return libfips_handle + index;
120 }
121
122 void *
123 dlwrap_real_dlopen (const char *filename, int flag)
124 {
125         static fips_dlopen_t real_dlopen = NULL;
126
127         if (! real_dlopen) {
128                 real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym (RTLD_NEXT, "dlopen");
129                 if (! real_dlopen) {
130                         fprintf (stderr, "Error: Failed to find symbol for dlopen.\n");
131                         exit (1);
132                 }
133         }
134
135         return real_dlopen (filename, flag);
136 }
137
138 /* Since we redirect dlopens of libGL.so and libEGL.so to libfips we
139  * need to ensure that dlysm succeeds for all functions that might be
140  * defined in the real, underlying libGL library. But we're far too
141  * lazy to implement wrappers for function that would simply
142  * pass-through, so instead we also wrap dlysm and arrange for it to
143  * pass things through with RTLD_next if libfips does not have the
144  * function desired.  */
145 void *
146 dlsym (void *handle, const char *name)
147 {
148         static void *symbol;
149         unsigned index;
150
151         /* All gl* and egl* symbols are preferentially looked up in libfips. */
152         if (STRNCMP_LITERAL (name, "gl") == 0 ||
153             STRNCMP_LITERAL (name, "egl") == 0)
154         {
155                 symbol = dlwrap_real_dlsym (libfips_handle, name);
156                 if (symbol)
157                         return symbol;
158         }
159
160         /* Failing that, anything specifically requested from the
161          * libfips library should be redirected to a real GL
162          * library. */
163
164         /* We subtract the index back out of the handle (see the addition
165          * of the index in our wrapper for dlopen above) to then use the
166          * correct, original dlopen'ed handle for the library of
167          * interest. */
168         index = handle - libfips_handle;
169         if (index < ARRAY_SIZE(orig_handles)) {
170                 return dlwrap_real_dlsym (orig_handles[index], name);
171         }
172
173         /* And anything else is some unrelated dlsym. Just pass it
174          * through.  (This also covers the cases of lookups with
175          * special handles such as RTLD_DEFAULT or RTLD_NEXT.)
176          */
177         return dlwrap_real_dlsym (handle, name);
178 }
179
180 void *
181 dlwrap_real_dlsym (void *handle, const char *name)
182 {
183         static fips_dlsym_t real_dlsym = NULL;
184
185         if (! real_dlsym) {
186                 /* FIXME: This brute-force, hard-coded searching for a versioned
187                  * symbol is really ugly. The only reason I'm doing this is because
188                  * I need some way to lookup the "dlsym" function in libdl, but
189                  * I can't use 'dlsym' to do it. So dlvsym works, but forces me
190                  * to guess what the right version is.
191                  *
192                  * Potential fixes here:
193                  *
194                  *   1. Use libelf to actually inspect libdl.so and
195                  *      find the right version, (finding the right
196                  *      libdl.so can be made easier with
197                  *      dl_iterate_phdr).
198                  *
199                  *   2. Use libelf to find the offset of the 'dlsym'
200                  *      symbol within libdl.so, (and then add this to
201                  *      the base address at which libdl.so is loaded
202                  *      as reported by dl_iterate_phdr).
203                  *
204                  * In the meantime, I'll just keep augmenting this
205                  * hard-coded version list as people report bugs. */
206                 const char *version[] = {
207                         "GLIBC_2.2.5",
208                         "GLIBC_2.0"
209                 };
210                 int num_versions = sizeof(version) / sizeof(version[0]);
211                 int i;
212                 for (i = 0; i < num_versions; i++) {
213                         real_dlsym = (fips_dlsym_t) dlvsym (RTLD_NEXT, "dlsym",
214                                                             version[i]);
215                         if (real_dlsym)
216                                 break;
217                 }
218                 if (i == num_versions) {
219                         fprintf (stderr, "Internal error: Failed to find real dlsym\n");
220                         fprintf (stderr,
221 "This may be a simple matter of fips not knowing about the version of GLIBC that\n"
222 "your program is using. Current known versions are:\n\n\t");
223                         for (i = 0; i < num_versions; i++)
224                                 fprintf (stderr, "%s ", version[i]);
225                         fprintf(stderr,
226 "\n\nYou can inspect your version by first finding libdl.so.2:\n"
227 "\n"
228 "\tldd <your-program> | grep libdl.so\n"
229 "\n"
230 "And then inspecting the version attached to the dlsym symbol:\n"
231 "\n"
232 "\treadelf -s /path/to/libdl.so.2 | grep dlsym\n"
233 "\n"
234 "And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n");
235
236                         exit (1);
237                 }
238         }
239
240         return real_dlsym (handle, name);
241 }