diff options
author | Jose Fonseca <jfonseca@vmware.com> | 2016-06-30 14:51:54 +0100 |
---|---|---|
committer | Jose Fonseca <jfonseca@vmware.com> | 2016-06-30 17:17:11 +0100 |
commit | 4f97da64f693607e191cd6b4cd4a8da86d7b6779 (patch) | |
tree | 6507e64022e541a7d840a28b328fe48ab8ccea6a | |
parent | 75ca211ceea3ec77bee3cdd8d91d5f0b23170ea6 (diff) |
egltrace: Don't intercept dlopen calls done by EGL/GL/GLES implementation.
Certain EGL implementations do this.
-rw-r--r-- | wrappers/CMakeLists.txt | 2 | ||||
-rw-r--r-- | wrappers/dlsym.cpp | 148 | ||||
-rw-r--r-- | wrappers/egltrace.py | 67 | ||||
-rw-r--r-- | wrappers/glxtrace.py | 50 |
4 files changed, 150 insertions, 117 deletions
diff --git a/wrappers/CMakeLists.txt b/wrappers/CMakeLists.txt index 5e29b1ac..4bd3ef19 100644 --- a/wrappers/CMakeLists.txt +++ b/wrappers/CMakeLists.txt @@ -418,6 +418,8 @@ if (ENABLE_EGL AND NOT WIN32 AND NOT APPLE) PREFIX "" ) + target_compile_definitions (egltrace PRIVATE -DEGLTRACE=1) + target_link_libraries (egltrace gltrace_common glproc_egl 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; +} diff --git a/wrappers/egltrace.py b/wrappers/egltrace.py index 333809b9..8e658141 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"' @@ -128,70 +127,6 @@ if __name__ == '__main__': 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) -{ - 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) /* @@ -251,6 +186,4 @@ void APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, GLsizei stride, */ #endif /* ANDROID */ - - ''' 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; -} - - - -''' |