Skip to content

Commit

Permalink
Add a test for EGL and GLX returning different function pointers.
Browse files Browse the repository at this point in the history
This is the case on the nvidia binary driver's GL, or when you've got
a system mixing nvidia binary GL and Mesa EGL.
  • Loading branch information
anholt committed Mar 20, 2014
1 parent dae2316 commit 037ac7f
Show file tree
Hide file tree
Showing 6 changed files with 426 additions and 75 deletions.
2 changes: 2 additions & 0 deletions src/dispatch_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ get_dlopen_handle(void **handle, const char *lib_name, bool exit_on_fail)
abort();
}

fprintf(stderr, "loading %s\n", lib_name);

#ifdef _WIN32
*handle = LoadLibraryA(lib_name);
#else
Expand Down
3 changes: 3 additions & 0 deletions test/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
egl_and_glx_different_pointers_egl
egl_and_glx_different_pointers_egl_glx
egl_and_glx_different_pointers_glx
egl_has_extension_nocontext
egl_gles1_without_glx
egl_gles2_without_glx
Expand Down
31 changes: 31 additions & 0 deletions test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,19 @@ AM_CFLAGS = $(CWARNFLAGS)
TESTS = \
$(EGL_TESTS) \
$(GLX_TESTS) \
$(EGL_AND_GLX_TESTS) \
$(WGL_TESTS) \
headerguards$(EXEEXT) \
miscdefines$(EXEEXT) \
khronos_typedefs$(EXEEXT) \
$()

check_BINARIES = $(EGL_AND_GLX_BIN)

XFAIL_TESTS = \
egl_and_glx_different_pointers_egl_glx \
egl_and_glx_different_pointers_egl \
egl_and_glx_different_pointers_glx \
$()

check_PROGRAMS = $(TESTS)
Expand All @@ -79,6 +85,16 @@ if HAS_ZNOW
GLX_SHARED_ZNOW = glx_shared_znow
endif

if BUILD_EGL
if BUILD_GLX
EGL_AND_GLX_TESTS = \
egl_and_glx_different_pointers_egl_glx \
egl_and_glx_different_pointers_egl \
egl_and_glx_different_pointers_glx \
$()
endif
endif

GLX_TESTS = \
glx_beginend \
glx_public_api \
Expand Down Expand Up @@ -110,6 +126,21 @@ egl_gles2_without_glx_CPPFLAGS = $(AM_CPPFLAGS) -DGLES_VERSION=2
egl_gles2_without_glx_SOURCES = egl_without_glx.c
egl_gles2_without_glx_LDADD = $(EPOXY) $(DLOPEN_LIBS) libegl_common.la $(X11_LIBS)

egl_and_glx_different_pointers_egl_SOURCES = egl_and_glx_different_pointers.c dlwrap.c dlwrap.h
egl_and_glx_different_pointers_egl_LDADD = libegl_common.la libglx_common.la $(DLOPEN_LIBS) $(EPOXY) $(X11_LIBS)
egl_and_glx_different_pointers_egl_LDFLAGS = -rdynamic
egl_and_glx_different_pointers_egl_CPPFLAGS = $(AM_CPPFLAGS) -DUSE_EGL

egl_and_glx_different_pointers_glx_SOURCES = egl_and_glx_different_pointers.c dlwrap.c dlwrap.h
egl_and_glx_different_pointers_glx_LDADD = libegl_common.la libglx_common.la $(DLOPEN_LIBS) $(EPOXY) $(X11_LIBS)
egl_and_glx_different_pointers_glx_LDFLAGS = -rdynamic
egl_and_glx_different_pointers_glx_CPPFLAGS = $(AM_CPPFLAGS) -DUSE_GLX

egl_and_glx_different_pointers_egl_glx_SOURCES = egl_and_glx_different_pointers.c dlwrap.c dlwrap.h
egl_and_glx_different_pointers_egl_glx_LDADD = libegl_common.la libglx_common.la $(DLOPEN_LIBS) $(EPOXY) $(X11_LIBS)
egl_and_glx_different_pointers_egl_glx_LDFLAGS = -rdynamic
egl_and_glx_different_pointers_egl_glx_CPPFLAGS = $(AM_CPPFLAGS) -DUSE_EGL -DUSE_GLX

glx_beginend_LDADD = $(EPOXY) libglx_common.la $(GL_LIBS) $(X11_LIBS)

