summaryrefslogtreecommitdiff
path: root/wrappers/dlsym.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wrappers/dlsym.cpp')
-rw-r--r--wrappers/dlsym.cpp148
1 files changed, 148 insertions, 0 deletions
diff --git a/wrappers/dlsym.cpp b/wrappers/dlsym.cpp
index 118110f2..5f34a2f0 100644
--- a/wrappers/dlsym.cpp
+++ b/wrappers/dlsym.cpp
@@ -25,6 +25,9 @@
**************************************************************************/
+#include <assert.h>
+#include <memory>
+
#include "os.hpp"
@@ -77,3 +80,148 @@ dlsym(void * handle, const char * symbol)
#endif /* __GLIBC__ */
+
+
+#include "dlopen.hpp"
+
+
+extern void * _libGlHandle;
+
+
+
+enum LibClass {
+ LIB_UNKNOWN = 0,
+ LIB_GL,
+ LIB_EGL,
+ LIB_GLES1,
+ LIB_GLES2,
+};
+
+
+inline LibClass
+classifyLibrary(const char *pathname)
+{
+ std::unique_ptr<char, decltype(std::free) *> dupname { strdup(pathname), std::free };
+
+ char *filename = basename(dupname.get());
+ assert(filename);
+
+ if (strcmp(filename, "libGL.so") == 0 ||
+ strcmp(filename, "libGL.so.1") == 0) {
+ return LIB_GL;
+ }
+
+#ifdef EGLTRACE
+ if (strcmp(filename, "libEGL.so") == 0 ||
+ strcmp(filename, "libEGL.so.1") == 0) {
+ return LIB_EGL;
+ }
+
+ if (strcmp(filename, "libGLESv1_CM.so") == 0 ||
+ strcmp(filename, "libGLESv1_CM.so.1") == 0) {
+ return LIB_GLES1;
+ }
+
+ if (strcmp(filename, "libGLESv2.so") == 0 ||
+ strcmp(filename, "libGLESv2.so.2") == 0) {
+ return LIB_GLES2;
+ }
+#endif
+
+ /*
+ * TODO: Identify driver SOs (e.g, *_dri.so), to prevent intercepting
+ * dlopen calls from them.
+ *
+ * Another alternative is to ignore dlopen calls when inside wrapped calls.
+ */
+
+ return LIB_UNKNOWN;
+}
+
+
+/*
+ * 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;
+
+ if (!filename) {
+ return _dlopen(filename, flag);
+ }
+
+ LibClass libClass = classifyLibrary(filename);
+ bool intercept = libClass != LIB_UNKNOWN;
+
+ if (intercept) {
+ void *caller = __builtin_return_address(0);
+ Dl_info info;
+ const char *caller_module = "<unknown>";
+ if (dladdr(caller, &info)) {
+ caller_module = info.dli_fname;
+ intercept = classifyLibrary(caller_module) == LIB_UNKNOWN;
+ }
+
+ const char * libgl_filename = getenv("TRACE_LIBGL");
+ if (libgl_filename) {
+ // Don't intercept when using LD_LIBRARY_PATH instead of LD_PRELOAD
+ intercept = false;
+ }
+
+ os::log("apitrace: %s dlopen(\"%s\", 0x%x) from %s\n",
+ intercept ? "redirecting" : "ignoring",
+ filename, flag, caller_module);
+ }
+
+#ifdef EGLTRACE
+
+ if (intercept) {
+ /* 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;
+ }
+
+#endif
+
+ handle = _dlopen(filename, flag);
+ if (!handle) {
+ return handle;
+ }
+
+ if (intercept) {
+ if (libClass == LIB_GL) {
+ // 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)) {
+ handle = _dlopen(info.dli_fname, flag);
+ } else {
+ os::log("apitrace: warning: dladdr() failed\n");
+ }
+
+#ifdef EGLTRACE
+ // 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);
+ }
+#endif
+ }
+
+ return handle;
+}