diff options
Diffstat (limited to 'wrappers/dlsym.cpp')
-rw-r--r-- | wrappers/dlsym.cpp | 148 |
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; +} |