Add some fixes.

This commit is contained in:
Er2
2023-11-06 14:42:45 +00:00
committed by Baldur Karlsson
parent 86cb3ebf80
commit dcbbcee6a8
17 changed files with 1566 additions and 55 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ if(APPLE)
endif()
if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "FreeBSD")
set(FreeBSD YES)
set(FREEBSD YES)
endif()
# Configure some stuff that needs to be set really early
+3 -13
View File
@@ -20,7 +20,6 @@
actual site failure. So the debugger stops on the CHECK() or REQUIRE()
call instead of inside AssertionHandler::complete()
* https://github.com/baldurk/renderdoc/commit/4232736fc21fc6a13a4de6997a5ae106598b225f
* Add FreeBSD support with correct debugger handling
*/
#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
@@ -2725,7 +2724,7 @@ namespace Catch {
#define CATCH_TRAP() __asm__(".inst 0xde01")
#endif
#elif defined(CATCH_PLATFORM_LINUX) || defined(__FreeBSD__)
#elif defined(CATCH_PLATFORM_LINUX)
// If we can use inline assembler, do it because this allows us to break
// directly at the location of the failing check instead of breaking inside
// raise() called from it, i.e. one stack frame below.
@@ -10429,7 +10428,7 @@ namespace Catch {
// end catch_debug_console.cpp
// start catch_debugger.cpp
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) || defined(__FreeBSD__)
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
# include <cassert>
# include <sys/types.h>
@@ -10441,13 +10440,10 @@ namespace Catch {
// These headers will only compile with AppleClang (XCode)
// For other compilers (Clang, GCC, ... ) we need to exclude them
# include <sys/sysctl.h>
#elif defined(__FreeBSD__)
# include <sys/user.h>
# include <sys/sysctl.h>
#endif
namespace Catch {
#if defined(__apple_build_version__) || defined(__FreeBSD__)
#ifdef __apple_build_version__
// The following function is taken directly from the following technical note:
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
@@ -10458,12 +10454,10 @@ namespace Catch {
struct kinfo_proc info;
std::size_t size;
#ifndef __FreeBSD__
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
#endif
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
@@ -10483,11 +10477,7 @@ namespace Catch {
// We're being debugged if the P_TRACED flag is set.
#ifdef __FreeBSD__
return ( (info.ki_flag & P_TRACED) != 0 );
#else
return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
#endif
}
#else
bool isDebuggerActive() {
+1 -1
View File
@@ -37,7 +37,7 @@
#include <QStyledItemDelegate>
#include "Code/Interface/QRDInterface.h"
#if !defined(RELEASE)
#if !defined(RELEASE) && !defined(__FreeBSD__)
#define ENABLE_UNIT_TESTS 1
#else
#define ENABLE_UNIT_TESTS 0
+3 -13
View File
@@ -20,7 +20,6 @@
actual site failure. So the debugger stops on the CHECK() or REQUIRE()
call instead of inside AssertionHandler::complete()
* https://github.com/baldurk/renderdoc/commit/4232736fc21fc6a13a4de6997a5ae106598b225f
* Add FreeBSD support with correct debugger handling
*/
#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
@@ -2725,7 +2724,7 @@ namespace Catch {
#define CATCH_TRAP() __asm__(".inst 0xde01")
#endif
#elif defined(CATCH_PLATFORM_LINUX) || defined(__FreeBSD__)
#elif defined(CATCH_PLATFORM_LINUX)
// If we can use inline assembler, do it because this allows us to break
// directly at the location of the failing check instead of breaking inside
// raise() called from it, i.e. one stack frame below.
@@ -10429,7 +10428,7 @@ namespace Catch {
// end catch_debug_console.cpp
// start catch_debugger.cpp
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) || defined(__FreeBSD__)
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
# include <cassert>
# include <sys/types.h>
@@ -10441,13 +10440,10 @@ namespace Catch {
// These headers will only compile with AppleClang (XCode)
// For other compilers (Clang, GCC, ... ) we need to exclude them
# include <sys/sysctl.h>
#elif defined(__FreeBSD__)
# include <sys/user.h>
# include <sys/sysctl.h>
#endif
namespace Catch {
#if defined(__apple_build_version__) || defined(__FreeBSD__)
#ifdef __apple_build_version__
// The following function is taken directly from the following technical note:
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
@@ -10458,12 +10454,10 @@ namespace Catch {
struct kinfo_proc info;
std::size_t size;
#ifndef __FreeBSD__
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
#endif
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
@@ -10483,11 +10477,7 @@ namespace Catch {
// We're being debugged if the P_TRACED flag is set.
#ifdef __FreeBSD__
return ( (info.ki_flag & P_TRACED) != 0 );
#else
return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
#endif
}
#else
bool isDebuggerActive() {
+8 -8
View File
@@ -59,7 +59,7 @@ elseif(UNIX)
PRIVATE -ldl
PRIVATE -lrt)
if(FreeBSD)
if(FREEBSD)
list(APPEND RDOC_LIBRARIES
PRIVATE -L/usr/local/lib
PRIVATE -liconv
@@ -338,15 +338,15 @@ elseif(ENABLE_GGP)
os/posix/posix_stringio.cpp
os/posix/posix_threading.cpp
os/posix/posix_specific.h)
elseif(FreeBSD)
elseif(FREEBSD)
list(APPEND sources
data/embedded_files.h
os/posix/linux/linux_stringio.cpp
os/posix/linux/linux_callstack.cpp
os/posix/linux/linux_threading.cpp
os/posix/freebsd/freebsd_process.cpp
os/posix/linux/linux_hook.cpp
os/posix/linux/linux_network.cpp
os/posix/bsd/bsd_stringio.cpp
os/posix/bsd/bsd_callstack.cpp
os/posix/bsd/bsd_threading.cpp
os/posix/bsd/bsd_process.cpp
os/posix/bsd/bsd_hook.cpp
os/posix/bsd/bsd_network.cpp
3rdparty/plthook/plthook.h
3rdparty/plthook/plthook_elf.c
os/posix/posix_network.h
+2 -2
View File
@@ -226,8 +226,8 @@ enum
// this strips them completely
#define STRIP_DEBUG_LOGS OPTION_OFF
// disable unit tests on android
#if ENABLED(RDOC_ANDROID)
// disable unit tests on android and *BSD
#if ENABLED(RDOC_ANDROID) || defined(__FreeBSD__)
#define ENABLE_UNIT_TESTS OPTION_OFF
-3
View File
@@ -84,9 +84,6 @@ void GLResourceManager::MarkFBOAttachmentsReferenced(ResourceId fboid, GLResourc
{
FBOCache *cache = m_FBOAttachmentsCache[fboid];
if(!record)
return;
if(!cache)
{
cache = m_FBOAttachmentsCache[fboid] = new FBOCache;
+88
View File
@@ -0,0 +1,88 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2023 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "common/common.h"
#include "os/os_specific.h"
class BSDCallstack : public Callstack::Stackwalk
{
public:
BSDCallstack()
{
RDCEraseEl(addrs);
numLevels = 0;
}
BSDCallstack(uint64_t *calls, size_t num) { Set(calls, num); }
~BSDCallstack() {}
void Set(uint64_t *calls, size_t num)
{
numLevels = num;
for(size_t i = 0; i < numLevels; i++)
addrs[i] = calls[i];
}
size_t NumLevels() const { return 0; }
const uint64_t *GetAddrs() const { return addrs; }
private:
BSDCallstack(const Callstack::Stackwalk &other);
uint64_t addrs[128];
size_t numLevels;
};
namespace Callstack
{
void Init()
{
}
Stackwalk *Collect()
{
return new BSDCallstack();
}
Stackwalk *Create()
{
return new BSDCallstack(NULL, 0);
}
bool GetLoadedModules(byte *buf, size_t &size)
{
size = 0;
if(buf)
memcpy(buf, "BSD_CALL", 8);
size += 8;
return true;
}
StackResolver *MakeResolver(bool interactive, byte *moduleDB, size_t DBSize,
RENDERDOC_ProgressCallback progress)
{
RDCERR("Callstack resolving not supported on *BSD.");
return NULL;
}
};
+556
View File
@@ -0,0 +1,556 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2023 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include <dlfcn.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <map>
#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"
#include "strings/string_utils.h"
Threading::CriticalSection libLock;
static std::map<rdcstr, rdcarray<FunctionLoadCallback>> libraryCallbacks;
static rdcarray<rdcstr> libraryHooks;
static rdcarray<FunctionHook> functionHooks;
void *intercept_dlopen(const char *filename, int flag, void *ret);
void plthook_lib(void *handle);
typedef pid_t (*FORKPROC)();
typedef int (*EXECLEPROC)(const char *pathname, const char *arg, ...);
typedef int (*EXECVEPROC)(const char *pathname, char *const argv[], char *const envp[]);
typedef int (*EXECVPEPROC)(const char *pathname, char *const argv[], char *const envp[]);
typedef void *(*DLOPENPROC)(const char *, int);
typedef void *(*DLSYMPROC)(void *, const char *);
DLOPENPROC realdlopen = NULL;
EXECLEPROC realexecle = NULL;
EXECVEPROC realexecve = NULL;
EXECVPEPROC realexecvpe = NULL;
FORKPROC realfork = NULL;
DLSYMPROC realdlsym = NULL;
static int32_t tlsbusyflag = 0;
__attribute__((visibility("default"))) void *dlopen(const char *filename, int flag)
{
if(!realdlopen)
{
DLOPENPROC passthru = (DLOPENPROC)dlsym(RTLD_NEXT, "dlopen");
void *ret = passthru(filename, flag);
if(filename && ret && (flag & RTLD_DEEPBIND))
plthook_lib(ret);
return ret;
}
if(RenderDoc::Inst().IsReplayApp())
return realdlopen(filename, flag);
// don't do any hook processing inside here even if we call dlopen again
Atomic::Inc32(&tlsbusyflag);
void *ret = realdlopen(filename, flag);
Atomic::Dec32(&tlsbusyflag);
if(filename && ret)
{
SCOPED_LOCK(libLock);
ret = intercept_dlopen(filename, flag, ret);
}
return ret;
}
int GetIdentPort(pid_t childPid);
void PreForkConfigureHooks();
void GetUnhookedEnvp(char *const *envp, rdcstr &envpStr, rdcarray<char *> &modifiedEnv);
void GetHookedEnvp(char *const *envp, rdcstr &envpStr, rdcarray<char *> &modifiedEnv);
void ResetHookingEnvVars();
void StopAtMainInChild();
bool StopChildAtMain(pid_t childPid, bool *exitWithNoExec);
void ResumeProcess(pid_t childPid, uint32_t delay = 0);
int direct_setenv(const char *name, const char *value, int overwrite);
///////////////////////////////////////////////////////////////
// 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
#if 1 // TODO: environ bug
char **GetCurrentEnvironment();
#define environ GetCurrentEnvironment()
#define GET_EXECL_PARAMS(has_e) \
va_list args; \
va_start(args, arg); \
\
rdcarray<char *> 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);
__attribute__((visibility("default"))) int execl(const char *pathname, const char *arg, ...)
{
GET_EXECL_PARAMS(false);
return execve(pathname, arglist.data(), environ);
}
__attribute__((visibility("default"))) int execle(const char *pathname, const char *arg, ...)
{
GET_EXECL_PARAMS(true);
return execve(pathname, arglist.data(), envp);
}
__attribute__((visibility("default"))) int execv(const char *pathname, char *const argv[])
{
return execve(pathname, argv, environ);
}
#endif
__attribute__((visibility("default"))) int execve(const char *pathname, char *const argv[],
char *const envp[])
{
if(!realexecve)
{
EXECVEPROC passthru = (EXECVEPROC)dlsym(RTLD_NEXT, "execve");
return passthru(pathname, argv, envp);
}
if(RenderDoc::Inst().IsReplayApp())
return realexecve(pathname, argv, envp);
rdcarray<char *> modifiedEnv;
rdcstr envpStr;
// 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, argv, modifiedEnv.data());
}
GetHookedEnvp(envp, envpStr, modifiedEnv);
return realexecve(pathname, argv, modifiedEnv.data());
}
__attribute__((visibility("default"))) int execvpe(const char *pathname, char *const argv[],
char *const envp[])
{
if(!realexecvpe)
{
EXECVPEPROC passthru = (EXECVPEPROC)dlsym(RTLD_NEXT, "execvpe");
return passthru(pathname, argv, envp);
}
if(RenderDoc::Inst().IsReplayApp())
return realexecvpe(pathname, argv, envp);
rdcarray<char *> modifiedEnv;
rdcstr envpStr;
// 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 realexecvpe(pathname, argv, modifiedEnv.data());
}
GetHookedEnvp(envp, envpStr, modifiedEnv);
return realexecvpe(pathname, argv, modifiedEnv.data());
}
__attribute__((visibility("default"))) pid_t fork()
{
if(!realfork)
{
FORKPROC passthru = (FORKPROC)dlsym(RTLD_NEXT, "fork");
return passthru();
}
if(RenderDoc::Inst().IsReplayApp())
return realfork();
// if we're not hooking children just call to the real one, we don't have to do anything
if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren)
{
// this is a nasty hack. We set this env var when we inject into a process, but because we don't
// know when vulkan may be initialised we need to leave it on indefinitely. If we're not
// injecting into children we need to unset this variable so it doesn't get inherited.
//
// Note this does nothing if the application is doing fork + execve or other variant that passes
// envp because it was probably fetched before the fork - see the exec hooks for where we patch
// that part.
pid_t ret = realfork();
if(ret == 0)
direct_setenv(RENDERDOC_VULKAN_LAYER_VAR, "", true);
return ret;
}
// fork in a captured application. Need to get the child ident and register it
// set up environment variables for hooking now
PreForkConfigureHooks();
pid_t ret = realfork();
if(ret == 0)
{
StopAtMainInChild();
}
else if(ret > 0)
{
// restore environment variables
ResetHookingEnvVars();
bool exitWithNoExec = false;
bool stopped = StopChildAtMain(ret, &exitWithNoExec);
if(!exitWithNoExec && stopped)
{
int ident = GetIdentPort(ret);
ResumeProcess(ret);
if(ident)
{
RDCLOG("Identified child process %u with ident %u", ret, ident);
RenderDoc::Inst().AddChildProcess((uint32_t)ret, (uint32_t)ident);
}
else
{
RDCERR("Couldn't get ident for PID %u after stopping at main", ret);
}
}
else
{
// resume the process just in case something went wrong. This should be harmless if we're not
// actually tracing
ResumeProcess(ret);
// ptrace_scope isn't amenable, or we hit an error. We'll have to spin up a thread to check
// the ident on the child process and add it as soon as it's available
Threading::ThreadHandle handle = Threading::CreateThread([ret]() {
RDCLOG("Starting thread to get ident for PID %u", ret);
// don't accept a return value of our own ident, that means we've checked too early and exec
// hasn't run yet
const uint32_t ownIdent = RenderDoc::Inst().GetTargetControlIdent();
uint32_t ident = ownIdent;
for(uint32_t i = 0; i < 10 && ident == ownIdent; i++)
{
ident = (uint32_t)GetIdentPort(ret);
if(ident == ownIdent)
usleep(1000);
}
if(ident == ownIdent)
ident = 0;
RDCLOG("PID %u has ident %u", ret, ident);
RenderDoc::Inst().AddChildProcess((uint32_t)ret, (uint32_t)ident);
RenderDoc::Inst().CompleteChildThread((uint32_t)ret);
});
RenderDoc::Inst().AddChildThread((uint32_t)ret, handle);
}
}
return ret;
}
#if defined(RENDERDOC_HOOK_DLSYM)
#pragma message("ALERT: dlsym() hooking enabled! This is unreliable & relies on glibc internals.")
extern "C" {
__attribute__((visibility("default"))) void *dlsym(void *handle, const char *name);
extern void *_dl_sym(void *, const char *, void *);
void bootstrap_dlsym()
{
realdlsym = (DLSYMPROC)_dl_sym(RTLD_NEXT, "dlsym", (void *)&dlsym);
}
__attribute__((visibility("default"))) void *dlsym(void *handle, const char *name)
{
if(!strcmp(name, "dlsym"))
return (void *)&dlsym;
if(!strcmp(name, "dlopen") && realdlopen)
return (void *)&dlopen;
if(realdlsym == NULL)
bootstrap_dlsym();
if(realdlsym == NULL)
{
fprintf(stderr, "Couldn't get onwards dlsym in hooked dlsym\n");
exit(-1);
}
return realdlsym(handle, name);
}
}; // extern "C"
#endif
void plthook_lib(void *handle)
{
plthook_t *plthook = NULL;
// minimal error handling as we can't do much more than log the error, and since this is
// 'best-effort' attempt to hook the unhookable, we just try and allow it to fail.
if(plthook_open_by_handle(&plthook, handle))
return;
plthook_replace(plthook, "dlopen", (void *)dlopen, NULL);
for(FunctionHook &hook : functionHooks)
{
void *orig = NULL;
plthook_replace(plthook, hook.function.c_str(), hook.hook, &orig);
if(hook.orig && *hook.orig == NULL && orig)
*hook.orig = orig;
}
plthook_close(plthook);
}
// multiple libraries names pointing at the same file are declared as hooks
// in this case, if the second version gets loaded or when CheckLoadedLibraries is run,
// hooks are run another time. Avoid this by clearing callbacks of hooks pointing at the same
// library
static void PreventDoubleHook(const void *loadedHandle)
{
for(auto it = libraryHooks.begin(); it != libraryHooks.end(); ++it)
{
rdcstr libName = *it;
// if callbacks are empty, there is no risk of executing anything
if(libraryCallbacks[libName].empty())
continue;
void *handle = realdlopen(libName.c_str(), RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL);
if(handle == loadedHandle)
{
// we have been loaded by a different name, don't run hooks again
libraryCallbacks[libName].clear();
}
}
}
static void CheckLoadedLibraries()
{
// don't process anything if the busy flag was set, otherwise set it ourselves
if(Atomic::CmpExch32(&tlsbusyflag, 0, 1) != 0)
return;
// iterate over the libraries and see which ones are already loaded, process function hooks for
// them and call callbacks.
for(auto it = libraryHooks.begin(); it != libraryHooks.end(); ++it)
{
rdcstr libName = *it;
void *handle = realdlopen(libName.c_str(), RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL);
if(handle)
{
for(FunctionHook &hook : functionHooks)
{
if(hook.orig && *hook.orig == NULL)
*hook.orig = dlsym(handle, hook.function.c_str());
}
rdcarray<FunctionLoadCallback> callbacks;
// don't call callbacks again if the library is dlopen'd again
libraryCallbacks[libName].swap(callbacks);
PreventDoubleHook(handle);
for(FunctionLoadCallback cb : callbacks)
if(cb)
cb(handle);
}
}
// clear any dl errors in case twitchy applications get set off by false positive errors.
dlerror();
// decrement the flag counter
Atomic::Dec32(&tlsbusyflag);
}
void *intercept_dlopen(const char *filename, int flag, void *ret)
{
if(filename == NULL)
return ret;
if(flag & RTLD_DEEPBIND)
plthook_lib(ret);
rdcstr base = get_basename(filename);
for(auto it = libraryHooks.begin(); it != libraryHooks.end(); ++it)
{
const rdcstr &libName = *it;
if(*it == base)
{
RDCDEBUG("Redirecting dlopen to ourselves for %s", filename);
for(FunctionHook &hook : functionHooks)
{
if(hook.orig && *hook.orig == NULL)
*hook.orig = dlsym(ret, hook.function.c_str());
}
rdcarray<FunctionLoadCallback> callbacks;
// don't call callbacks again if the library is dlopen'd again
libraryCallbacks[libName].swap(callbacks);
PreventDoubleHook(ret);
for(FunctionLoadCallback cb : callbacks)
if(cb)
cb(ret);
ret = realdlopen("lib" STRINGIZE(RDOC_BASE_NAME) ".so", flag);
break;
}
}
// this library might depend on one we care about, so check again as we
// did in EndHookRegistration to see if any library has been loaded.
CheckLoadedLibraries();
return ret;
}
void LibraryHooks::ReplayInitialise()
{
realdlopen = (DLOPENPROC)dlsym(RTLD_NEXT, "dlopen");
realfork = (FORKPROC)dlsym(RTLD_NEXT, "fork");
realexecle = (EXECLEPROC)dlsym(RTLD_NEXT, "execle");
realexecve = (EXECVEPROC)dlsym(RTLD_NEXT, "execve");
realexecvpe = (EXECVPEPROC)dlsym(RTLD_NEXT, "execvpe");
}
void LibraryHooks::BeginHookRegistration()
{
realdlopen = (DLOPENPROC)dlsym(RTLD_NEXT, "dlopen");
realfork = (FORKPROC)dlsym(RTLD_NEXT, "fork");
realexecle = (EXECLEPROC)dlsym(RTLD_NEXT, "execle");
realexecve = (EXECVEPROC)dlsym(RTLD_NEXT, "execve");
realexecvpe = (EXECVPEPROC)dlsym(RTLD_NEXT, "execvpe");
}
bool LibraryHooks::Detect(const char *identifier)
{
return dlsym(RTLD_DEFAULT, identifier) != NULL;
}
void LibraryHooks::RemoveHooks()
{
RDCERR("Removing hooks is not possible on this platform");
}
void LibraryHooks::EndHookRegistration()
{
CheckLoadedLibraries();
}
void LibraryHooks::Refresh()
{
// don't need to refresh on linux
}
void LibraryHooks::RegisterFunctionHook(const char *libraryName, const FunctionHook &hook)
{
// we don't use the library name
(void)libraryName;
SCOPED_LOCK(libLock);
functionHooks.push_back(hook);
}
void LibraryHooks::RegisterLibraryHook(char const *name, FunctionLoadCallback cb)
{
SCOPED_LOCK(libLock);
if(!libraryHooks.contains(name))
libraryHooks.push_back(name);
if(cb)
libraryCallbacks[name].push_back(cb);
}
void LibraryHooks::IgnoreLibrary(const char *libraryName)
{
}
// android only hooking functions, not used on linux
ScopedSuppressHooking::ScopedSuppressHooking()
{
}
ScopedSuppressHooking::~ScopedSuppressHooking()
{
}
+44
View File
@@ -0,0 +1,44 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2023 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "os/os_specific.h"
#include "os/posix/posix_network.h"
namespace Network
{
void SocketPostSend()
{
// only needed for awful hack on Android
}
uint32_t Socket::GetRemoteIP() const
{
return GetIPFromTCPSocket((int)socket);
}
Socket *CreateServerSocket(const rdcstr &bindaddr, uint16_t port, int queuesize)
{
return CreateTCPServerSocket(bindaddr, port, queuesize);
}
};
@@ -24,8 +24,11 @@
#include <dlfcn.h> // for dlsym
#include <stdlib.h>
#include <sys/sysctl.h>
// clang-format off
// sys/types.h needs to be included before sys/sysctl.h
#include <sys/types.h>
#include <sys/sysctl.h>
// clang-format on
#include <sys/user.h>
#include <unistd.h>
#include "common/common.h"
+809
View File
@@ -0,0 +1,809 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2023 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include <ctype.h>
#include <dlfcn.h>
#include <errno.h>
#include <iconv.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <time.h>
#include <unistd.h>
#include <set>
#include "api/app/renderdoc_app.h"
#include "api/replay/replay_enums.h"
#include "common/common.h"
#include "common/threading.h"
#include "os/os_specific.h"
#include "strings/string_utils.h"
#if ENABLED(RDOC_XLIB)
#include <X11/Xlib.h>
#include <X11/keysym.h>
#else
typedef struct _XDisplay Display;
#endif
#if ENABLED(RDOC_XCB)
#include <X11/keysym.h>
#include <xcb/xcb_keysyms.h>
#else
struct xcb_connection_t;
#endif
#if ENABLED(RDOC_WAYLAND)
#include <linux/input.h>
#include <wayland-client.h>
#include <map>
#else
struct wl_display;
struct wl_surface;
#endif
namespace Keyboard
{
void Init()
{
}
#if ENABLED(RDOC_XLIB)
Display *CurrentXDisplay = NULL;
void UseXlibDisplay(Display *dpy)
{
if(CurrentXDisplay || dpy == NULL)
return;
CurrentXDisplay = XOpenDisplay(XDisplayString(dpy));
}
bool HasXlibInput()
{
return CurrentXDisplay != NULL;
}
bool GetXlibKeyState(int key)
{
if(CurrentXDisplay == NULL)
return false;
KeySym ks = 0;
if(key >= eRENDERDOC_Key_A && key <= eRENDERDOC_Key_Z)
ks = key;
if(key >= eRENDERDOC_Key_0 && key <= eRENDERDOC_Key_9)
ks = key;
switch(key)
{
case eRENDERDOC_Key_Divide: ks = XK_KP_Divide; break;
case eRENDERDOC_Key_Multiply: ks = XK_KP_Multiply; break;
case eRENDERDOC_Key_Subtract: ks = XK_KP_Subtract; break;
case eRENDERDOC_Key_Plus: ks = XK_KP_Add; break;
case eRENDERDOC_Key_F1: ks = XK_F1; break;
case eRENDERDOC_Key_F2: ks = XK_F2; break;
case eRENDERDOC_Key_F3: ks = XK_F3; break;
case eRENDERDOC_Key_F4: ks = XK_F4; break;
case eRENDERDOC_Key_F5: ks = XK_F5; break;
case eRENDERDOC_Key_F6: ks = XK_F6; break;
case eRENDERDOC_Key_F7: ks = XK_F7; break;
case eRENDERDOC_Key_F8: ks = XK_F8; break;
case eRENDERDOC_Key_F9: ks = XK_F9; break;
case eRENDERDOC_Key_F10: ks = XK_F10; break;
case eRENDERDOC_Key_F11: ks = XK_F11; break;
case eRENDERDOC_Key_F12: ks = XK_F12; break;
case eRENDERDOC_Key_Home: ks = XK_Home; break;
case eRENDERDOC_Key_End: ks = XK_End; break;
case eRENDERDOC_Key_Insert: ks = XK_Insert; break;
case eRENDERDOC_Key_Delete: ks = XK_Delete; break;
case eRENDERDOC_Key_PageUp: ks = XK_Prior; break;
case eRENDERDOC_Key_PageDn: ks = XK_Next; break;
case eRENDERDOC_Key_Backspace: ks = XK_BackSpace; break;
case eRENDERDOC_Key_Tab: ks = XK_Tab; break;
case eRENDERDOC_Key_PrtScrn: ks = XK_Print; break;
case eRENDERDOC_Key_Pause: ks = XK_Pause; break;
default: break;
}
if(ks == 0)
return false;
KeyCode kc = XKeysymToKeycode(CurrentXDisplay, ks);
char keyState[32];
XQueryKeymap(CurrentXDisplay, keyState);
int byteIdx = (kc / 8);
int bitMask = 1 << (kc % 8);
uint8_t keyByte = (uint8_t)keyState[byteIdx];
return (keyByte & bitMask) != 0;
}
#else
// if RENDERDOC_WINDOWING_XLIB is not enabled
void UseXlibDisplay(Display *dpy)
{
}
bool HasXlibInput()
{
return false;
}
bool GetXlibKeyState(int key)
{
return false;
}
#endif
#if ENABLED(RDOC_XCB)
xcb_connection_t *connection;
xcb_key_symbols_t *symbols;
void UseXcbConnection(xcb_connection_t *conn)
{
connection = conn;
symbols = xcb_key_symbols_alloc(conn);
}
bool HasXCBInput()
{
return symbols != NULL;
}
bool GetXCBKeyState(int key)
{
if(symbols == NULL)
return false;
xcb_keysym_t ks = 0;
if(key >= eRENDERDOC_Key_A && key <= eRENDERDOC_Key_Z)
ks = key;
if(key >= eRENDERDOC_Key_0 && key <= eRENDERDOC_Key_9)
ks = key;
switch(key)
{
case eRENDERDOC_Key_Divide: ks = XK_KP_Divide; break;
case eRENDERDOC_Key_Multiply: ks = XK_KP_Multiply; break;
case eRENDERDOC_Key_Subtract: ks = XK_KP_Subtract; break;
case eRENDERDOC_Key_Plus: ks = XK_KP_Add; break;
case eRENDERDOC_Key_F1: ks = XK_F1; break;
case eRENDERDOC_Key_F2: ks = XK_F2; break;
case eRENDERDOC_Key_F3: ks = XK_F3; break;
case eRENDERDOC_Key_F4: ks = XK_F4; break;
case eRENDERDOC_Key_F5: ks = XK_F5; break;
case eRENDERDOC_Key_F6: ks = XK_F6; break;
case eRENDERDOC_Key_F7: ks = XK_F7; break;
case eRENDERDOC_Key_F8: ks = XK_F8; break;
case eRENDERDOC_Key_F9: ks = XK_F9; break;
case eRENDERDOC_Key_F10: ks = XK_F10; break;
case eRENDERDOC_Key_F11: ks = XK_F11; break;
case eRENDERDOC_Key_F12: ks = XK_F12; break;
case eRENDERDOC_Key_Home: ks = XK_Home; break;
case eRENDERDOC_Key_End: ks = XK_End; break;
case eRENDERDOC_Key_Insert: ks = XK_Insert; break;
case eRENDERDOC_Key_Delete: ks = XK_Delete; break;
case eRENDERDOC_Key_PageUp: ks = XK_Prior; break;
case eRENDERDOC_Key_PageDn: ks = XK_Next; break;
case eRENDERDOC_Key_Backspace: ks = XK_BackSpace; break;
case eRENDERDOC_Key_Tab: ks = XK_Tab; break;
case eRENDERDOC_Key_PrtScrn: ks = XK_Print; break;
case eRENDERDOC_Key_Pause: ks = XK_Pause; break;
default: break;
}
if(ks == 0)
return false;
xcb_keycode_t *keyCodes = xcb_key_symbols_get_keycode(symbols, ks);
if(!keyCodes)
return false;
xcb_query_keymap_cookie_t keymapcookie = xcb_query_keymap(connection);
xcb_query_keymap_reply_t *keys = xcb_query_keymap_reply(connection, keymapcookie, NULL);
bool ret = false;
if(keys && keyCodes[0] != XCB_NO_SYMBOL)
{
int byteIdx = (keyCodes[0] / 8);
int bitMask = 1 << (keyCodes[0] % 8);
ret = (keys->keys[byteIdx] & bitMask) != 0;
}
free(keyCodes);
free(keys);
return ret;
}
#else
// if RENDERDOC_WINDOWING_XCB is not enabled
void UseXcbConnection(xcb_connection_t *conn)
{
}
bool GetXCBKeyState(int key)
{
return false;
}
bool HasXCBInput()
{
return false;
}
#endif
#if ENABLED(RDOC_WAYLAND)
#include <wayland-client.h>
std::set<wl_display *> displays;
std::set<wl_surface *> surfaces;
std::map<rdcpair<wl_registry *, uint32_t>, wl_seat *> seatNames;
std::map<wl_seat *, wl_keyboard *> seatKeyboard;
bool inFocus = false;
Threading::CriticalSection waylandLock;
bool keyState[eRENDERDOC_Key_Max] = {};
void WaylandKeymapDummy(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size)
{
}
void WaylandModifiersDummy(void *data, wl_keyboard *keyboard, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
uint32_t group)
{
}
void WaylandRepeatInfoDummy(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay)
{
}
void WaylandEnter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surf, wl_array *keys)
{
SCOPED_LOCK(waylandLock);
inFocus = surfaces.find(surf) != surfaces.end();
RDCEraseEl(keyState);
}
void WaylandLeave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surf)
{
SCOPED_LOCK(waylandLock);
inFocus = false;
RDCEraseEl(keyState);
}
void WaylandKeypress(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time,
uint32_t key, uint32_t state)
{
int keyIdx = -1;
switch(key)
{
case KEY_0: keyIdx = eRENDERDOC_Key_0; break;
case KEY_1: keyIdx = eRENDERDOC_Key_1; break;
case KEY_2: keyIdx = eRENDERDOC_Key_2; break;
case KEY_3: keyIdx = eRENDERDOC_Key_3; break;
case KEY_4: keyIdx = eRENDERDOC_Key_4; break;
case KEY_5: keyIdx = eRENDERDOC_Key_5; break;
case KEY_6: keyIdx = eRENDERDOC_Key_6; break;
case KEY_7: keyIdx = eRENDERDOC_Key_7; break;
case KEY_8: keyIdx = eRENDERDOC_Key_8; break;
case KEY_9: keyIdx = eRENDERDOC_Key_9; break;
case KEY_A: keyIdx = eRENDERDOC_Key_A; break;
case KEY_B: keyIdx = eRENDERDOC_Key_B; break;
case KEY_C: keyIdx = eRENDERDOC_Key_C; break;
case KEY_D: keyIdx = eRENDERDOC_Key_D; break;
case KEY_E: keyIdx = eRENDERDOC_Key_E; break;
case KEY_F: keyIdx = eRENDERDOC_Key_F; break;
case KEY_G: keyIdx = eRENDERDOC_Key_G; break;
case KEY_H: keyIdx = eRENDERDOC_Key_H; break;
case KEY_I: keyIdx = eRENDERDOC_Key_I; break;
case KEY_J: keyIdx = eRENDERDOC_Key_J; break;
case KEY_K: keyIdx = eRENDERDOC_Key_K; break;
case KEY_L: keyIdx = eRENDERDOC_Key_L; break;
case KEY_M: keyIdx = eRENDERDOC_Key_M; break;
case KEY_N: keyIdx = eRENDERDOC_Key_N; break;
case KEY_O: keyIdx = eRENDERDOC_Key_O; break;
case KEY_P: keyIdx = eRENDERDOC_Key_P; break;
case KEY_Q: keyIdx = eRENDERDOC_Key_Q; break;
case KEY_R: keyIdx = eRENDERDOC_Key_R; break;
case KEY_S: keyIdx = eRENDERDOC_Key_S; break;
case KEY_T: keyIdx = eRENDERDOC_Key_T; break;
case KEY_U: keyIdx = eRENDERDOC_Key_U; break;
case KEY_V: keyIdx = eRENDERDOC_Key_V; break;
case KEY_W: keyIdx = eRENDERDOC_Key_W; break;
case KEY_X: keyIdx = eRENDERDOC_Key_X; break;
case KEY_Y: keyIdx = eRENDERDOC_Key_Y; break;
case KEY_Z: keyIdx = eRENDERDOC_Key_Z; break;
case KEY_KPSLASH: keyIdx = eRENDERDOC_Key_Divide; break;
case KEY_KPASTERISK: keyIdx = eRENDERDOC_Key_Multiply; break;
case KEY_KPMINUS: keyIdx = eRENDERDOC_Key_Subtract; break;
case KEY_KPPLUS: keyIdx = eRENDERDOC_Key_Plus; break;
case KEY_F1: keyIdx = eRENDERDOC_Key_F1; break;
case KEY_F2: keyIdx = eRENDERDOC_Key_F2; break;
case KEY_F3: keyIdx = eRENDERDOC_Key_F3; break;
case KEY_F4: keyIdx = eRENDERDOC_Key_F4; break;
case KEY_F5: keyIdx = eRENDERDOC_Key_F5; break;
case KEY_F6: keyIdx = eRENDERDOC_Key_F6; break;
case KEY_F7: keyIdx = eRENDERDOC_Key_F7; break;
case KEY_F8: keyIdx = eRENDERDOC_Key_F8; break;
case KEY_F9: keyIdx = eRENDERDOC_Key_F9; break;
case KEY_F10: keyIdx = eRENDERDOC_Key_F10; break;
case KEY_F11: keyIdx = eRENDERDOC_Key_F11; break;
case KEY_F12: keyIdx = eRENDERDOC_Key_F12; break;
case KEY_HOME: keyIdx = eRENDERDOC_Key_Home; break;
case KEY_END: keyIdx = eRENDERDOC_Key_End; break;
case KEY_INSERT: keyIdx = eRENDERDOC_Key_Insert; break;
case KEY_DELETE: keyIdx = eRENDERDOC_Key_Delete; break;
case KEY_PAGEUP: keyIdx = eRENDERDOC_Key_PageUp; break;
case KEY_PAGEDOWN: keyIdx = eRENDERDOC_Key_PageDn; break;
case KEY_BACKSPACE: keyIdx = eRENDERDOC_Key_Backspace; break;
case KEY_TAB: keyIdx = eRENDERDOC_Key_Tab; break;
case KEY_SYSRQ: keyIdx = eRENDERDOC_Key_PrtScrn; break;
case KEY_PAUSE: keyIdx = eRENDERDOC_Key_Pause; break;
}
if(keyIdx < 0)
return;
{
SCOPED_LOCK(waylandLock);
keyState[keyIdx] = (state == WL_KEYBOARD_KEY_STATE_PRESSED);
}
}
void WaylandSeatCaps(void *data, wl_seat *seat, uint32_t capabilities)
{
if(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)
{
{
SCOPED_LOCK(waylandLock);
if(seatKeyboard[seat])
return;
}
wl_keyboard *keyboard = wl_seat_get_keyboard(seat);
static const wl_keyboard_listener listener = {
WaylandKeymapDummy, WaylandEnter, WaylandLeave,
WaylandKeypress, WaylandModifiersDummy, WaylandRepeatInfoDummy,
};
wl_keyboard_add_listener(keyboard, &listener, NULL);
{
SCOPED_LOCK(waylandLock);
seatKeyboard[seat] = keyboard;
}
}
else
{
wl_keyboard *keyboard = NULL;
{
SCOPED_LOCK(waylandLock);
keyboard = seatKeyboard[seat];
if(!keyboard)
return;
seatKeyboard[seat] = NULL;
}
wl_keyboard_destroy(keyboard);
}
}
void WaylandRegistryAdd(void *data, wl_registry *reg, uint32_t name, const char *iface,
uint32_t version)
{
if(!strcmp(iface, "wl_seat"))
{
wl_seat *seat = (wl_seat *)wl_registry_bind(reg, name, &wl_seat_interface, 1);
static const wl_seat_listener listener = {&WaylandSeatCaps};
wl_seat_add_listener(seat, &listener, NULL);
{
SCOPED_LOCK(waylandLock);
seatNames[{reg, name}] = seat;
}
}
}
void WaylandRegistryRemove(void *data, wl_registry *reg, uint32_t name)
{
SCOPED_LOCK(waylandLock);
auto it = seatNames.find({reg, name});
if(it != seatNames.end())
{
wl_seat_destroy(it->second);
seatNames.erase(it);
}
}
void UseWaylandDisplay(wl_display *disp)
{
// only listen to each display once at most
{
SCOPED_LOCK(waylandLock);
if(displays.find(disp) != displays.end())
return;
displays.insert(disp);
}
static const wl_registry_listener listener = {&WaylandRegistryAdd, &WaylandRegistryRemove};
// get the registry and listen to it. This will then let us find seats
wl_registry_add_listener(wl_display_get_registry(disp), &listener, NULL);
}
void AddWaylandInputWindow(wl_surface *wnd)
{
SCOPED_LOCK(waylandLock);
surfaces.insert(wnd);
}
void RemoveWaylandInputWindow(wl_surface *wnd)
{
SCOPED_LOCK(waylandLock);
surfaces.erase(wnd);
}
bool HasWaylandInput()
{
SCOPED_LOCK(waylandLock);
return !displays.empty();
}
bool GetWaylandKeyState(int key)
{
SCOPED_LOCK(waylandLock);
return keyState[key];
}
#else
void UseWaylandDisplay(wl_display *disp)
{
}
void AddWaylandInputWindow(wl_surface *wnd)
{
}
void RemoveWaylandInputWindow(wl_surface *wnd)
{
}
bool HasWaylandInput()
{
return false;
}
bool GetWaylandKeyState(int key)
{
return false;
}
#endif
WindowingSystem UseUnknownDisplay(void *disp)
{
if(disp == NULL)
return WindowingSystem::Unknown;
// could be wayland or xlib, try to detect.
// both Display* and wl_display* are valid pointers, so dereference and read the first pointer
// sized bytes
void *firstPointer = NULL;
memcpy(&firstPointer, disp, sizeof(void *));
// in a Display* we don't know what this contains, but in a wl_display it should point to the
// wl_display_interface exported symbol. Check with dladdr
Dl_info info;
if(dladdr(firstPointer, &info) && !strcmp(info.dli_sname, "wl_display_interface"))
{
UseWaylandDisplay((wl_display *)disp);
return WindowingSystem::Wayland;
}
else
{
UseXlibDisplay((Display *)disp);
return WindowingSystem::Xlib;
}
}
void AddInputWindow(WindowingSystem windowSystem, void *wnd)
{
if(windowSystem == WindowingSystem::Wayland)
{
AddWaylandInputWindow((wl_surface *)wnd);
}
else
{
// TODO check against this drawable & parent window being focused in GetKeyState
}
}
void RemoveInputWindow(WindowingSystem windowSystem, void *wnd)
{
if(windowSystem == WindowingSystem::Wayland)
{
RemoveWaylandInputWindow((wl_surface *)wnd);
}
}
bool PlatformHasKeyInput()
{
return HasXCBInput() || HasXlibInput() || HasWaylandInput();
}
bool GetKeyState(int key)
{
return GetXCBKeyState(key) || GetXlibKeyState(key) || GetWaylandKeyState(key);
}
}
namespace FileIO
{
rdcstr GetTempRootPath()
{
return "/tmp";
}
rdcstr GetAppFolderFilename(const rdcstr &filename)
{
passwd *pw = getpwuid(getuid());
rdcstr homedir = pw ? pw->pw_dir : "";
if(homedir.empty())
homedir = Process::GetEnvVariable("HOME");
if(homedir.empty())
{
RDCERR("Can't get HOME directory, defaulting to '/' instead");
homedir = "";
}
rdcstr ret = homedir + "/.renderdoc/";
mkdir(ret.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
return ret + filename;
}
rdcstr DefaultFindFileInPath(const rdcstr &fileName);
rdcstr FindFileInPath(const rdcstr &fileName)
{
return DefaultFindFileInPath(fileName);
}
void GetExecutableFilename(rdcstr &selfName)
{
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, getpid()};
char path[512] = {0};
size_t size = sizeof(path);
if(sysctl(mib, ARRAY_COUNT(mib), path, &size, NULL, 0) != 0)
{
selfName = "/unknown/unknown";
RDCERR("Can't get executable name");
return; // don't try and readlink this
}
selfName = rdcstr(path);
memset(path, 0, sizeof(path));
readlink(selfName.c_str(), path, 511);
if(path[0] != 0)
selfName = rdcstr(path);
}
int LibraryLocator = 42;
void GetLibraryFilename(rdcstr &selfName)
{
Dl_info info;
if(dladdr(&LibraryLocator, &info))
{
selfName = info.dli_fname;
}
else
{
RDCERR("dladdr failed to get library path");
selfName = "";
}
}
};
namespace StringFormat
{
// cache iconv_t descriptor to save on iconv_open/iconv_close each time
iconv_t iconvWide2UTF8 = (iconv_t)-1;
iconv_t iconvUTF82Wide = (iconv_t)-1;
// iconv is not thread safe when sharing an iconv_t descriptor
// I don't expect much contention but if it happens we could TryLock
// before creating a temporary iconv_t, or hold two iconv_ts, or something.
Threading::CriticalSection iconvLock;
void Shutdown()
{
SCOPED_LOCK(iconvLock);
if(iconvWide2UTF8 != (iconv_t)-1)
iconv_close(iconvWide2UTF8);
iconvWide2UTF8 = (iconv_t)-1;
if(iconvUTF82Wide != (iconv_t)-1)
iconv_close(iconvUTF82Wide);
iconvUTF82Wide = (iconv_t)-1;
}
rdcstr Wide2UTF8(const rdcwstr &s)
{
// include room for null terminator, assuming unicode input (not ucs)
// utf-8 characters can be max 4 bytes.
size_t len = (s.length() + 1) * 4;
rdcarray<char> charBuffer;
charBuffer.resize(len);
size_t ret;
{
SCOPED_LOCK(iconvLock);
if(iconvWide2UTF8 == (iconv_t)-1)
iconvWide2UTF8 = iconv_open("UTF-8", "WCHAR_T");
if(iconvWide2UTF8 == (iconv_t)-1)
{
RDCERR("Couldn't open iconv for WCHAR_T to UTF-8: %d", errno);
return "";
}
char *inbuf = (char *)s.c_str();
size_t insize = (s.length() + 1) * sizeof(wchar_t); // include null terminator
char *outbuf = &charBuffer[0];
size_t outsize = len;
ret = iconv(iconvWide2UTF8, &inbuf, &insize, &outbuf, &outsize);
}
if(ret == (size_t)-1)
{
#if ENABLED(RDOC_DEVEL)
RDCWARN("Failed to convert wstring");
#endif
return "";
}
// convert to string from null-terminated string - utf-8 never contains
// 0 bytes before the null terminator, and this way we don't care if
// charBuffer is larger than the string
return rdcstr(&charBuffer[0]);
}
rdcwstr UTF82Wide(const rdcstr &s)
{
// include room for null terminator, for ascii input we need at least as many output chars as
// input.
size_t len = s.length() + 1;
rdcarray<wchar_t> wcharBuffer;
wcharBuffer.resize(len);
size_t ret;
{
SCOPED_LOCK(iconvLock);
if(iconvUTF82Wide == (iconv_t)-1)
iconvUTF82Wide = iconv_open("WCHAR_T", "UTF-8");
if(iconvUTF82Wide == (iconv_t)-1)
{
RDCERR("Couldn't open iconv for UTF-8 to WCHAR_T: %d", errno);
return L"";
}
char *inbuf = (char *)s.c_str();
size_t insize = s.length() + 1; // include null terminator
char *outbuf = (char *)&wcharBuffer[0];
size_t outsize = len * sizeof(wchar_t);
ret = iconv(iconvUTF82Wide, &inbuf, &insize, &outbuf, &outsize);
}
if(ret == (size_t)-1)
{
#if ENABLED(RDOC_DEVEL)
RDCWARN("Failed to convert wstring");
#endif
return L"";
}
// convert to string from null-terminated string
return rdcwstr(&wcharBuffer[0]);
}
};
namespace OSUtility
{
void WriteOutput(int channel, const char *str)
{
if(channel == OSUtility::Output_StdOut)
{
fprintf(stdout, "%s", str);
fflush(stdout);
}
else if(channel == OSUtility::Output_StdErr)
{
fprintf(stderr, "%s", str);
fflush(stderr);
}
}
uint64_t GetMachineIdent()
{
uint64_t ret = MachineIdent_Linux;
#if defined(_M_ARM) || defined(__arm__)
ret |= MachineIdent_Arch_ARM;
#else
ret |= MachineIdent_Arch_x86;
#endif
#if ENABLED(RDOC_X64)
ret |= MachineIdent_64bit;
#else
ret |= MachineIdent_32bit;
#endif
return ret;
}
};
+44
View File
@@ -0,0 +1,44 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2023 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "os/os_specific.h"
#include <time.h>
#include <unistd.h>
double Timing::GetTickFrequency()
{
return 1000000.0;
}
uint64_t Timing::GetTick()
{
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return uint64_t(ts.tv_sec) * 1000000000ULL + uint32_t(ts.tv_nsec & 0xffffffff);
}
void Threading::SetCurrentThreadName(const rdcstr &name)
{
}
+2 -2
View File
@@ -66,11 +66,11 @@ private:
{
void *addrs_ptr[ARRAY_COUNT(addrs)];
size_t ret = backtrace(addrs_ptr, ARRAY_COUNT(addrs));
int ret = backtrace(addrs_ptr, ARRAY_COUNT(addrs));
numLevels = 0;
if(ret > 0)
numLevels = ret;
numLevels = (size_t)ret;
int offs = 0;
// if we want to trim levels of the stack, we can do that here
-6
View File
@@ -38,11 +38,7 @@
Threading::CriticalSection libLock;
#ifdef __linux__
RDOC_EXTERN_CONFIG(bool, Linux_Debug_PtraceLogging);
#else
#define Linux_Debug_PtraceLogging() false
#endif
static std::map<rdcstr, rdcarray<FunctionLoadCallback>> libraryCallbacks;
static rdcarray<rdcstr> libraryHooks;
@@ -120,7 +116,6 @@ int direct_setenv(const char *name, const char *value, int overwrite);
// The other variants all forward to one of those - the 'l' cases unroll the va_args first before
// calling onwards
#ifndef __FreeBSD__ // TODO: environ bug
#define GET_EXECL_PARAMS(has_e) \
va_list args; \
va_start(args, arg); \
@@ -199,7 +194,6 @@ __attribute__((visibility("default"))) int execvp(const char *pathname, char *co
return execvpe(pathname, argv, environ);
}
#endif // __FreeBSD__
__attribute__((visibility("default"))) int execve(const char *pathname, char *const argv[],
char *const envp[])
@@ -24,9 +24,7 @@
#include "os/os_specific.h"
#ifdef __linux__
#include <sys/prctl.h>
#endif
#include <time.h>
#include <unistd.h>
@@ -44,7 +42,5 @@ uint64_t Timing::GetTick()
void Threading::SetCurrentThreadName(const rdcstr &name)
{
#ifdef __linux__
prctl(PR_SET_NAME, (unsigned long)name.c_str(), 0, 0, 0);
#endif
}
+1 -1
View File
@@ -50,7 +50,7 @@ elseif(ENABLE_GGP)
elseif(UNIX)
list(APPEND sources renderdoccmd_linux.cpp)
if(FreeBSD)
if(FREEBSD)
list(APPEND libraries PRIVATE -L/usr/local/lib)
list(APPEND includes /usr/local/include)
endif()