summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/.gitignore3
-rw-r--r--test/Makefile.am31
-rw-r--r--test/dlwrap.c209
-rw-r--r--test/dlwrap.h10
-rw-r--r--test/egl_and_glx_different_pointers.c246
5 files changed, 424 insertions, 75 deletions
diff --git a/test/.gitignore b/test/.gitignore
index cbc9d71..a33ea93 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -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
diff --git a/test/Makefile.am b/test/Makefile.am
index 7cf7d19..b23ddfa 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -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)
diff --git a/test/dlwrap.c b/test/dlwrap.c
index 3af6ceb..7b28c37 100644
--- a/test/dlwrap.c
+++ b/test/dlwrap.c
@@ -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
@@ -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));
+}
diff --git a/test/dlwrap.h b/test/dlwrap.h
index 7e943e0..39ec9ec 100644
--- a/test/dlwrap.h
+++ b/test/dlwrap.h
@@ -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
diff --git a/test/egl_and_glx_different_pointers.c b/test/egl_and_glx_different_pointers.c
new file mode 100644
index 0000000..2a2ff3c
--- /dev/null
+++ b/test/egl_and_glx_different_pointers.c
@@ -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;
+}