Add a test for EGL and GLX returning different function pointers.

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.
macos/v1.5.9
Eric Anholt 11 years ago
parent dae231680e
commit 037ac7f566
  1. 2
      src/dispatch_common.c
  2. 3
      test/.gitignore
  3. 31
      test/Makefile.am
  4. 207
      test/dlwrap.c
  5. 10
      test/dlwrap.h
  6. 246
      test/egl_and_glx_different_pointers.c

@ -179,6 +179,8 @@ get_dlopen_handle(void **handle, const char *lib_name, bool exit_on_fail)
abort(); abort();
} }
fprintf(stderr, "loading %s\n", lib_name);
#ifdef _WIN32 #ifdef _WIN32
*handle = LoadLibraryA(lib_name); *handle = LoadLibraryA(lib_name);
#else #else

3
test/.gitignore vendored

@ -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_has_extension_nocontext
egl_gles1_without_glx egl_gles1_without_glx
egl_gles2_without_glx egl_gles2_without_glx

@ -53,13 +53,19 @@ AM_CFLAGS = $(CWARNFLAGS)
TESTS = \ TESTS = \
$(EGL_TESTS) \ $(EGL_TESTS) \
$(GLX_TESTS) \ $(GLX_TESTS) \
$(EGL_AND_GLX_TESTS) \
$(WGL_TESTS) \ $(WGL_TESTS) \
headerguards$(EXEEXT) \ headerguards$(EXEEXT) \
miscdefines$(EXEEXT) \ miscdefines$(EXEEXT) \
khronos_typedefs$(EXEEXT) \ khronos_typedefs$(EXEEXT) \
$() $()
check_BINARIES = $(EGL_AND_GLX_BIN)
XFAIL_TESTS = \ XFAIL_TESTS = \
egl_and_glx_different_pointers_egl_glx \
egl_and_glx_different_pointers_egl \
egl_and_glx_different_pointers_glx \
$() $()
check_PROGRAMS = $(TESTS) check_PROGRAMS = $(TESTS)
@ -79,6 +85,16 @@ if HAS_ZNOW
GLX_SHARED_ZNOW = glx_shared_znow GLX_SHARED_ZNOW = glx_shared_znow
endif 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_TESTS = \
glx_beginend \ glx_beginend \
glx_public_api \ glx_public_api \
@ -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_SOURCES = egl_without_glx.c
egl_gles2_without_glx_LDADD = $(EPOXY) $(DLOPEN_LIBS) libegl_common.la $(X11_LIBS) 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_beginend_LDADD = $(EPOXY) libglx_common.la $(GL_LIBS) $(X11_LIBS)
glx_public_api_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS) glx_public_api_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS)