glx_public_api_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS)
Expand Down
209 changes: 134 additions & 75 deletions test/dlwrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,77 +19,74 @@
* THE SOFTWARE.
*/

/** @file dlwrap.c
*
* Implements a wrapper for dlopen() and dlsym() so that epoxy will
* end up finding symbols from the testcases named
* "override_EGL_eglWhatever()" or "override_GLES2_glWhatever()" or
* "override_GL_glWhatever()" when it tries to dlopen() and dlsym()
* the real GL or EGL functions in question.
*
* This lets us simulate some target systems in the test suite, or
* just stub out GL functions so we can be sure of what's being
* called.
*/

/* dladdr is a glibc extension */
#define _GNU_SOURCE
#include <dlfcn.h>

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "fips.h"

#include "dlwrap.h"

#include "glwrap.h"
#define STRNCMP_LITERAL(var, literal) \
strncmp ((var), (literal), sizeof (literal) - 1)

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

void *libfips_handle;

typedef void *(*fips_dlopen_t)(const char *filename, int flag);
typedef void *(*fips_dlsym_t)(void *handle, const char *symbol);

static const char *wrapped_libs[] = {
"libGL.so",
"libEGL.so",
"libGLESv2.so"
void *override_EGL_eglGetProcAddress(const char *name);
void *override_GL_glXGetProcAddress(const char *name);
void *override_GL_glXGetProcAddressARB(const char *name);
void __dlclose(void *handle);

static struct libwrap {
const char *filename;
const char *symbol_prefix;
void *handle;
} wrapped_libs[] = {
{ "libGL.so", "GL", NULL },
{ "libEGL.so", "EGL", NULL },
{ "libGLESv2.so", "GLES2", NULL },
};

static void *orig_handles[ARRAY_SIZE(wrapped_libs)];

/* Match 'filename' against an internal list of libraries for which
* libfips has wrappers.
*
* Returns true and sets *index_ret if a match is found.
* Returns false if no match is found. */
static bool
find_wrapped_library_index(const char *filename, unsigned *index_ret)
static struct libwrap *
find_wrapped_library(const char *filename)
{
unsigned i;

for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) {
if (strncmp(wrapped_libs[i], filename,
strlen(wrapped_libs[i])) == 0)
{
*index_ret = i;
return true;
}
}

return false;
}

/* Perform a dlopen on the libfips library itself.
*
* Many places in fips need to lookup symbols within the libfips
* library itself, (and not in any other library). This function
* provides a reliable way to get a handle for performing such
* lookups.
*
* The returned handle can be passed to dlwrap_real_dlsym for the
* lookups. */
void *
dlwrap_dlopen_libfips(void)
{
Dl_info info;

/* We first find our own filename by looking up a function
* known to exist only in libfips. This function itself
* (dlwrap_dlopen_libfips) is a good one for that purpose. */
if (dladdr(dlwrap_dlopen_libfips, &info) == 0) {
fprintf(stderr, "Internal error: Failed to lookup filename of "
"libfips library with dladdr\n");
exit(1);
if (strncmp(wrapped_libs[i].filename, filename,
strlen(wrapped_libs[i].filename)) == 0) {
return &wrapped_libs[i];
}
}

return dlwrap_real_dlopen(info.dli_fname, RTLD_NOW);
return NULL;
}

/* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking
Expand All @@ -101,7 +98,7 @@ void *
dlopen(const char *filename, int flag)
{
void *ret;
unsigned index;
struct libwrap *wrap;

/* Before deciding whether to redirect this dlopen to our own
* library, we call the real dlopen. This assures that any
Expand All @@ -111,27 +108,29 @@ dlopen(const char *filename, int flag)
ret = dlwrap_real_dlopen(filename, flag);

/* If filename is not a wrapped library, just return real dlopen */
if (!find_wrapped_library_index(filename, &index))
wrap = find_wrapped_library(filename);
if (!wrap)
return ret;

/* When the application dlopens any wrapped library starting
* with 'libGL', (whether libGL.so.1 or libGLESv2.so.2), let's
* continue to use that library handle for future lookups of
* OpenGL functions. */
if (STRNCMP_LITERAL(filename, "libGL") == 0)
glwrap_set_gl_handle(ret);
wrap->handle = ret;

assert(index < ARRAY_SIZE(orig_handles));
orig_handles[index] = ret;
/* We use wrapped_libs as our handles to libraries. */
return wrap;
}

