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