@ -19,77 +19,74 @@
* THE SOFTWARE. * 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 */ /* dladdr is a glibc extension */
#define _GNU_SOURCE #define _GNU_SOURCE
#include <dlfcn.h> #include <dlfcn.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h> #include <assert.h>
#include "fips.h"
#include "dlwrap.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; void *libfips_handle;
typedef void *(*fips_dlopen_t)(const char *filename, int flag); typedef void *(*fips_dlopen_t)(const char *filename, int flag);
typedef void *(*fips_dlsym_t)(void *handle, const char *symbol); typedef void *(*fips_dlsym_t)(void *handle, const char *symbol);
static const char *wrapped_libs[] = { void *override_EGL_eglGetProcAddress(const char *name);
"libGL.so", void *override_GL_glXGetProcAddress(const char *name);
"libEGL.so", void *override_GL_glXGetProcAddressARB(const char *name);
"libGLESv2.so" 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 /* Match 'filename' against an internal list of libraries for which
* libfips has wrappers. * libfips has wrappers.
* *
* Returns true and sets *index_ret if a match is found. * Returns true and sets *index_ret if a match is found.
* Returns false if no match is found. */ * Returns false if no match is found. */
static bool static struct libwrap *
find_wrapped_library_index(const char *filename, unsigned *index_ret) find_wrapped_library(const char *filename)
{ {
unsigned i; unsigned i;
for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) { for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) {
if (strncmp(wrapped_libs[i], filename, if (strncmp(wrapped_libs[i].filename, filename,
strlen(wrapped_libs[i])) == 0) strlen(wrapped_libs[i].filename)) == 0) {
{ return &wrapped_libs[i];
*index_ret = i;
return true;
} }
} }
return false; return NULL;
}
/* 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);
}
return dlwrap_real_dlopen(info.dli_fname, RTLD_NOW);
} }
/* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking /* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking
@ -101,7 +98,7 @@ void *
dlopen(const char *filename, int flag) dlopen(const char *filename, int flag)
{ {
void *ret; void *ret;
unsigned index; struct libwrap *wrap;
/* Before deciding whether to redirect this dlopen to our own /* Before deciding whether to redirect this dlopen to our own
* library, we call the real dlopen. This assures that any * library, we call the real dlopen. This assures that any
@ -111,27 +108,29 @@ dlopen(const char *filename, int flag)
ret = dlwrap_real_dlopen(filename, flag); ret = dlwrap_real_dlopen(filename, flag);
/* If filename is not a wrapped library, just return real dlopen */ /* 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; return ret;
/* When the application dlopens any wrapped library starting wrap->handle = ret;
* 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);
assert(index < ARRAY_SIZE(orig_handles)); /* We use wrapped_libs as our handles to libraries. */
orig_handles[index] = ret; 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 if (wrap < wrapped_libs ||
* future calls to dlsym. We encode the index in the return value wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) {
* so that we can later map back to the originally requested void (*real_dlclose)(void *handle) = dlwrap_real_dlsym(RTLD_NEXT, "__dlclose");
* dlopen-handle if necessary. */ real_dlclose(handle);
return libfips_handle + index; }
} }
void * void *
@ -150,6 +149,22 @@ dlwrap_real_dlopen(const char *filename, int flag)
return real_dlopen(filename, 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 /* 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 * need to ensure that dlysm succeeds for all functions that might be
* defined in the real, underlying libGL library. But we're far too * defined in the real, underlying libGL library. But we're far too
@ -160,27 +175,24 @@ dlwrap_real_dlopen(const char *filename, int flag)
void * void *
dlsym(void *handle, const char *name) dlsym(void *handle, const char *name)
{ {
static void *symbol; struct libwrap *wrap = handle;
unsigned index;
/* All gl* and egl* symbols are preferentially looked up in libfips. */ /* Make sure that handle is actually one of our wrapped libs. */
if (STRNCMP_LITERAL(name, "gl") == 0 || STRNCMP_LITERAL(name, "egl") == 0) { if (wrap < wrapped_libs ||
symbol = dlwrap_real_dlsym(libfips_handle, name); wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) {
if (symbol) wrap = NULL;
return symbol;
} }
/* Failing that, anything specifically requested from the /* Failing that, anything specifically requested from the
* libfips library should be redirected to a real GL * libfips library should be redirected to a real GL
* library. */ * library. */
/* We subtract the index back out of the handle (see the addition if (wrap) {
* of the index in our wrapper for dlopen above) to then use the void *symbol = wrapped_dlsym(wrap->symbol_prefix, name);
* correct, original dlopen'ed handle for the library of if (symbol)
* interest. */ return symbol;
index = handle - libfips_handle; else
if (index < ARRAY_SIZE(orig_handles)) { return dlwrap_real_dlsym(wrap->handle, name);
return dlwrap_real_dlsym(orig_handles[index], name);
} }
/* And anything else is some unrelated dlsym. Just pass it /* And anything else is some unrelated dlsym. Just pass it
@ -251,3 +263,50 @@ dlwrap_real_dlsym(void *handle, const char *name)
return real_dlsym(handle, 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));
}

@ -53,5 +53,15 @@ dlwrap_dlopen_libfips(void);
void * void *
dlwrap_real_dlsym(void *handle, const char *symbol); 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 #endif

@ -0,0 +1,246 @@
/*
* Copyright © 2014 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/**
* @file egl_and_glx_different_pointers.c
*
* Tests that epoxy correctly handles an EGL and GLX implementation
* that return different function pointers between the two.
*
* This is the case for EGL and GLX on nvidia binary drivers
* currently, but is also the case if someone has nvidia binary GLX
* installed but still has Mesa (software) EGL installed. This seems
* common enough that we should make sure things work.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <err.h>
#include <dlfcn.h>
#include "epoxy/gl.h"
#include "epoxy/egl.h"
#include "epoxy/glx.h"
#include "egl_common.h"
#include "glx_common.h"
#include "dlwrap.h"
#define GLX_FAKED_VENDOR_STRING "libepoxy override GLX"
#define EGL_FAKED_VENDOR_STRING "libepoxy override EGL"
#define GL_CREATESHADER_VALUE 1234
#define GLES2_CREATESHADER_VALUE 5678
const char *override_GLES2_glGetString(GLenum e);
const char *override_GL_glGetString(GLenum e);
GLuint override_GLES2_glCreateShader(GLenum e);
GLuint override_GL_glCreateShader(GLenum e);
const char *
override_GL_glGetString(GLenum e)
{
if (e == GL_VENDOR)
return GLX_FAKED_VENDOR_STRING;
return DEFER_TO_GL("libGL.so.1", override_GL_glGetString,
"glGetString", (e));
}
const char *
override_GLES2_glGetString(GLenum e)
{
if (e == GL_VENDOR)
return EGL_FAKED_VENDOR_STRING;
return DEFER_TO_GL("libGLESv2.so.2", override_GLES2_glGetString,
"glGetString", (e));
}
GLuint
override_GL_glCreateShader(GLenum type)
{
return GL_CREATESHADER_VALUE;
}
GLuint
override_GLES2_glCreateShader(GLenum type)
{
return GLES2_CREATESHADER_VALUE;
}
#ifdef USE_GLX
static bool
make_glx_current_and_test(Display *dpy, GLXContext ctx, Drawable draw)
{
const char *string;
GLuint shader;
bool pass = true;
glXMakeCurrent(dpy, draw, ctx);
if (!epoxy_is_desktop_gl()) {
fprintf(stderr, "Claimed to be ES\n");
pass = false;
}
string = (const char *)glGetString(GL_VENDOR);
printf("GLX vendor: %s\n", string);
shader = glCreateShader(GL_FRAGMENT_SHADER);
if (shader != GL_CREATESHADER_VALUE) {
fprintf(stderr, "glCreateShader() returned %d instead of %d\n",
shader, GL_CREATESHADER_VALUE);
pass = false;
}
pass = pass && !strcmp(string, GLX_FAKED_VENDOR_STRING);
return pass;
}
static void
init_glx(Display **out_dpy, GLXContext *out_ctx, Drawable *out_draw)
{
Display *dpy = get_display_or_skip();
make_glx_context_current_or_skip(dpy);
*out_dpy = dpy;
*out_ctx = glXGetCurrentContext();
*out_draw= glXGetCurrentDrawable();
}
#endif /* USE_GLX */
#ifdef USE_EGL
static bool
make_egl_current_and_test(EGLDisplay *dpy, EGLContext ctx)
{
const char *string;
GLuint shader;
bool pass = true;
eglMakeCurrent(dpy, NULL, NULL, ctx);
if (epoxy_is_desktop_gl()) {
fprintf(stderr, "Claimed to be desktop\n");
pass = false;
}
if (epoxy_gl_version() < 20) {
fprintf(stderr, "Claimed to be GL version %d\n",
epoxy_gl_version());
pass = false;
}
shader = glCreateShader(GL_FRAGMENT_SHADER);
if (shader != GLES2_CREATESHADER_VALUE) {
fprintf(stderr, "glCreateShader() returned %d instead of %d\n",
shader, GLES2_CREATESHADER_VALUE);
pass = false;
}
string = (const char *)glGetString(GL_VENDOR);
printf("EGL vendor: %s\n", string);
pass = pass && !strcmp(string, EGL_FAKED_VENDOR_STRING);
return pass;
}
static void
init_egl(EGLDisplay **out_dpy, EGLContext *out_ctx)
{
EGLDisplay *dpy = get_egl_display_or_skip();
static const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_NONE
};
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLContext ctx;
EGLConfig cfg;
EGLint count;
if (!epoxy_has_egl_extension(dpy, "EGL_KHR_surfaceless_context"))
errx(77, "Test requires EGL_KHR_surfaceless_context");
eglBindAPI(EGL_OPENGL_ES_API);
if (!eglChooseConfig(dpy, config_attribs, &cfg, 1, &count))
errx(77, "Couldn't get an EGLConfig\n");
ctx = eglCreateContext(dpy, cfg, NULL, context_attribs);
if (!ctx)
errx(77, "Couldn't create a GLES2 context\n");
*out_dpy = dpy;
*out_ctx = ctx;
}
#endif /* USE_EGL */
int
main(int argc, char **argv)
{
bool pass = true;
#ifdef USE_EGL
EGLDisplay *egl_dpy;
EGLContext egl_ctx;
#endif
#ifdef USE_GLX
Display *glx_dpy;
GLXContext glx_ctx;
Drawable glx_draw;
#endif
/* Force epoxy to have loaded both EGL and GLX libs already -- we
* can't assume anything about symbol resolution based on having
* EGL or GLX loaded.
*/
(void)glXGetCurrentContext();
(void)eglGetCurrentContext();
#ifdef USE_GLX
init_glx(&glx_dpy, &glx_ctx, &glx_draw);
pass = make_glx_current_and_test(glx_dpy, glx_ctx, glx_draw) && pass;
#endif
#ifdef USE_EGL
init_egl(&egl_dpy, &egl_ctx);
pass = make_egl_current_and_test(egl_dpy, egl_ctx) && pass;
#endif
#if defined(USE_GLX) && defined(USE_EGL)
pass = make_glx_current_and_test(glx_dpy, glx_ctx, glx_draw) && pass;
pass = make_egl_current_and_test(egl_dpy, egl_ctx) && pass;
#endif
return pass != true;
}
Loading…
Cancel
Save