diff --git a/renderdoc/driver/gl/egl_hooks.cpp b/renderdoc/driver/gl/egl_hooks.cpp index 94330e817..5cb22e455 100644 --- a/renderdoc/driver/gl/egl_hooks.cpp +++ b/renderdoc/driver/gl/egl_hooks.cpp @@ -108,6 +108,29 @@ public: } eglhook; +// On linux if a user doesn't link to libEGL or try to dlopen it, but just calls dlsym with +// RTLD_NEXT it might successfully one of our functions without anything ever loading libEGL. Then +// our attempts to call onwards will fail. When any of our functions are called we check to see if +// DEFAULT_HANDLE is RTLD_NEXT and if so we manually load the library. This will trigger our hook +// callback and we'll get a specific library handle. +// +// On other platforms this is not needed because we know the real library will be loaded before any +// of our hooks can be called +static void EnsureRealLibraryLoaded() +{ +#if ENABLED(RDOC_LINUX) + if(eglhook.handle == DEFAULT_HANDLE) + { + RDCLOG("Loading libEGL at the last second"); + + void *handle = Process::LoadModule("libEGL.so"); + + if(!handle) + handle = Process::LoadModule("libEGL.so.1"); + } +#endif +} + HOOK_EXPORT EGLDisplay EGLAPIENTRY eglGetDisplay_renderdoc_hooked(EGLNativeDisplayType display) { if(RenderDoc::Inst().IsReplayApp()) @@ -118,6 +141,8 @@ HOOK_EXPORT EGLDisplay EGLAPIENTRY eglGetDisplay_renderdoc_hooked(EGLNativeDispl return EGL.GetDisplay(display); } + EnsureRealLibraryLoaded(); + #if ENABLED(RDOC_LINUX) Keyboard::CloneDisplay(display); #endif @@ -135,6 +160,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglBindAPI_renderdoc_hooked(EGLenum api) return EGL.BindAPI(api); } + EnsureRealLibraryLoaded(); + EGLBoolean ret = EGL.BindAPI(api); if(ret) @@ -156,6 +183,8 @@ HOOK_EXPORT EGLContext EGLAPIENTRY eglCreateContext_renderdoc_hooked(EGLDisplay return EGL.CreateContext(display, config, shareContext, attribList); } + EnsureRealLibraryLoaded(); + LibraryHooks::Refresh(); std::vector attribs; @@ -274,6 +303,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglDestroyContext_renderdoc_hooked(EGLDisplay return EGL.DestroyContext(dpy, ctx); } + EnsureRealLibraryLoaded(); + eglhook.driver.SetDriverType(eglhook.activeAPI); { SCOPED_LOCK(glLock); @@ -297,6 +328,8 @@ HOOK_EXPORT EGLSurface EGLAPIENTRY eglCreateWindowSurface_renderdoc_hooked(EGLDi return EGL.CreateWindowSurface(dpy, config, win, attrib_list); } + EnsureRealLibraryLoaded(); + EGLSurface ret = EGL.CreateWindowSurface(dpy, config, win, attrib_list); if(ret) @@ -321,6 +354,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglMakeCurrent_renderdoc_hooked(EGLDisplay di return EGL.MakeCurrent(display, draw, read, ctx); } + EnsureRealLibraryLoaded(); + EGLBoolean ret = EGL.MakeCurrent(display, draw, read, ctx); if(ret) @@ -377,6 +412,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffers_renderdoc_hooked(EGLDisplay dp return EGL.SwapBuffers(dpy, surface); } + EnsureRealLibraryLoaded(); + SCOPED_LOCK(glLock); eglhook.driver.SetDriverType(eglhook.activeAPI); @@ -413,6 +450,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglPostSubBufferNV_renderdoc_hooked(EGLDispla return EGL.PostSubBufferNV(dpy, surface, x, y, width, height); } + EnsureRealLibraryLoaded(); + SCOPED_LOCK(glLock); eglhook.driver.SetDriverType(eglhook.activeAPI); @@ -440,6 +479,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT_renderdoc_hooked( return EGL.SwapBuffersWithDamageEXT(dpy, surface, rects, n_rects); } + EnsureRealLibraryLoaded(); + SCOPED_LOCK(glLock); eglhook.driver.SetDriverType(eglhook.activeAPI); @@ -467,6 +508,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR_renderdoc_hooked( return EGL.SwapBuffersWithDamageKHR(dpy, surface, rects, n_rects); } + EnsureRealLibraryLoaded(); + SCOPED_LOCK(glLock); eglhook.driver.SetDriverType(eglhook.activeAPI); @@ -492,6 +535,8 @@ eglGetProcAddress_renderdoc_hooked(const char *func) return EGL.GetProcAddress(func); } + EnsureRealLibraryLoaded(); + __eglMustCastToProperFunctionPointerType realFunc = NULL; { ScopedSuppressHooking suppress; @@ -593,6 +638,7 @@ HOOK_EXPORT __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddre typedef ret (*CONCAT(function, _hooktype))(); \ HOOK_EXPORT ret EGLAPIENTRY function() \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))Process::GetFunctionAddress(eglhook.handle, \ STRINGIZE(function)); \ @@ -603,6 +649,7 @@ HOOK_EXPORT __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddre typedef ret (*CONCAT(function, _hooktype))(t1); \ HOOK_EXPORT ret EGLAPIENTRY function(t1 p1) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))Process::GetFunctionAddress(eglhook.handle, \ STRINGIZE(function)); \ @@ -613,6 +660,7 @@ HOOK_EXPORT __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddre typedef ret (*CONCAT(function, _hooktype))(t1, t2); \ HOOK_EXPORT ret EGLAPIENTRY function(t1 p1, t2 p2) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))Process::GetFunctionAddress(eglhook.handle, \ STRINGIZE(function)); \ @@ -623,6 +671,7 @@ HOOK_EXPORT __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddre typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3); \ HOOK_EXPORT ret EGLAPIENTRY function(t1 p1, t2 p2, t3 p3) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))Process::GetFunctionAddress(eglhook.handle, \ STRINGIZE(function)); \ @@ -633,6 +682,7 @@ HOOK_EXPORT __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddre typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3, t4); \ HOOK_EXPORT ret EGLAPIENTRY function(t1 p1, t2 p2, t3 p3, t4 p4) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))Process::GetFunctionAddress(eglhook.handle, \ STRINGIZE(function)); \ @@ -643,6 +693,7 @@ HOOK_EXPORT __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddre typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3, t4, t5); \ HOOK_EXPORT ret EGLAPIENTRY function(t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))Process::GetFunctionAddress(eglhook.handle, \ STRINGIZE(function)); \ diff --git a/renderdoc/driver/gl/glx_hooks.cpp b/renderdoc/driver/gl/glx_hooks.cpp index 54b1c9104..ec402e1bc 100644 --- a/renderdoc/driver/gl/glx_hooks.cpp +++ b/renderdoc/driver/gl/glx_hooks.cpp @@ -58,6 +58,25 @@ public: std::set contexts; } glxhook; +// On linux if a user doesn't link to libGL/libGLX or try to dlopen it, but just calls dlsym with +// RTLD_NEXT it might successfully one of our functions without anything ever loading libEGL. Then +// our attempts to call onwards will fail. When any of our functions are called we check to see if +// DEFAULT_HANDLE is RTLD_NEXT and if so we manually load the library. This will trigger our hook +// callback and we'll get a specific library handle. +static void EnsureRealLibraryLoaded() +{ + if(glxhook.handle == RTLD_NEXT) + { + RDCLOG("Loading libGL at the last second"); + + void *handle = Process::LoadModule("libGL.so.1"); + if(!handle) + handle = Process::LoadModule("libGL.so"); + if(!handle) + handle = Process::LoadModule("libGLX.so.0"); + } +} + HOOK_EXPORT GLXContext glXCreateContext_renderdoc_hooked(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct) { @@ -69,6 +88,8 @@ HOOK_EXPORT GLXContext glXCreateContext_renderdoc_hooked(Display *dpy, XVisualIn return GLX.glXCreateContext(dpy, vis, shareList, direct); } + EnsureRealLibraryLoaded(); + GLXContext ret = GLX.glXCreateContext(dpy, vis, shareList, direct); // don't continue if context creation failed @@ -121,6 +142,8 @@ HOOK_EXPORT void glXDestroyContext_renderdoc_hooked(Display *dpy, GLXContext ctx return GLX.glXDestroyContext(dpy, ctx); } + EnsureRealLibraryLoaded(); + { SCOPED_LOCK(glLock); glxhook.driver.DeleteContext(ctx); @@ -142,6 +165,8 @@ HOOK_EXPORT GLXContext glXCreateContextAttribsARB_renderdoc_hooked(Display *dpy, return GLX.glXCreateContextAttribsARB(dpy, config, shareList, direct, attribList); } + EnsureRealLibraryLoaded(); + int defaultAttribList[] = {0}; const int *attribs = attribList ? attribList : defaultAttribList; @@ -264,6 +289,8 @@ HOOK_EXPORT Bool glXMakeCurrent_renderdoc_hooked(Display *dpy, GLXDrawable drawa return GLX.glXMakeCurrent(dpy, drawable, ctx); } + EnsureRealLibraryLoaded(); + Bool ret = GLX.glXMakeCurrent(dpy, drawable, ctx); if(ret) @@ -332,6 +359,8 @@ HOOK_EXPORT Bool glXMakeContextCurrent_renderdoc_hooked(Display *dpy, GLXDrawabl return GLX.glXMakeContextCurrent(dpy, draw, read, ctx); } + EnsureRealLibraryLoaded(); + Bool ret = GLX.glXMakeContextCurrent(dpy, draw, read, ctx); if(ret) @@ -398,6 +427,8 @@ HOOK_EXPORT void glXSwapBuffers_renderdoc_hooked(Display *dpy, GLXDrawable drawa return GLX.glXSwapBuffers(dpy, drawable); } + EnsureRealLibraryLoaded(); + SCOPED_LOCK(glLock); { @@ -425,6 +456,8 @@ HOOK_EXPORT __GLXextFuncPtr glXGetProcAddress_renderdoc_hooked(const GLubyte *f) return GLX.glXGetProcAddress(f); } + EnsureRealLibraryLoaded(); + const char *func = (const char *)f; __GLXextFuncPtr realFunc = NULL; @@ -527,6 +560,7 @@ HOOK_EXPORT __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *f) typedef ret (*CONCAT(function, _hooktype))(); \ extern "C" __attribute__((visibility("default"))) ret function() \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))dlsym(glxhook.handle, STRINGIZE(function)); \ return real(); \ @@ -536,6 +570,7 @@ HOOK_EXPORT __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *f) typedef ret (*CONCAT(function, _hooktype))(t1); \ extern "C" __attribute__((visibility("default"))) ret function(t1 p1) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))dlsym(glxhook.handle, STRINGIZE(function)); \ return real(p1); \ @@ -545,6 +580,7 @@ HOOK_EXPORT __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *f) typedef ret (*CONCAT(function, _hooktype))(t1, t2); \ extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))dlsym(glxhook.handle, STRINGIZE(function)); \ return real(p1, p2); \ @@ -554,6 +590,7 @@ HOOK_EXPORT __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *f) typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3); \ extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))dlsym(glxhook.handle, STRINGIZE(function)); \ return real(p1, p2, p3); \ @@ -563,6 +600,7 @@ HOOK_EXPORT __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *f) typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3, t4); \ extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3, t4 p4) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))dlsym(glxhook.handle, STRINGIZE(function)); \ return real(p1, p2, p3, p4); \ @@ -572,6 +610,7 @@ HOOK_EXPORT __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *f) typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3, t4, t5); \ extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) \ { \ + EnsureRealLibraryLoaded(); \ CONCAT(function, _hooktype) \ real = (CONCAT(function, _hooktype))dlsym(glxhook.handle, STRINGIZE(function)); \ return real(p1, p2, p3, p4, p5); \