]> git.cworth.org Git - fips/commitdiff
Add wrappers for dlopen, dlsym, and glXGetProcAddressARB
authorCarl Worth <cworth@cworth.org>
Wed, 24 Apr 2013 07:58:26 +0000 (00:58 -0700)
committerCarl Worth <cworth@cworth.org>
Wed, 24 Apr 2013 07:58:26 +0000 (00:58 -0700)
This allows for many applications to start working with fips that would not
work before. Specifically, applications that dlopen libGL.so.1 instead of
directly linking with it would previously bypass fips' attempts to wrap
GL calls.

With these new wrappers carefully in place, many applications now work.

I've verified the following applications at least:

apitrace replay
NightSkyHD, a Humble Bundle game, both 32 and 64-bit versions
World of Goo, via Steam

I was also happy to notice that the Steam overlay does not cause fips
any difficulties.

Makefile.local
dlwrap.c [new file with mode: 0644]
dlwrap.h [new file with mode: 0644]
glxwrap.c
libfips.sym

index 3dd793271a02ae49775e8181d853ba534f439c46..6ee50ab1148e458af9ec731875d5e8613e093d23 100644 (file)
@@ -80,6 +80,7 @@ LIBRARY_LINK_FLAGS = -shared -Wl,--version-script=libfips.sym,--no-undefined
 extra_cflags += -I$(srcdir) -fPIC
 
 libfips_srcs = \
+       dlwrap.c \
        glxwrap.c
 
 libfips_32_modules = $(libfips_srcs:.c=-32.o)
@@ -87,10 +88,10 @@ libfips_32_modules = $(libfips_srcs:.c=-32.o)
 libfips_64_modules = $(libfips_srcs:.c=-64.o)
 
 libfips-32.so: $(libfips_32_modules) libfips.sym
-       $(call quiet,$(FINAL_FIPS_LINKER) $(CFLAGS) -m32) $(FINAL_CFLAGS) -m32 $(libfips_32_modules) $(FINAL_LIBFIPS_LDFLAGS) $(LIBRARY_LINK_FLAGS) -o $@
+       $(call quiet,$(FINAL_FIPS_LINKER) $(CFLAGS) -m32) -o $@ $(FINAL_CFLAGS) -m32 $(libfips_32_modules)  $(LIBRARY_LINK_FLAGS) $(FINAL_LIBFIPS_LDFLAGS)
 
 libfips-64.so: $(libfips_64_modules) libfips.sym
-       $(call quiet,$(FINAL_FIPS_LINKER) $(CFLAGS) -m64) $(FINAL_CFLAGS) -m64 $(libfips_64_modules) $(FINAL_LIBFIPS_LDFLAGS) $(LIBRARY_LINK_FLAGS) -o $@
+       $(call quiet,$(FINAL_FIPS_LINKER) $(CFLAGS) -m64) -o $@ $(FINAL_CFLAGS) -m64 $(libfips_64_modules) $(LIBRARY_LINK_FLAGS) $(FINAL_LIBFIPS_LDFLAGS)
 
 .PHONY: install
 install: all
