]> git.cworth.org Git - fips/blob - dlwrap.c
fixup __dlsym
[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 <stdio.h>
27 #include <stdlib.h>
28
29 #include <string.h>
30
31 #include "dlwrap.h"
32
33 #define STRNCMP_LITERAL(var, literal) \
34     strncmp ((var), (literal), sizeof (literal) - 1)
35
36 void *libfips_handle;
37
38 typedef void * (* fips_dlopen_t)(const char * filename, int flag);
39 typedef void * (* fips_dlsym_t)(void *handle, const char *symbol);
40
41 /* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking
42  * against it directly, which means they would not be seeing our
43  * wrapped GL symbols via LD_PRELOAD. So we catch the dlopen in a
44  * wrapper here and redirect it to our library.
45  */
46 void *
47 dlopen (const char *filename, int flag)
48 {
49         Dl_info info;
50
51         /* Not libGL, so just return real dlopen */
52         if (STRNCMP_LITERAL (filename, "libGL.so"))
53                 return dlwrap_real_dlopen (filename, flag);
54
55         /* Redirect all dlopens to libGL to our own wrapper library.
56          * We find our own filename by looking up this very function
57          * (that is, this "dlopen"), with dladdr).*/
58         if (dladdr (dlopen, &info)) {
59                 libfips_handle = dlwrap_real_dlopen (info.dli_fname, flag);
60                 return libfips_handle;
61         } else {
62                 fprintf (stderr, "Error: Failed to redirect dlopen of %s:\n",
63                          filename);
64                 exit (1);
65         }
66 }
67
68 void *
69 dlwrap_real_dlopen (const char *filename, int flag)
70 {
71         fips_dlopen_t real_dlopen = NULL;
72
73         if (! real_dlopen) {
74                 real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym (RTLD_NEXT, "dlopen");
75                 if (! real_dlopen) {
76                         fprintf (stderr, "Error: Failed to find symbol for dlopen.\n");
77                         exit (1);
78                 }
79         }
80
81         return real_dlopen (filename, flag);
82 }
83
84 /* Since we redirect a dlopen of libGL.so to libfips we need to ensure
85  * that dlysm succeeds for all functions that might be defined in the
86  * real, underlying libGL library. But we're far too lazy to implement
87  * wrappers for function that would simply pass-through, so instead we
88  * also wrap dlysm and arrange for it to pass things through with
89  * RTLD_next if libfips does not have the function desired.
90 */
91 void *
92 dlsym (void *handle, const char *name)
93 {
94         static void *libgl_handle = NULL;
95         static void *symbol;
96
97         /* All gl symbols are preferentially looked up in libfips. */
98         if (STRNCMP_LITERAL (name, "gl") == 0) {
99                 symbol = dlwrap_real_dlsym (libfips_handle, name);
100                 if (symbol)
101                         return symbol;
102         }
103
104         /* Failing that, anything specifically requested from the
105          * libfips library should be redirected to a real GL
106          * library. */
107         if (handle == libfips_handle) {
108                 if (! libgl_handle)
109                         libgl_handle = dlwrap_real_dlopen ("libGL.so.1", RTLD_LAZY);
110                 return dlwrap_real_dlsym (libgl_handle, name);
111         }
112
113         /* And anything else is some unrelated dlsym. Just pass it through. */
114         return dlwrap_real_dlsym (handle, name);
115 }
116
117 void *
118 dlwrap_real_dlsym (void *handle, const char *name)
119 {
120         static fips_dlsym_t real_dlsym = NULL;
121
122         if (! real_dlsym) {
123                 /* FIXME: This brute-force, hard-coded searching for a versioned
124                  * symbol is really ugly. The only reason I'm doing this is because
125                  * I need some way to lookup the "dlsym" function in libdl, but
126                  * I can't use 'dlsym' to do it. So dlvsym works, but forces me
127                  * to guess what the right version is.
128                  *
129                  * Potential fixes here:
130                  *
131                  *   1. Use libelf to actually inspect libdl.so and
132                  *      find the right version, (finding the right
133                  *      libdl.so can be made easier with
134                  *      dl_iterate_phdr).
135                  *
136                  *   2. Use libelf to find the offset of the 'dlsym'
137                  *      symbol within libdl.so, (and then add this to
138                  *      the base address at which libdl.so is loaded
139                  *      as reported by dl_iterate_phdr).
140                  *
141                  * In the meantime, I'll just keep augmenting this
142                  * hard-coded version list as people report bugs. */
143                 const char *version[] = {
144                         "GLIBC_2.2.5",
145                         "GLIBC_2.0"
146                 };
147                 int num_versions = sizeof(version) / sizeof(version[0]);
148                 int i;
149                 for (i = 0; i < num_versions; i++) {
150                         real_dlsym = (fips_dlsym_t) dlvsym (RTLD_NEXT, "dlsym",
151                                                             version[i]);
152                         if (real_dlsym)
153                                 break;
154                 }
155                 if (i == num_versions) {
156                         fprintf (stderr, "Internal error: Failed to find real dlsym\n");
157                         fprintf (stderr,
158 "This may be a simple matter of fips not knowing about the version of GLIBC that\n"
159 "your program is using. Current known versions are:\n\n\t");
160                         for (i = 0; i < num_versions; i++)
161                                 fprintf (stderr, "%s ", version[i]);
162                         fprintf(stderr,
163 "\n\nYou can inspect your version by first finding libdl.so.2:\n"
164 "\n"
165 "\tldd <your-program> | grep libdl.so\n"
166 "\n"
167 "And then inspecting the version attached to the dlsym symbol:\n"
168 "\n"
169 "\treadelf -s /path/to/libdl.so.2 | grep dlsym\n"
170 "\n"
171 "And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n");
172
173                         exit (1);
174                 }
175         }
176
177         return real_dlsym (handle, name);
178 }