if (libfips_handle == NULL)
libfips_handle = dlwrap_dlopen_libfips();
/**
* Wraps dlclose to hide our faked handles from it.
*/
void
__dlclose(void *handle)
{
struct libwrap *wrap = handle;

/* Otherwise, we return our own handle so that we can intercept
* future calls to dlsym. We encode the index in the return value
* so that we can later map back to the originally requested
* dlopen-handle if necessary. */
return libfips_handle + index;
if (wrap < wrapped_libs ||
wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) {
void (*real_dlclose)(void *handle) = dlwrap_real_dlsym(RTLD_NEXT, "__dlclose");
real_dlclose(handle);
}
}

void *
Expand All @@ -150,6 +149,22 @@ dlwrap_real_dlopen(const char *filename, int flag)
return real_dlopen(filename, flag);
}

/**
* Return the dlsym() on the application's namespace for
* "override_<prefix>_<name>"
*/
static void *
wrapped_dlsym(const char *prefix, const char *name)
{
char *wrap_name;
void *symbol;

asprintf(&wrap_name, "override_%s_%s", prefix, name);
symbol = dlwrap_real_dlsym(RTLD_DEFAULT, wrap_name);
free(wrap_name);
return symbol;
}

/* Since we redirect dlopens of libGL.so and libEGL.so to libfips we
* need to ensure that dlysm succeeds for all functions that might be
* defined in the real, underlying libGL library. But we're far too
Expand All @@ -160,27 +175,24 @@ dlwrap_real_dlopen(const char *filename, int flag)
void *
dlsym(void *handle, const char *name)
{
static void *symbol;
unsigned index;
struct libwrap *wrap = handle;

/* All gl* and egl* symbols are preferentially looked up in libfips. */
if (STRNCMP_LITERAL(name, "gl") == 0 || STRNCMP_LITERAL(name, "egl") == 0) {
symbol = dlwrap_real_dlsym(libfips_handle, name);
if (symbol)
return symbol;
/* Make sure that handle is actually one of our wrapped libs. */
if (wrap < wrapped_libs ||
wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) {
wrap = NULL;
}

/* Failing that, anything specifically requested from the
* libfips library should be redirected to a real GL
* library. */

/* We subtract the index back out of the handle (see the addition
* of the index in our wrapper for dlopen above) to then use the
* correct, original dlopen'ed handle for the library of
* interest. */
index = handle - libfips_handle;
if (index < ARRAY_SIZE(orig_handles)) {
return dlwrap_real_dlsym(orig_handles[index], name);
if (wrap) {
void *symbol = wrapped_dlsym(wrap->symbol_prefix, name);
if (symbol)
return symbol;
else
return dlwrap_real_dlsym(wrap->handle, name);
}

/* And anything else is some unrelated dlsym. Just pass it
Expand Down Expand Up @@ -251,3 +263,50 @@ dlwrap_real_dlsym(void *handle, const char *name)

return real_dlsym(handle, name);
}

void *
override_GL_glXGetProcAddress(const char *name)
{
void *symbol;

symbol = wrapped_dlsym("GL", name);
if (symbol)
return symbol;

return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddress,
"glXGetProcAddress", (name));
}

void *
override_GL_glXGetProcAddressARB(const char *name)
{
void *symbol;

symbol = wrapped_dlsym("GL", name);
if (symbol)
return symbol;

return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddressARB,
"glXGetProcAddressARB", (name));
}

void *
override_EGL_eglGetProcAddress(const char *name)
{
void *symbol;

if (!STRNCMP_LITERAL(name, "gl")) {
symbol = wrapped_dlsym("GLES2", name);
if (symbol)
return symbol;
}

if (!STRNCMP_LITERAL(name, "egl")) {
symbol = wrapped_dlsym("EGL", name);
if (symbol)
return symbol;
}

return DEFER_TO_GL("libEGL.so.1", override_EGL_eglGetProcAddress,
"eglGetProcAddress", (name));
}
10 changes: 10 additions & 0 deletions test/dlwrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,15 @@ dlwrap_dlopen_libfips(void);
void *
dlwrap_real_dlsym(void *handle, const char *symbol);

#define DEFER_TO_GL(library, func, name, args) \
({ \
void *lib = dlwrap_real_dlopen(library, RTLD_LAZY | RTLD_LOCAL); \
typeof(&func) real_func = dlwrap_real_dlsym(lib, name); \
/* gcc extension -- func's return value is the return value of \
* the statement. \
*/ \
real_func args; \
})

#endif

0 comments on commit 037ac7f

Please sign in to comment.