diff --git a/dlwrap.c b/dlwrap.c
new file mode 100644 (file)
index 0000000..4bdb12c
--- /dev/null
+++ b/dlwrap.c
@@ -0,0 +1,179 @@
+/* Copyright © 2013, 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 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.
+ */
+
+/* dladdr is a glibc extension */
+#define _GNU_SOURCE
+#include <dlfcn.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+
+#include "dlwrap.h"
+
+#define STRNCMP_LITERAL(var, literal) \
+    strncmp ((var), (literal), sizeof (literal) - 1)
+
+void *libfips_handle;
+
+typedef void * (* fips_dlopen_t)(const char * filename, int flag);
+typedef void * (* fips_dlsym_t)(void *handle, const char *symbol);
+
+/* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking
+ * against it directly, which means they would not be seeing our
+ * wrapped GL symbols via LD_PRELOAD. So we catch the dlopen in a
+ * wrapper here and redirect it to our library.
+ */
+void *
+dlopen (const char *filename, int flag)
+{
+       Dl_info info;
+
+       /* Not libGL, so just return real dlopen */
+       if (STRNCMP_LITERAL (filename, "libGL.so"))
+               return dlwrap_real_dlopen (filename, flag);
+
+       /* Redirect all dlopens to libGL to our own wrapper library.
+        * We find our own filename by looking up this very function
+        * (that is, this "dlopen"), with dladdr).*/
+       if (dladdr (dlopen, &info)) {
+               libfips_handle = dlwrap_real_dlopen (info.dli_fname, flag);
+               return libfips_handle;
+       } else {
+               fprintf (stderr, "Error: Failed to redirect dlopen of %s:\n",
+                        filename);
+               exit (1);
+       }
+}
+
+void *
+dlwrap_real_dlopen (const char *filename, int flag)
+{
+       fips_dlopen_t real_dlopen = NULL;
+
+       if (! real_dlopen) {
+               real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym (RTLD_NEXT, "dlopen");
+               if (! real_dlopen) {
+                       fprintf (stderr, "Error: Failed to find symbol for dlopen.\n");
+                       exit (1);
+               }
+       }
+
+       return real_dlopen (filename, flag);
+}
+
+/* Since we redirect a dlopen of libGL.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 lazy to implement
+ * wrappers for function that would simply pass-through, so instead we
+ * also wrap dlysm and arrange for it to pass things through with
+ * RTLD_next if libfips does not have the function desired.
+*/
+void *
+dlsym (void *handle, const char *name)
+{
+       static void *libgl_handle = NULL;
+       static void *symbol;
+
+       /* All gl symbols are preferentially looked up in libfips. */
+       if (STRNCMP_LITERAL (name, "gl") == 0) {
+               symbol = dlwrap_real_dlsym (libfips_handle, name);
+               if (symbol)
+                       return symbol;
+       }
+
+       /* Failing that, anything specifically requested from the
+        * libfips library should be redirected to a real GL
+        * library. */
+       if (handle == libfips_handle) {
+               if (! libgl_handle)
+                       libgl_handle = dlwrap_real_dlopen ("libGL.so.1", RTLD_LAZY);
+               return dlwrap_real_dlsym (libgl_handle, name);
+       }
+
+       /* And anything else is some unrelated dlsym. Just pass it through. */
+       return dlwrap_real_dlsym (handle, name);
+}
+
+extern void *__dlsym (void *handle, const char *name);
+void *
+dlwrap_real_dlsym (void *handle, const char *name)
+{
+       static fips_dlsym_t real_dlsym = NULL;
+
+       if (! real_dlsym) {
+               /* FIXME: This brute-force, hard-coded searching for a versioned
+                * symbol is really ugly. The only reason I'm doing this is because
+                * I need some way to lookup the "dlsym" function in libdl, but
+                * I can't use 'dlsym' to do it. So dlvsym works, but forces me
+                * to guess what the right version is.
+                *
+                * Potential fixes here:
+                *
+                *   1. Use libelf to actually inspect libdl.so and
+                *      find the right version, (finding the right
+                *      libdl.so can be made easier with
+                *      dl_iterate_phdr).
+                *
+                *   2. Use libelf to find the offset of the 'dlsym'
+                *      symbol within libdl.so, (and then add this to
+                *      the base address at which libdl.so is loaded
+                *      as reported by dl_iterate_phdr).
+                *
+                * In the meantime, I'll just keep augmenting this
+                * hard-coded version list as people report bugs. */
+               const char *version[] = {
+                       "GLIBC_2.2.5",
+                       "GLIBC_2.0"
+               };
+               int num_versions = sizeof(version) / sizeof(version[0]);
+               int i;
+               for (i = 0; i < num_versions; i++) {
+                       real_dlsym = (fips_dlsym_t) dlvsym (RTLD_NEXT, "dlsym",
+                                                           version[i]);
+                       if (real_dlsym)
+                               break;
+               }
+               if (i == num_versions) {
+                       fprintf (stderr, "Internal error: Failed to find real dlsym\n");
+                       fprintf (stderr,
+"This may be a simple matter of fips not knowing about the version of GLIBC that\n"
+"your program is using. Current known versions are:\n\n\t");
+                       for (i = 0; i < num_versions; i++)
+                               fprintf (stderr, "%s ", version[i]);
+                       fprintf(stderr,
+"\n\nYou can inspect your version by first finding libdl.so.2:\n"
+"\n"
+"\tldd <your-program> | grep libdl.so\n"
+"\n"
+"And then inspecting the version attached to the dlsym symbol:\n"
+"\n"
+"\treadelf -s /path/to/libdl.so.2 | grep dlsym\n"
+"\n"
+"And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n");
+
+                       exit (1);
+               }
+       }
+
+       return real_dlsym (handle, name);
+}
diff --git a/dlwrap.h b/dlwrap.h
new file mode 100644 (file)
index 0000000..9b6b53d
--- /dev/null
+++ b/dlwrap.h
@@ -0,0 +1,42 @@
+/* Copyright © 2013, 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 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.
+ */
+
+#ifndef DLWRAP_H
+#define DLWRAP_H
+
+/* Call the *real* dlopen. We have our own wrapper for dlopen that, of
+ * necessity must use claim the symbol 'dlopen'. So whenever anything
+ * internal needs to call the real, underlying dlopen function, the
+ * thing to call is dlwrap_real_dlopen.
+ */
+void *
+dlwrap_real_dlopen (const char *filename, int flag);
+
+/* Call the *real* dlsym. We have our own wrapper for dlsym that, of
+ * necessity must use claim the symbol 'dlsym'. So whenever anything
+ * internal needs to call the real, underlying dlysm function, the
+ * thing to call is dlwrap_real_dlsym.
+ */
+void *
+dlwrap_real_dlsym (void *handle, const char *symbol);
+
+#endif
+
index 51b5faf58f83f8017c822d7d287f7eb72dbb12d0..9ebe799c70aea1f5678d0c3283ecd4162aa67d98 100644 (file)
--- a/glxwrap.c
+++ b/glxwrap.c
@@ -22,6 +22,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <string.h>
+
 #include <dlfcn.h>
 
 #include <X11/Xlib.h>
