1 /* Copyright © 2013, Intel Corporation
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:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
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
22 /* dladdr is a glibc extension */
36 typedef void * (* fips_dlopen_t)(const char * filename, int flag);
37 typedef void * (* fips_dlsym_t)(void *handle, const char *symbol);
39 static const char *wrapped_libs[] = {
45 static void *orig_handles[ARRAY_SIZE(wrapped_libs)];
47 /* Match 'filename' against an internal list of libraries for which
48 * libfips has wrappers.
50 * Returns true and sets *index_ret if a match is found.
51 * Returns false if no match is found. */
53 find_wrapped_library_index (const char *filename, unsigned *index_ret)
57 for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) {
58 if (strncmp(wrapped_libs[i], filename,
59 strlen (wrapped_libs[i])) == 0)
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.
75 dlopen (const char *filename, int flag)
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);
88 /* If filename is not a wrapped library, just return real dlopen */
89 if (! find_wrapped_library_index (filename, &index))
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);
99 assert (index < ARRAY_SIZE(orig_handles));
100 orig_handles[index] = ret;
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. */
107 return libfips_handle + index;
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",
117 libfips_handle = dlwrap_real_dlopen (info.dli_fname, flag);
119 return libfips_handle + index;
123 dlwrap_real_dlopen (const char *filename, int flag)
125 static fips_dlopen_t real_dlopen = NULL;
128 real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym (RTLD_NEXT, "dlopen");
130 fprintf (stderr, "Error: Failed to find symbol for dlopen.\n");
135 return real_dlopen (filename, flag);
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. */
146 dlsym (void *handle, const char *name)
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)
155 symbol = dlwrap_real_dlsym (libfips_handle, name);
160 /* Failing that, anything specifically requested from the
161 * libfips library should be redirected to a real GL
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
168 index = handle - libfips_handle;
169 if (index < ARRAY_SIZE(orig_handles)) {
170 return dlwrap_real_dlsym (orig_handles[index], name);
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.)
177 return dlwrap_real_dlsym (handle, name);
181 dlwrap_real_dlsym (void *handle, const char *name)
183 static fips_dlsym_t real_dlsym = NULL;
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.
192 * Potential fixes here:
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
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).
204 * In the meantime, I'll just keep augmenting this
205 * hard-coded version list as people report bugs. */
206 const char *version[] = {
210 int num_versions = sizeof(version) / sizeof(version[0]);
212 for (i = 0; i < num_versions; i++) {
213 real_dlsym = (fips_dlsym_t) dlvsym (RTLD_NEXT, "dlsym",
218 if (i == num_versions) {
219 fprintf (stderr, "Internal error: Failed to find real dlsym\n");
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]);
226 "\n\nYou can inspect your version by first finding libdl.so.2:\n"
228 "\tldd <your-program> | grep libdl.so\n"
230 "And then inspecting the version attached to the dlsym symbol:\n"
232 "\treadelf -s /path/to/libdl.so.2 | grep dlsym\n"
234 "And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n");
240 return real_dlsym (handle, name);