summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Fonseca <jfonseca@vmware.com>2015-11-17 00:20:17 +0000
committerJose Fonseca <jfonseca@vmware.com>2015-11-22 22:53:20 +0000
commitf16f95c147ed21de11345f8b3ccf9a3615c9d8c3 (patch)
treeeff473af01ace3c141dce464a8b0c50352eb443b
parent1366e67590014b395ff28cc875a0a3434db284ea (diff)
wrappers: Intercept dlsym where possible (WIP).dlsym
-rw-r--r--dispatch/dlopen.hpp38
-rw-r--r--dispatch/glproc_egl.cpp8
-rw-r--r--dispatch/glproc_gl.cpp4
-rwxr-xr-xretrace/glretrace_main.cpp13
-rw-r--r--wrappers/CMakeLists.txt4
-rw-r--r--wrappers/dlsym.cpp265
-rw-r--r--wrappers/egltrace.py63
-rw-r--r--wrappers/egltrace.version1
-rw-r--r--wrappers/glxtrace.py50
-rw-r--r--wrappers/glxtrace.version1
10 files changed, 267 insertions, 180 deletions
diff --git a/dispatch/dlopen.hpp b/dispatch/dlopen.hpp
deleted file mode 100644
index 94d9f62c..00000000
--- a/dispatch/dlopen.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/**************************************************************************
- *
- * Copyright 2011 Jose Fonseca
- * All Rights Reserved.
- *
- * 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.
- *
- **************************************************************************/
-
-/*
- * Provides access to real dlopen, as tracing libraries interpose it.
- */
-
-#pragma once
-
-#include <dlfcn.h>
-
-/*
- * Invoke the true dlopen() function.
- */
-void *
-_dlopen(const char *filename, int flag);
diff --git a/dispatch/glproc_egl.cpp b/dispatch/glproc_egl.cpp
index 739a72c9..0f0683f2 100644
--- a/dispatch/glproc_egl.cpp
+++ b/dispatch/glproc_egl.cpp
@@ -29,7 +29,7 @@
#include <string.h>
#if !defined(_WIN32)
-#include "dlopen.hpp"
+#include <dlfcn.h>
#endif
@@ -90,7 +90,7 @@ _getPublicProcAddress(const char *procName)
if (procName[0] == 'e' && procName[1] == 'g' && procName[2] == 'l') {
static void *libEGL = NULL;
if (!libEGL) {
- libEGL = _dlopen("libEGL.so", RTLD_LOCAL | RTLD_LAZY);
+ libEGL = dlopen("libEGL.so", RTLD_LOCAL | RTLD_LAZY);
if (!libEGL) {
return NULL;
}
@@ -133,7 +133,7 @@ _getPublicProcAddress(const char *procName)
static void *libGLESv2 = NULL;
if (!libGLESv2) {
- libGLESv2 = _dlopen("libGLESv2.so", RTLD_LOCAL | RTLD_LAZY);
+ libGLESv2 = dlopen("libGLESv2.so", RTLD_LOCAL | RTLD_LAZY);
}
if (libGLESv2) {
proc = dlsym(libGLESv2, procName);
@@ -144,7 +144,7 @@ _getPublicProcAddress(const char *procName)
static void *libGLESv1 = NULL;
if (!libGLESv1) {
- libGLESv1 = _dlopen("libGLESv1_CM.so", RTLD_LOCAL | RTLD_LAZY);
+ libGLESv1 = dlopen("libGLESv1_CM.so", RTLD_LOCAL | RTLD_LAZY);
}
if (libGLESv1) {
proc = dlsym(libGLESv1, procName);
diff --git a/dispatch/glproc_gl.cpp b/dispatch/glproc_gl.cpp
index 8ae40d42..ce3ee67c 100644
--- a/dispatch/glproc_gl.cpp
+++ b/dispatch/glproc_gl.cpp
@@ -30,7 +30,7 @@
#if !defined(_WIN32)
#include <unistd.h> // for symlink
-#include "dlopen.hpp"
+#include <dlfcn.h>
#endif
@@ -197,7 +197,7 @@ void * _libgl_sym(const char *symbol)
* exposes symbols to it.
*/
- _libGlHandle = _dlopen(libgl_filename, RTLD_GLOBAL | RTLD_LAZY);
+ _libGlHandle = dlopen(libgl_filename, RTLD_GLOBAL | RTLD_LAZY);
if (!_libGlHandle) {
os::log("apitrace: error: couldn't find libGL.so\n");
return NULL;
diff --git a/retrace/glretrace_main.cpp b/retrace/glretrace_main.cpp
index 8527126a..525822ad 100755
--- a/retrace/glretrace_main.cpp
+++ b/retrace/glretrace_main.cpp
@@ -689,16 +689,3 @@ void
retrace::cleanUp(void) {
glws::cleanup();
}
-
-
-#ifndef _WIN32
-
-#include "dlopen.hpp"
-
-void *
-_dlopen(const char *filename, int flag)
-{
- return dlopen(filename, flag);
-}
-
-#endif
diff --git a/wrappers/CMakeLists.txt b/wrappers/CMakeLists.txt
index 2ceac332..27dc7eb1 100644
--- a/wrappers/CMakeLists.txt
+++ b/wrappers/CMakeLists.txt
@@ -351,7 +351,7 @@ elseif (X11_FOUND)
# Prevent symbol relocations internal to our wrapper library to be
# overwritten by the application. And fail if there are missing
# symbols.
- LINK_FLAGS "-Wl,-Bsymbolic -Wl,-Bsymbolic-functions -Wl,-z,defs"
+ LINK_FLAGS "-Wl,-Bsymbolic -Wl,-Bsymbolic-functions -Wl,-z,defs -Wl,-defsym=_begin=0"
)
target_link_libraries (glxtrace
@@ -402,7 +402,7 @@ if (ENABLE_EGL AND NOT WIN32 AND NOT APPLE)
# Prevent symbol relocations internal to our wrapper library to be
# overwritten by the application. And fail if there are missing
# symbols.
- LINK_FLAGS "-Wl,-Bsymbolic -Wl,-Bsymbolic-functions -Wl,-z,defs"
+ LINK_FLAGS "-Wl,-Bsymbolic -Wl,-Bsymbolic-functions -Wl,-z,defs -Wl,-defsym=_begin=0"
)
target_link_libraries (egltrace
diff --git a/wrappers/dlsym.cpp b/wrappers/dlsym.cpp
index 24d3a087..8a22c989 100644
--- a/wrappers/dlsym.cpp
+++ b/wrappers/dlsym.cpp
@@ -26,24 +26,171 @@
/*
- * Provides access to real dlopen, as tracing libraries interpose it.
+ * Intercept dlsym or dlopen.
*/
#include "os.hpp"
-#include "dlopen.hpp"
-#ifdef __GLIBC__
-
+#if !defined(_WIN32) && !defined(__APPLE__)
#include <dlfcn.h>
+#include <execinfo.h>
+#if defined(__GLIBC__)
extern "C" void * __libc_dlopen_mode(const char * filename, int flag);
extern "C" void * __libc_dlsym(void * handle, const char * symbol);
+#endif
+
+
+static const int verbosity = 2;
+
+
+extern "C" char _begin[]; // obtained via -Wl,-defsym=_begin=0
+extern "C" char _etext[]; // builtin
+
+
+/*
+ * Whether the given address belongs to this shared object or not.
+ */
+static inline bool
+isInternalAddress(void *addr)
+{
+ return _begin <= addr && addr < _etext;
+}
+
+
+
+#define DLSYM_INTERCEPTION 1
+
+
+#if DLSYM_INTERCEPTION && defined(__GLIBC__)
+
+#include <link.h> // for link_map
+
+
+static void * self_handle = NULL;
+
+
+// Try to get GCC to do tail-call optimization
+// XXX: Unfortunately not effective with calls to function pointers
+#pragma GCC optimize ("omit-frame-pointer")
+#pragma GCC optimize ("optimize-sibling-calls")
+
+
+/*
+ * Intercept dlsym.
+ *
+ * Intercepting dlopen has several disadvantages:
+ *
+ */
+PUBLIC
+void *
+dlsym(void * handle, const char * symbol)
+{
+ void * returnAddr = __builtin_extract_return_addr(__builtin_return_address(0));
+
+ bool internal = isInternalAddress(returnAddr);
+
+ /*
+ * We rely on glibc's internal __libc_dlsym. See also
+ * http://www.linuxforu.com/2011/08/lets-hook-a-library-function/
+ *
+ * Use use it to obtain the true dlsym. We don't use __libc_dlsym directly
+ * because it does not support things such as RTLD_NEXT.
+ */
+ typedef void * (*PFN_DLSYM)(void *, const char *);
+ static PFN_DLSYM dlsym_ptr = NULL;
+ static void *libdl_handle;
+ if (!dlsym_ptr) {
+ libdl_handle = __libc_dlopen_mode("libdl.so.2", RTLD_LOCAL | RTLD_NOW);
+ if (libdl_handle) {
+ dlsym_ptr = (PFN_DLSYM)__libc_dlsym(libdl_handle, "dlsym");
+ }
+ if (!dlsym_ptr) {
+ os::log("apitrace: error: failed to look up real dlsym\n");
+ os::abort();
+ }
+ }
+
+ const char *s = symbol;
+ bool intercept = (s[0] == 'g' && s[1] == 'l' && (s[2] >= 'A' && s[2] <= 'Z')) ||
+ (s[0] == 'e' && s[1] == 'g' && s[2] == 'l' && (s[3] >= 'A' && s[3] <= 'Z'));
+
+ void *addr = NULL;
+ if (!internal) {
+
+ if (intercept) {
+ // Get our own handle
+ if (!self_handle) {
+ static const int dummy = 0xdeadbeef;
+ Dl_info info;
+ if (!dladdr(&dummy, &info)) {
+ os::log("apitrace: error: dladdr() failed\n");
+ os::abort();
+ }
+ self_handle = __libc_dlopen_mode(info.dli_fname, RTLD_LAZY | RTLD_NOLOAD);
+ if (!self_handle) {
+ os::log("apitrace: error: dlopen(\"%s\") failed\n", info.dli_fname);
+ os::abort();
+ }
+ }
+
+ addr = __libc_dlsym(self_handle, symbol);
+ }
+
+ if (verbosity > 1 ||
+ (verbosity == 1 && addr)) {
+ const char *filename = NULL;
+ const char *quote = "";
+ if (handle == RTLD_DEFAULT) {
+ filename = "RTLD_DEFAULT";
+ } else if (handle == RTLD_NEXT) {
+ filename = "RTLD_NEXT";
+ } else if (handle == libdl_handle) {
+ filename = "libdl.so.2";
+ } else {
+ struct link_map *map;
+ if (dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0) {
+ filename = map->l_name;
+ if (filename && filename[0] == '\0') {
+ filename = NULL;
+ } else {
+ quote = "\"";
+ }
+ }
+ }
+ const char *verb = addr ? "intercepting" : "ignoring";
+ if (filename) {
+ os::log("apitrace: %s dlsym(%s%s%s, \"%s\")\n", verb, quote, filename, quote, symbol);
+ } else {
+ os::log("apitrace: %s dlsym(%p, \"%s\")\n", verb, handle, symbol);
+ }
+ }
+
+ if (addr) {
+ return addr;
+ }
+ }
+
+ addr = dlsym_ptr(handle, symbol);
+
+ if (addr && isInternalAddress(addr)) {
+ os::log("apitrace: error: infinite recursing looking up \"%s\"\n", symbol);
+ os::abort();
+ }
+
+ return addr;
+}
+#else // !DLSYM_INTERCEPTION
+
+
+#ifdef __GLIBC__
+
/*
* Protect against dlsym interception.
*
@@ -58,6 +205,7 @@ PRIVATE
void *
dlsym(void * handle, const char * symbol)
{
+ os::log("apitrace: info: _dlsym(..., \"%s\")\n", symbol);
/*
* We rely on glibc's internal __libc_dlsym. See also
* http://www.linuxforu.com/2011/08/lets-hook-a-library-function/
@@ -78,18 +226,27 @@ dlsym(void * handle, const char * symbol)
}
}
- return dlsym_ptr(handle, symbol);
-}
+ void *addr = dlsym_ptr(handle, symbol);
+ if (addr && isInternalAddress(addr)) {
+ os::log("apitrace: error: infinite recursing looking up \"%s\"\n", symbol);
+ os::abort();
+ }
+
+ return addr;
+}
#endif /* __GLIBC__ */
+extern void * _libGlHandle;
+
+
/*
* Invoke the true dlopen() function.
*/
-void *
-_dlopen(const char *filename, int flag)
+static void *
+real_dlopen(const char *filename, int flag)
{
typedef void * (*PFN_DLOPEN)(const char *, int);
static PFN_DLOPEN dlopen_ptr = NULL;
@@ -115,3 +272,95 @@ _dlopen(const char *filename, int flag)
return dlopen_ptr(filename, flag);
}
+
+
+/*
+ * Several applications, such as Quake3, use dlopen("libGL.so.1"), but
+ * LD_PRELOAD does not intercept symbols obtained via dlopen/dlsym, therefore
+ * we need to intercept the dlopen() call here, and redirect to our wrapper
+ * shared object.
+ */
+static void *
+fake_dlopen(const char *filename, int flag)
+{
+ bool intercept = false;
+
+ os::log("apitrace: info: dlopen(\"%s\", 0x%x)\n", filename, flag);
+
+ if (filename) {
+ intercept =
+ strcmp(filename, "libEGL.so") == 0 ||
+ strcmp(filename, "libEGL.so.1") == 0 ||
+ strcmp(filename, "libGLESv1_CM.so") == 0 ||
+ strcmp(filename, "libGLESv1_CM.so.1") == 0 ||
+ strcmp(filename, "libGLESv2.so") == 0 ||
+ strcmp(filename, "libGLESv2.so.2") == 0 ||
+ strcmp(filename, "libGL.so") == 0 ||
+ strcmp(filename, "libGL.so.1") == 0;
+
+ if (intercept) {
+ os::log("apitrace: redirecting dlopen(\"%s\", 0x%x)\n", filename, flag);
+
+ /* The current dispatch implementation relies on core entry-points
+ * to be globally available, so force this.
+ *
+ * TODO: A better approach would be note down the entry points here
+ * and use them latter. Another alternative would be to reopen the
+ * library with RTLD_NOLOAD | RTLD_GLOBAL.
+ */
+ flag &= ~RTLD_LOCAL;
+ flag |= RTLD_GLOBAL;
+ }
+ }
+
+ void *handle = real_dlopen(filename, flag);
+
+ if (intercept) {
+
+ // FIXME: handle absolute paths and other versions
+ if (strcmp(filename, "libGL.so") == 0 ||
+ strcmp(filename, "libGL.so.1") == 0) {
+
+ // Use the true libGL.so handle instead of RTLD_NEXT from now on
+ _libGlHandle = handle;
+ }
+
+ // Get the file path for our shared object, and use it instead
+ static const int dummy = 0xdeadbeef;
+ Dl_info info;
+ if (dladdr(&dummy, &info)) {
+ handle = real_dlopen(info.dli_fname, flag);
+ } else {
+ os::log("apitrace: warning: dladdr() failed\n");
+ }
+
+ // SDL will skip dlopen'ing libEGL.so after it spots EGL symbols on our
+ // wrapper, so force loading it here.
+ // (https://github.com/apitrace/apitrace/issues/291#issuecomment-59734022)
+ if (strcmp(filename, "libEGL.so") != 0 &&
+ strcmp(filename, "libEGL.so.1") != 0) {
+ real_dlopen("libEGL.so.1", RTLD_GLOBAL | RTLD_LAZY);
+ }
+ }
+
+ return handle;
+}
+
+
+extern "C" PUBLIC
+void *
+dlopen(const char *filename, int flag)
+{
+ void * returnAddr = __builtin_return_address(0);
+ bool internal = isInternalAddress(returnAddr);
+ if (internal) {
+ return real_dlopen(filename, flag);
+ } else {
+ return fake_dlopen(filename, flag);
+ }
+}
+
+
+#endif // !DLSYM_INTERCEPTION
+
+#endif // !defined(_WIN32) && !defined(__APPLE__)
diff --git a/wrappers/egltrace.py b/wrappers/egltrace.py
index 7d2d182c..d9c597c9 100644
--- a/wrappers/egltrace.py
+++ b/wrappers/egltrace.py
@@ -113,7 +113,6 @@ if __name__ == '__main__':
print '#define GL_GLEXT_PROTOTYPES'
print '#define EGL_EGLEXT_PROTOTYPES'
print
- print '#include "dlopen.hpp"'
print '#include "glproc.hpp"'
print '#include "glsize.hpp"'
print '#include "eglsize.hpp"'
@@ -130,68 +129,6 @@ if __name__ == '__main__':
print r'''
-
-/*
- * Several applications, such as Quake3, use dlopen("libGL.so.1"), but
- * LD_PRELOAD does not intercept symbols obtained via dlopen/dlsym, therefore
- * we need to intercept the dlopen() call here, and redirect to our wrapper
- * shared object.
- */
-extern "C" PUBLIC
-void * dlopen(const char *filename, int flag)
-{
- bool intercept = false;
-
- if (filename) {
- intercept =
- strcmp(filename, "libEGL.so") == 0 ||
- strcmp(filename, "libEGL.so.1") == 0 ||
- strcmp(filename, "libGLESv1_CM.so") == 0 ||
- strcmp(filename, "libGLESv1_CM.so.1") == 0 ||
- strcmp(filename, "libGLESv2.so") == 0 ||
- strcmp(filename, "libGLESv2.so.2") == 0 ||
- strcmp(filename, "libGL.so") == 0 ||
- strcmp(filename, "libGL.so.1") == 0;
-
- if (intercept) {
- os::log("apitrace: redirecting dlopen(\"%s\", 0x%x)\n", filename, flag);
-
- /* The current dispatch implementation relies on core entry-points to be globally available, so force this.
- *
- * TODO: A better approach would be note down the entry points here and
- * use them latter. Another alternative would be to reopen the library
- * with RTLD_NOLOAD | RTLD_GLOBAL.
- */
- flag &= ~RTLD_LOCAL;
- flag |= RTLD_GLOBAL;
- }
- }
-
- void *handle = _dlopen(filename, flag);
-
- if (intercept) {
- // Get the file path for our shared object, and use it instead
- static int dummy = 0xdeedbeef;
- Dl_info info;
- if (dladdr(&dummy, &info)) {
- handle = _dlopen(info.dli_fname, flag);
- } else {
- os::log("apitrace: warning: dladdr() failed\n");
- }
-
- // SDL will skip dlopen'ing libEGL.so after it spots EGL symbols on our
- // wrapper, so force loading it here.
- // (https://github.com/apitrace/apitrace/issues/291#issuecomment-59734022)
- if (strcmp(filename, "libEGL.so") != 0 &&
- strcmp(filename, "libEGL.so.1") != 0) {
- _dlopen("libEGL.so.1", RTLD_GLOBAL | RTLD_LAZY);
- }
- }
-
- return handle;
-}
-
-
#if defined(ANDROID)
/*
diff --git a/wrappers/egltrace.version b/wrappers/egltrace.version
index 0e7e62b9..e15a37c1 100644
--- a/wrappers/egltrace.version
+++ b/wrappers/egltrace.version
@@ -5,6 +5,7 @@
egl[A-Z]*;
gl[A-Z]*;
dlopen;
+ dlsym;
local:
*;
};
diff --git a/wrappers/glxtrace.py b/wrappers/glxtrace.py
index e9c43a9c..d9dce953 100644
--- a/wrappers/glxtrace.py
+++ b/wrappers/glxtrace.py
@@ -166,7 +166,6 @@ if __name__ == '__main__':
print '#define GL_GLEXT_PROTOTYPES'
print '#define GLX_GLXEXT_PROTOTYPES'
print
- print '#include "dlopen.hpp"'
print '#include "glproc.hpp"'
print '#include "glsize.hpp"'
print
@@ -178,52 +177,3 @@ if __name__ == '__main__':
api.addModule(module)
tracer = GlxTracer()
tracer.traceApi(api)
-
- print r'''
-
-
-/*
- * Several applications, such as Quake3, use dlopen("libGL.so.1"), but
- * LD_PRELOAD does not intercept symbols obtained via dlopen/dlsym, therefore
- * we need to intercept the dlopen() call here, and redirect to our wrapper
- * shared object.
- */
-extern "C" PUBLIC
-void * dlopen(const char *filename, int flag)
-{
- void *handle;
-
- handle = _dlopen(filename, flag);
-
- const char * libgl_filename = getenv("TRACE_LIBGL");
-
- if (filename && handle && !libgl_filename) {
- if (0) {
- os::log("apitrace: warning: dlopen(\"%s\", 0x%x)\n", filename, flag);
- }
-
- // FIXME: handle absolute paths and other versions
- if (strcmp(filename, "libGL.so") == 0 ||
- strcmp(filename, "libGL.so.1") == 0) {
-
- // Use the true libGL.so handle instead of RTLD_NEXT from now on
- _libGlHandle = handle;
-
- // Get the file path for our shared object, and use it instead
- static int dummy = 0xdeedbeef;
- Dl_info info;
- if (dladdr(&dummy, &info)) {
- os::log("apitrace: redirecting dlopen(\"%s\", 0x%x)\n", filename, flag);
- handle = _dlopen(info.dli_fname, flag);
- } else {
- os::log("apitrace: warning: dladdr() failed\n");
- }
- }
- }
-
- return handle;
-}
-
-
-
-'''
diff --git a/wrappers/glxtrace.version b/wrappers/glxtrace.version
index 7d7a1624..69d111a6 100644
--- a/wrappers/glxtrace.version
+++ b/wrappers/glxtrace.version
@@ -4,6 +4,7 @@
_fini;
gl[A-Z]*;
dlopen;
+ dlsym;
local:
*;
};