diff --git a/renderdoc/os/posix/linux/linux_hook.cpp b/renderdoc/os/posix/linux/linux_hook.cpp index 8bd3d1309..4989e2865 100644 --- a/renderdoc/os/posix/linux/linux_hook.cpp +++ b/renderdoc/os/posix/linux/linux_hook.cpp @@ -30,6 +30,7 @@ #include #include "common/threading.h" #include "core/core.h" +#include "core/settings.h" #include "hooks/hooks.h" #include "os/os_specific.h" #include "plthook/plthook.h" @@ -37,6 +38,8 @@ Threading::CriticalSection libLock; +RDOC_EXTERN_CONFIG(bool, Linux_Debug_PtraceLogging); + static std::map> libraryCallbacks; static rdcarray libraryHooks; static rdcarray functionHooks; @@ -97,47 +100,95 @@ void StopAtMainInChild(); bool StopChildAtMain(pid_t childPid); void ResumeProcess(pid_t childPid, uint32_t delay = 0); -__attribute__((visibility("default"))) int execle(const char *pathname, const char *arg, ...) -{ - va_list args; - va_start(args, arg); - - rdcarray argList; - argList.push_back((char *)arg); - - while(true) - { - char *nextArg = va_arg(args, char *); - argList.push_back(nextArg); - - // list is terminated with a NULL - if(!nextArg) - break; - } - - char **envp = va_arg(args, char **); +/////////////////////////////////////////////////////////////// +// exec hooks - we have to hook each variant since if the application calls the 'real' one of a +// variant, even if it ultimately goes to execve it will be resolved to the real libc one as libc +// doesn't get LD_PRELOAD hooked. +// +// There are two 'real' implementations we have, execve and execvpe that forward to the real +// function after patching the environment. That's to allow the real function to handle the path +// handling in the vpe case, otherwise they're identical. +// +// The other variants all forward to one of those - the 'l' cases unroll the va_args first before +// calling onwards +#define GET_EXECL_PARAMS(has_e) \ + va_list args; \ + va_start(args, arg); \ + \ + rdcarray arglist; \ + arglist.push_back((char *)arg); \ + \ + while(true) \ + { \ + char *nextArg = va_arg(args, char *); \ + arglist.push_back(nextArg); \ + \ + /* list is terminated with a NULL */ \ + if(!nextArg) \ + break; \ + } \ + \ + char **envp = NULL; \ + if(has_e) \ + envp = va_arg(args, char **); \ + \ va_end(args); - if(!realexecve) - { - EXECVEPROC passthru = (EXECVEPROC)dlsym(RTLD_NEXT, "execve"); - return passthru(pathname, argList.data(), envp); - } +__attribute__((visibility("default"))) int execl(const char *pathname, const char *arg, ...) +{ + GET_EXECL_PARAMS(false); - rdcarray modifiedEnv; - rdcstr envpStr; + if(Linux_Debug_PtraceLogging()) + RDCLOG("execl(%s)", pathname); - // if we're not hooking children just call to the real one, but ensure we remove any hooking env - // vars that were kept around after initialisation - if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren) - { - GetUnhookedEnvp(envp, envpStr, modifiedEnv); - return realexecve(pathname, argList.data(), modifiedEnv.data()); - } + return execve(pathname, arglist.data(), environ); +} - GetHookedEnvp(envp, envpStr, modifiedEnv); - return realexecve(pathname, argList.data(), modifiedEnv.data()); +__attribute__((visibility("default"))) int execlp(const char *pathname, const char *arg, ...) +{ + GET_EXECL_PARAMS(false); + + if(Linux_Debug_PtraceLogging()) + RDCLOG("execlp(%s)", pathname); + + return execvpe(pathname, arglist.data(), environ); +} + +__attribute__((visibility("default"))) int execle(const char *pathname, const char *arg, ...) +{ + GET_EXECL_PARAMS(true); + + if(Linux_Debug_PtraceLogging()) + RDCLOG("execle(%s)", pathname); + + return execve(pathname, arglist.data(), envp); +} + +__attribute__((visibility("default"))) int execlpe(const char *pathname, const char *arg, ...) +{ + GET_EXECL_PARAMS(true); + + if(Linux_Debug_PtraceLogging()) + RDCLOG("execlpe(%s)", pathname); + + return execvpe(pathname, arglist.data(), envp); +} + +__attribute__((visibility("default"))) int execv(const char *pathname, char *const argv[]) +{ + if(Linux_Debug_PtraceLogging()) + RDCLOG("execv(%s)", pathname); + + return execve(pathname, argv, environ); +} + +__attribute__((visibility("default"))) int execvp(const char *pathname, char *const argv[]) +{ + if(Linux_Debug_PtraceLogging()) + RDCLOG("execvp(%s)", pathname); + + return execvpe(pathname, argv, environ); } __attribute__((visibility("default"))) int execve(const char *pathname, char *const argv[], @@ -145,6 +196,9 @@ __attribute__((visibility("default"))) int execve(const char *pathname, char *co { if(!realexecve) { + if(Linux_Debug_PtraceLogging()) + RDCLOG("unhooked early execve(%s)", pathname); + EXECVEPROC passthru = (EXECVEPROC)dlsym(RTLD_NEXT, "execve"); return passthru(pathname, argv, envp); } @@ -156,10 +210,16 @@ __attribute__((visibility("default"))) int execve(const char *pathname, char *co // vars that were kept around after initialisation if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren) { + if(Linux_Debug_PtraceLogging()) + RDCLOG("unhooked execve(%s)", pathname); + GetUnhookedEnvp(envp, envpStr, modifiedEnv); - return realexecve(pathname, argv, envp); + return realexecve(pathname, argv, modifiedEnv.data()); } + if(Linux_Debug_PtraceLogging()) + RDCLOG("hooked execve(%s)", pathname); + GetHookedEnvp(envp, envpStr, modifiedEnv); return realexecve(pathname, argv, modifiedEnv.data()); } @@ -169,6 +229,9 @@ __attribute__((visibility("default"))) int execvpe(const char *pathname, char *c { if(!realexecvpe) { + if(Linux_Debug_PtraceLogging()) + RDCLOG("unhooked early execvpe(%s)", pathname); + EXECVPEPROC passthru = (EXECVPEPROC)dlsym(RTLD_NEXT, "execvpe"); return passthru(pathname, argv, envp); } @@ -180,10 +243,16 @@ __attribute__((visibility("default"))) int execvpe(const char *pathname, char *c // vars that were kept around after initialisation if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren) { + if(Linux_Debug_PtraceLogging()) + RDCLOG("unhooked execvpe(%s)", pathname); + GetUnhookedEnvp(envp, envpStr, modifiedEnv); - return realexecvpe(pathname, argv, envp); + return realexecvpe(pathname, argv, modifiedEnv.data()); } + if(Linux_Debug_PtraceLogging()) + RDCLOG("hooked execvpe(%s)", pathname); + GetHookedEnvp(envp, envpStr, modifiedEnv); return realexecvpe(pathname, argv, modifiedEnv.data()); } @@ -207,6 +276,9 @@ __attribute__((visibility("default"))) pid_t fork() // envp because it was probably fetched before the fork - see the exec hooks for where we patch // that part. + if(Linux_Debug_PtraceLogging()) + RDCLOG("non-hooked fork()"); + pid_t ret = realfork(); if(ret == 0) unsetenv(RENDERDOC_VULKAN_LAYER_VAR); @@ -214,6 +286,9 @@ __attribute__((visibility("default"))) pid_t fork() return ret; } + if(Linux_Debug_PtraceLogging()) + RDCLOG("hooked fork()"); + // fork in a captured application. Need to get the child ident and register it // set up environment variables for hooking now @@ -223,6 +298,9 @@ __attribute__((visibility("default"))) pid_t fork() if(ret == 0) { + if(Linux_Debug_PtraceLogging()) + RDCLOG("hooked fork() in child %d", getpid()); + StopAtMainInChild(); } else if(ret > 0) @@ -230,6 +308,9 @@ __attribute__((visibility("default"))) pid_t fork() // restore environment variables ResetHookingEnvVars(); + if(Linux_Debug_PtraceLogging()) + RDCLOG("hooked fork() in parent, child is %d", ret); + bool stopped = StopChildAtMain(ret); if(stopped) diff --git a/renderdoc/renderdoc.version b/renderdoc/renderdoc.version index c75e39eb3..5b0e0e535 100644 --- a/renderdoc/renderdoc.version +++ b/renderdoc/renderdoc.version @@ -8,7 +8,12 @@ dlopen; dlsym; fork; + execl; + execlp; execle; + execlpe; + execv; + execvp; execve; execvpe; _exit;