dlwrap.c revision ca86eba8
1e52adb7bSmrg/* Copyright © 2013, Intel Corporation
2e52adb7bSmrg *
3e52adb7bSmrg * Permission is hereby granted, free of charge, to any person obtaining a copy
4e52adb7bSmrg * of this software and associated documentation files (the "Software"), to deal
5e52adb7bSmrg * in the Software without restriction, including without limitation the rights
6e52adb7bSmrg * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7e52adb7bSmrg * copies of the Software, and to permit persons to whom the Software is
8e52adb7bSmrg * furnished to do so, subject to the following conditions:
9e52adb7bSmrg *
10e52adb7bSmrg * The above copyright notice and this permission notice shall be included in
11e52adb7bSmrg * all copies or substantial portions of the Software.
12e52adb7bSmrg *
13e52adb7bSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14e52adb7bSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15e52adb7bSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16e52adb7bSmrg * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17e52adb7bSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18e52adb7bSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19e52adb7bSmrg * THE SOFTWARE.
20e52adb7bSmrg */
21e52adb7bSmrg
22e52adb7bSmrg/** @file dlwrap.c
23e52adb7bSmrg *
24e52adb7bSmrg * Implements a wrapper for dlopen() and dlsym() so that epoxy will
25e52adb7bSmrg * end up finding symbols from the testcases named
26e52adb7bSmrg * "override_EGL_eglWhatever()" or "override_GLES2_glWhatever()" or
27e52adb7bSmrg * "override_GL_glWhatever()" when it tries to dlopen() and dlsym()
28e52adb7bSmrg * the real GL or EGL functions in question.
29e52adb7bSmrg *
30e52adb7bSmrg * This lets us simulate some target systems in the test suite, or
31e52adb7bSmrg * just stub out GL functions so we can be sure of what's being
32e52adb7bSmrg * called.
33e52adb7bSmrg */
34e52adb7bSmrg
35e52adb7bSmrg/* dladdr is a glibc extension */
36e52adb7bSmrg#define _GNU_SOURCE
37e52adb7bSmrg#include <dlfcn.h>
38e52adb7bSmrg
39e52adb7bSmrg#include <stdbool.h>
40e52adb7bSmrg#include <stdio.h>
41e52adb7bSmrg#include <stdlib.h>
42e52adb7bSmrg#include <string.h>
43e52adb7bSmrg#include <assert.h>
44e52adb7bSmrg
45e52adb7bSmrg#include "dlwrap.h"
46e52adb7bSmrg
47e52adb7bSmrg#define STRNCMP_LITERAL(var, literal) \
48e52adb7bSmrg    strncmp ((var), (literal), sizeof (literal) - 1)
49e52adb7bSmrg
50e52adb7bSmrg#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
51e52adb7bSmrg
52e52adb7bSmrgvoid *libfips_handle;
53e52adb7bSmrg
54e52adb7bSmrgtypedef void *(*fips_dlopen_t)(const char *filename, int flag);
55e52adb7bSmrgtypedef void *(*fips_dlsym_t)(void *handle, const char *symbol);
56e52adb7bSmrg
57e52adb7bSmrgvoid *override_EGL_eglGetProcAddress(const char *name);
58e52adb7bSmrgvoid *override_GL_glXGetProcAddress(const char *name);
59e52adb7bSmrgvoid *override_GL_glXGetProcAddressARB(const char *name);
60e52adb7bSmrgvoid __dlclose(void *handle);
61e52adb7bSmrg
62e52adb7bSmrgstatic struct libwrap {
63e52adb7bSmrg    const char *filename;
64e52adb7bSmrg    const char *symbol_prefix;
65e52adb7bSmrg    void *handle;
66e52adb7bSmrg} wrapped_libs[] = {
67e52adb7bSmrg    { "libGL.so", "GL", NULL },
68e52adb7bSmrg    { "libEGL.so", "EGL", NULL },
69e52adb7bSmrg    { "libGLESv2.so", "GLES2", NULL },
70ca86eba8Smrg    { "libOpenGL.so", "GL", NULL},
71e52adb7bSmrg};
72e52adb7bSmrg
73e52adb7bSmrg/* Match 'filename' against an internal list of libraries for which
74e52adb7bSmrg * libfips has wrappers.
75e52adb7bSmrg *
76e52adb7bSmrg * Returns true and sets *index_ret if a match is found.
77e52adb7bSmrg * Returns false if no match is found. */
78e52adb7bSmrgstatic struct libwrap *
79e52adb7bSmrgfind_wrapped_library(const char *filename)
80e52adb7bSmrg{
81e52adb7bSmrg    unsigned i;
82e52adb7bSmrg
83e52adb7bSmrg    if (!filename)
84e52adb7bSmrg        return NULL;
85e52adb7bSmrg
86e52adb7bSmrg    for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) {
87e52adb7bSmrg        if (strncmp(wrapped_libs[i].filename, filename,
88e52adb7bSmrg                    strlen(wrapped_libs[i].filename)) == 0) {
89e52adb7bSmrg            return &wrapped_libs[i];
90e52adb7bSmrg        }
91e52adb7bSmrg    }
92e52adb7bSmrg
93e52adb7bSmrg    return NULL;
94e52adb7bSmrg}
95e52adb7bSmrg
96e52adb7bSmrg/* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking
97e52adb7bSmrg * against it directly, which means they would not be seeing our
98e52adb7bSmrg * wrapped GL symbols via LD_PRELOAD. So we catch the dlopen in a
99e52adb7bSmrg * wrapper here and redirect it to our library.
100e52adb7bSmrg */
101e52adb7bSmrgvoid *
102e52adb7bSmrgdlopen(const char *filename, int flag)
103e52adb7bSmrg{
104e52adb7bSmrg    void *ret;
105e52adb7bSmrg    struct libwrap *wrap;
106e52adb7bSmrg
107e52adb7bSmrg    /* Before deciding whether to redirect this dlopen to our own
108e52adb7bSmrg     * library, we call the real dlopen. This assures that any
109e52adb7bSmrg     * expected side-effects from loading the intended library are
110e52adb7bSmrg     * resolved. Below, we may still return a handle pointing to
111e52adb7bSmrg     * our own library, and not what is opened here. */
112e52adb7bSmrg    ret = dlwrap_real_dlopen(filename, flag);
113e52adb7bSmrg
114e52adb7bSmrg    /* If filename is not a wrapped library, just return real dlopen */
115e52adb7bSmrg    wrap = find_wrapped_library(filename);
116e52adb7bSmrg    if (!wrap)
117e52adb7bSmrg        return ret;
118e52adb7bSmrg
119e52adb7bSmrg    wrap->handle = ret;
120e52adb7bSmrg
121e52adb7bSmrg    /* We use wrapped_libs as our handles to libraries. */
122e52adb7bSmrg    return wrap;
123e52adb7bSmrg}
124e52adb7bSmrg
125e52adb7bSmrg/**
126e52adb7bSmrg * Wraps dlclose to hide our faked handles from it.
127e52adb7bSmrg */
128e52adb7bSmrgvoid
129e52adb7bSmrg__dlclose(void *handle)
130e52adb7bSmrg{
131e52adb7bSmrg    struct libwrap *wrap = handle;
132e52adb7bSmrg
133e52adb7bSmrg    if (wrap < wrapped_libs ||
134e52adb7bSmrg        wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) {
135e52adb7bSmrg        void (*real_dlclose)(void *handle) = dlwrap_real_dlsym(RTLD_NEXT, "__dlclose");
136e52adb7bSmrg        real_dlclose(handle);
137e52adb7bSmrg    }
138e52adb7bSmrg}
139e52adb7bSmrg
140e52adb7bSmrgvoid *
141e52adb7bSmrgdlwrap_real_dlopen(const char *filename, int flag)
142e52adb7bSmrg{
143e52adb7bSmrg    static fips_dlopen_t real_dlopen = NULL;
144e52adb7bSmrg
145e52adb7bSmrg    if (!real_dlopen) {
146e52adb7bSmrg        real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym(RTLD_NEXT, "dlopen");
147e52adb7bSmrg        if (!real_dlopen) {
148ca86eba8Smrg            fputs("Error: Failed to find symbol for dlopen.\n", stderr);
149e52adb7bSmrg            exit(1);
150e52adb7bSmrg        }
151e52adb7bSmrg    }
152e52adb7bSmrg
153e52adb7bSmrg    return real_dlopen(filename, flag);
154e52adb7bSmrg}
155e52adb7bSmrg
156e52adb7bSmrg/**
157e52adb7bSmrg * Return the dlsym() on the application's namespace for
158e52adb7bSmrg * "override_<prefix>_<name>"
159e52adb7bSmrg */
160e52adb7bSmrgstatic void *
161e52adb7bSmrgwrapped_dlsym(const char *prefix, const char *name)
162e52adb7bSmrg{
163e52adb7bSmrg    char *wrap_name;
164e52adb7bSmrg    void *symbol;
165e52adb7bSmrg
166ca86eba8Smrg    if (asprintf(&wrap_name, "override_%s_%s", prefix, name) < 0) {
167ca86eba8Smrg        fputs("Error: Failed to allocate memory.\n", stderr);
168ca86eba8Smrg        abort();
169ca86eba8Smrg    }
170ca86eba8Smrg
171e52adb7bSmrg    symbol = dlwrap_real_dlsym(RTLD_DEFAULT, wrap_name);
172e52adb7bSmrg    free(wrap_name);
173e52adb7bSmrg    return symbol;
174e52adb7bSmrg}
175e52adb7bSmrg
176e52adb7bSmrg/* Since we redirect dlopens of libGL.so and libEGL.so to libfips we
177e52adb7bSmrg * need to ensure that dlysm succeeds for all functions that might be
178e52adb7bSmrg * defined in the real, underlying libGL library. But we're far too
179e52adb7bSmrg * lazy to implement wrappers for function that would simply
180e52adb7bSmrg * pass-through, so instead we also wrap dlysm and arrange for it to
181e52adb7bSmrg * pass things through with RTLD_next if libfips does not have the
182e52adb7bSmrg * function desired.  */
183e52adb7bSmrgvoid *
184e52adb7bSmrgdlsym(void *handle, const char *name)
185e52adb7bSmrg{
186e52adb7bSmrg    struct libwrap *wrap = handle;
187e52adb7bSmrg
188e52adb7bSmrg    /* Make sure that handle is actually one of our wrapped libs. */
189e52adb7bSmrg    if (wrap < wrapped_libs ||
190e52adb7bSmrg        wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) {
191e52adb7bSmrg        wrap = NULL;
192e52adb7bSmrg    }
193e52adb7bSmrg
194e52adb7bSmrg    /* Failing that, anything specifically requested from the
195e52adb7bSmrg     * libfips library should be redirected to a real GL
196e52adb7bSmrg     * library. */
197e52adb7bSmrg
198e52adb7bSmrg    if (wrap) {
199e52adb7bSmrg        void *symbol = wrapped_dlsym(wrap->symbol_prefix, name);
200e52adb7bSmrg        if (symbol)
201e52adb7bSmrg            return symbol;
202e52adb7bSmrg        else
203e52adb7bSmrg            return dlwrap_real_dlsym(wrap->handle, name);
204e52adb7bSmrg    }
205e52adb7bSmrg
206e52adb7bSmrg    /* And anything else is some unrelated dlsym. Just pass it
207e52adb7bSmrg     * through.  (This also covers the cases of lookups with
208e52adb7bSmrg     * special handles such as RTLD_DEFAULT or RTLD_NEXT.)
209e52adb7bSmrg     */
210e52adb7bSmrg    return dlwrap_real_dlsym(handle, name);
211e52adb7bSmrg}
212e52adb7bSmrg
213e52adb7bSmrgvoid *
214e52adb7bSmrgdlwrap_real_dlsym(void *handle, const char *name)
215e52adb7bSmrg{
216e52adb7bSmrg    static fips_dlsym_t real_dlsym = NULL;
217e52adb7bSmrg
218e52adb7bSmrg    if (!real_dlsym) {
219e52adb7bSmrg        /* FIXME: This brute-force, hard-coded searching for a versioned
220e52adb7bSmrg         * symbol is really ugly. The only reason I'm doing this is because
221e52adb7bSmrg         * I need some way to lookup the "dlsym" function in libdl, but
222e52adb7bSmrg         * I can't use 'dlsym' to do it. So dlvsym works, but forces me
223e52adb7bSmrg         * to guess what the right version is.
224e52adb7bSmrg         *
225e52adb7bSmrg         * Potential fixes here:
226e52adb7bSmrg         *
227e52adb7bSmrg         *   1. Use libelf to actually inspect libdl.so and
228e52adb7bSmrg         *      find the right version, (finding the right
229e52adb7bSmrg         *      libdl.so can be made easier with
230e52adb7bSmrg         *      dl_iterate_phdr).
231e52adb7bSmrg         *
232e52adb7bSmrg         *   2. Use libelf to find the offset of the 'dlsym'
233e52adb7bSmrg         *      symbol within libdl.so, (and then add this to
234e52adb7bSmrg         *      the base address at which libdl.so is loaded
235e52adb7bSmrg         *      as reported by dl_iterate_phdr).
236e52adb7bSmrg         *
237e52adb7bSmrg         * In the meantime, I'll just keep augmenting this
238e52adb7bSmrg         * hard-coded version list as people report bugs. */
239e52adb7bSmrg        const char *version[] = {
240f71742dfSmrg            "GLIBC_2.17",
241f71742dfSmrg            "GLIBC_2.4",
242f71742dfSmrg            "GLIBC_2.3",
243e52adb7bSmrg            "GLIBC_2.2.5",
244f71742dfSmrg            "GLIBC_2.2",
245ca86eba8Smrg            "GLIBC_2.0",
246ca86eba8Smrg            "FBSD_1.0"
247e52adb7bSmrg        };
248e52adb7bSmrg        int num_versions = sizeof(version) / sizeof(version[0]);
249e52adb7bSmrg        int i;
250e52adb7bSmrg        for (i = 0; i < num_versions; i++) {
251e52adb7bSmrg            real_dlsym = (fips_dlsym_t) dlvsym(RTLD_NEXT, "dlsym", version[i]);
252e52adb7bSmrg            if (real_dlsym)
253e52adb7bSmrg                break;
254e52adb7bSmrg        }
255e52adb7bSmrg        if (i == num_versions) {
256ca86eba8Smrg            fputs("Internal error: Failed to find real dlsym\n", stderr);
257ca86eba8Smrg            fputs("This may be a simple matter of fips not knowing about the version of GLIBC that\n"
258ca86eba8Smrg                  "your program is using. Current known versions are:\n\n\t",
259ca86eba8Smrg                  stderr);
260e52adb7bSmrg            for (i = 0; i < num_versions; i++)
261e52adb7bSmrg                fprintf(stderr, "%s ", version[i]);
262ca86eba8Smrg            fputs("\n\nYou can inspect your version by first finding libdl.so.2:\n"
263ca86eba8Smrg                  "\n"
264ca86eba8Smrg                  "\tldd <your-program> | grep libdl.so\n"
265ca86eba8Smrg                  "\n"
266ca86eba8Smrg                  "And then inspecting the version attached to the dlsym symbol:\n"
267ca86eba8Smrg                  "\n"
268ca86eba8Smrg                  "\treadelf -s /path/to/libdl.so.2 | grep dlsym\n"
269ca86eba8Smrg                  "\n"
270ca86eba8Smrg                  "And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n",
271ca86eba8Smrg                  stderr);
272e52adb7bSmrg
273e52adb7bSmrg            exit(1);
274e52adb7bSmrg        }
275e52adb7bSmrg    }
276e52adb7bSmrg
277e52adb7bSmrg    return real_dlsym(handle, name);
278e52adb7bSmrg}
279e52adb7bSmrg
280e52adb7bSmrgvoid *
281e52adb7bSmrgoverride_GL_glXGetProcAddress(const char *name)
282e52adb7bSmrg{
283e52adb7bSmrg    void *symbol;
284e52adb7bSmrg
285e52adb7bSmrg    symbol = wrapped_dlsym("GL", name);
286e52adb7bSmrg    if (symbol)
287e52adb7bSmrg        return symbol;
288e52adb7bSmrg
289e52adb7bSmrg    return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddress,
290e52adb7bSmrg                       "glXGetProcAddress", (name));
291e52adb7bSmrg}
292e52adb7bSmrg
293e52adb7bSmrgvoid *
294e52adb7bSmrgoverride_GL_glXGetProcAddressARB(const char *name)
295e52adb7bSmrg{
296e52adb7bSmrg    void *symbol;
297e52adb7bSmrg
298e52adb7bSmrg    symbol = wrapped_dlsym("GL", name);
299e52adb7bSmrg    if (symbol)
300e52adb7bSmrg        return symbol;
301e52adb7bSmrg
302e52adb7bSmrg    return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddressARB,
303e52adb7bSmrg                       "glXGetProcAddressARB", (name));
304e52adb7bSmrg}
305e52adb7bSmrg
306e52adb7bSmrgvoid *
307e52adb7bSmrgoverride_EGL_eglGetProcAddress(const char *name)
308e52adb7bSmrg{
309e52adb7bSmrg    void *symbol;
310e52adb7bSmrg
311e52adb7bSmrg    if (!STRNCMP_LITERAL(name, "gl")) {
312e52adb7bSmrg        symbol = wrapped_dlsym("GLES2", name);
313e52adb7bSmrg        if (symbol)
314e52adb7bSmrg            return symbol;
315e52adb7bSmrg    }
316e52adb7bSmrg
317e52adb7bSmrg    if (!STRNCMP_LITERAL(name, "egl")) {
318e52adb7bSmrg        symbol = wrapped_dlsym("EGL", name);
319e52adb7bSmrg        if (symbol)
320e52adb7bSmrg            return symbol;
321e52adb7bSmrg    }
322e52adb7bSmrg
323e52adb7bSmrg    return DEFER_TO_GL("libEGL.so.1", override_EGL_eglGetProcAddress,
324e52adb7bSmrg                       "eglGetProcAddress", (name));
325e52adb7bSmrg}
326