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();
}
fprintf(stderr, "loading %s\n", lib_name);
#ifdef _WIN32
*handle = LoadLibraryA(lib_name);
#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_gles1_without_glx
egl_gles2_without_glx

@ -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)
@ -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 \
@ -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)

@ -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;
if (strncmp(wrapped_libs[i].filename, filename,
strlen(wrapped_libs[i].filename)) == 0) {
return &wrapped_libs[i];
}
}
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);
}
return dlwrap_real_dlopen(info.dli_fname, RTLD_NOW);
return NULL;
}
/* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking
@ -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
@ -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 *
@ -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
@ -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
@ -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));
}

@ -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,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