@@ -31,6 +33,8 @@
 
 #include <sys/time.h>
 
+#include "dlwrap.h"
+
 typedef void (* fips_glXSwapBuffers_t)(Display *dpy, GLXDrawable drawable);
 
 static void *
@@ -40,7 +44,7 @@ lookup (const char *name)
        static void *libgl_handle = NULL;
 
        if (! libgl_handle) {
-               libgl_handle = dlopen (libgl_filename, RTLD_NOW);
+               libgl_handle = dlwrap_real_dlopen (libgl_filename, RTLD_NOW | RTLD_DEEPBIND);
                if (! libgl_handle) {
                        fprintf (stderr, "Error: Failed to dlopen %s\n",
                                 libgl_filename);
@@ -48,7 +52,7 @@ lookup (const char *name)
                }
        }
 
-       return dlsym (libgl_handle, name);
+       return dlwrap_real_dlsym (libgl_handle, name);
 }
 
 static void
@@ -94,3 +98,26 @@ glXSwapBuffers (Display *dpy, GLXDrawable drawable)
                printf("FPS: %.3f\n", fps);
        }
 }
+
+
+typedef __GLXextFuncPtr (* fips_glXGetProcAddressARB_t)(const GLubyte *func);
+__GLXextFuncPtr
+glXGetProcAddressARB (const GLubyte *func)
+{
+       static fips_glXGetProcAddressARB_t real_glXGetProcAddressARB = NULL;
+       const char *name = "glXGetProcAddressARB";
+
+       if (! real_glXGetProcAddressARB) {
+               real_glXGetProcAddressARB = (fips_glXGetProcAddressARB_t) lookup (name);
+               if (! real_glXGetProcAddressARB) {
+                       fprintf (stderr, "Error: Failed to find function %s.\n",
+                                name);
+                       return NULL;
+               }
+       }
+
+       if (strcmp ((const char *)func, "glXSwapBuffers") == 0)
+               return (__GLXextFuncPtr) glXSwapBuffers;
+       else
+               return real_glXGetProcAddressARB (func);
+}
index 28fff8442dcde8c956fd88e4a6e6ed630d9e890b..a387ddad14495a7f65ce6d637467b436d05e5c69 100644 (file)
@@ -1,6 +1,9 @@
 {
 global:
+       dlopen;
+       dlsym;
        glXSwapBuffers;
+       glXGetProcAddressARB;
 local:
        *;
 };