slackware-current/source/xap/ssr/0010-plthook.patch
Patrick J Volkerding 3e4363cb32 Tue Aug 13 20:33:27 UTC 2024
ap/moc-2.6_alpha3-x86_64-4.txz:  Rebuilt.
  The ffmpeg7 patch isn't 100% there, so rebuild without the ffmpeg plugin.
ap/sqlite-3.46.1-x86_64-1.txz:  Upgraded.
kde/digikam-8.4.0-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
kde/ffmpegthumbs-23.08.5-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
kde/k3b-23.08.5-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
kde/kfilemetadata-5.116.0-x86_64-6.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
kde/kpipewire-5.27.11-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
l/alsa-plugins-1.2.12-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
l/ffmpeg-7.0.2-x86_64-1.txz:  Upgraded.
  Shared library .so-version bump.
  Compiled against vulkan-sdk-1.3.290.0.
l/freetype-2.13.3-x86_64-1.txz:  Upgraded.
l/gegl-0.4.48-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
l/gst-plugins-bad-free-1.24.6-x86_64-2.txz:  Rebuilt.
  Recompiled against vulkan-sdk-1.3.290.0.
l/gst-plugins-libav-1.24.6-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
l/gtk4-4.14.4-x86_64-2.txz:  Rebuilt.
  Recompiled against vulkan-sdk-1.3.290.0.
l/libplacebo-7.349.0-x86_64-2.txz:  Rebuilt.
  Recompiled against vulkan-sdk-1.3.290.0.
l/mlt-7.24.0-x86_64-3.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
l/opencv-4.10.0-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
l/pipewire-1.2.2-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
  Recompiled against vulkan-sdk-1.3.290.0.
l/qt5-5.15.14_20240716_ae0c8451-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
l/qt6-6.7.2_20240610_3f005f1e-x86_64-5.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
l/spirv-llvm-translator-18.1.3-x86_64-2.txz:  Rebuilt.
  Recompiled against vulkan-sdk-1.3.290.0.
x/fcitx5-gtk-5.1.3-x86_64-2.txz:  Rebuilt.
  Recompiled against vulkan-sdk-1.3.290.0.
x/ibus-1.5.30-x86_64-2.txz:  Rebuilt.
  Recompiled against vulkan-sdk-1.3.290.0.
x/mesa-24.1.5-x86_64-2.txz:  Rebuilt.
  Recompiled against vulkan-sdk-1.3.290.0.
x/vulkan-sdk-1.3.290.0-x86_64-1.txz:  Upgraded.
xap/MPlayer-20240812-x86_64-1.txz:  Upgraded.
  Recompiled against ffmpeg-7.0.2.
xap/audacious-plugins-4.4-x86_64-3.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
xap/ffmpegthumbnailer-2.2.2-x86_64-6.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
xap/freerdp-2.11.7-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
xap/mpv-0.38.0-x86_64-5.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
  Recompiled against vulkan-sdk-1.3.290.0.
xap/ssr-0.4.4-x86_64-3.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
xap/xine-lib-1.2.13-x86_64-8.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
xap/xscreensaver-6.09-x86_64-2.txz:  Rebuilt.
  Recompiled against ffmpeg-7.0.2.
2024-08-13 23:09:21 +02:00

2349 lines
74 KiB
Diff

From 83b8f9f5d9c9ab06152657e57f85b6f71954a6b9 Mon Sep 17 00:00:00 2001
From: Maarten Baert <maarten-baert@hotmail.com>
Date: Sun, 1 May 2022 22:48:24 +0200
Bug: https://github.com/MaartenBaert/ssr/issues/947
Bug-Debian: https://bugs.debian.org/1040375
Forwarded: not-needed
Origin: https://github.com/MaartenBaert/ssr/commit/83b8f9f5d9c9ab06152657e57f85b6f71954a6b9
Last-Updated: 2024-05-07
Subject: [PATCH] Switch to PLT hooks to make GLInject work with new
dlopen/dlsym in libc
---
data/resources/about.htm | 4 +-
glinject/CMakeLists.txt | 4 +-
glinject/Hook.cpp | 425 ++++++++++----------
glinject/ShmStructs.h | 2 +
glinject/elfhacks.c | 611 -----------------------------
glinject/elfhacks.h | 213 ----------
glinject/plthook.h | 67 ++++
glinject/plthook_elf.c | 821 +++++++++++++++++++++++++++++++++++++++
src/GUI/PageWelcome.cpp | 4 +-
9 files changed, 1091 insertions(+), 1060 deletions(-)
delete mode 100644 glinject/elfhacks.c
delete mode 100644 glinject/elfhacks.h
create mode 100644 glinject/plthook.h
create mode 100644 glinject/plthook_elf.c
Index: simplescreenrecorder-salsa/data/resources/about.htm
===================================================================
--- simplescreenrecorder-salsa.orig/data/resources/about.htm 2024-05-07 08:50:15.918773109 +0200
+++ simplescreenrecorder-salsa/data/resources/about.htm 2024-05-07 08:50:15.910773036 +0200
@@ -21,8 +21,8 @@
<p>%USES%</p>
<ul>
<li>%USES_QT%
-<li>%USES_LIBAV_FFMPEG%
-<li>%USES_ELFHACKS%
+<li>%USES_FFMPEG%
+<li>%USES_PLTHOOK%
</ul>
<p><i>%VERSIONINFO%</i></p>
Index: simplescreenrecorder-salsa/glinject/CMakeLists.txt
===================================================================
--- simplescreenrecorder-salsa.orig/glinject/CMakeLists.txt 2024-05-07 08:50:15.918773109 +0200
+++ simplescreenrecorder-salsa/glinject/CMakeLists.txt 2024-05-07 08:50:15.910773036 +0200
@@ -5,14 +5,14 @@
find_package(OpenGL REQUIRED)
set(sources
- elfhacks.c
- elfhacks.h
GLInject.cpp
GLInject.h
Global.h
GLXFrameGrabber.cpp
GLXFrameGrabber.h
Hook.cpp
+ plthook_elf.c
+ plthook.h
ShmStructs.h
SSRVideoStreamWriter.cpp
SSRVideoStreamWriter.h
Index: simplescreenrecorder-salsa/glinject/Hook.cpp
===================================================================
--- simplescreenrecorder-salsa.orig/glinject/Hook.cpp 2024-05-07 08:50:15.918773109 +0200
+++ simplescreenrecorder-salsa/glinject/Hook.cpp 2024-05-07 08:50:15.910773036 +0200
@@ -8,152 +8,130 @@
#include "Global.h"
-#include "elfhacks.h"
#include "GLInject.h"
#include "GLXFrameGrabber.h"
+#include "plthook.h"
+
+#include <dlfcn.h>
+#include <link.h>
#include <GL/glx.h>
#include <X11/X.h>
+// global variable from the standard library that holds all environment variables
extern char **environ;
+// return type of glXGetProcAddressARB
typedef void (*GLXextFuncPtr)(void);
-void InitGLInject();
-void FreeGLInject();
+// hook replacement function prototypes
+void* glinject_hook_dlsym(void* handle, const char* symbol);
+void* glinject_hook_dlvsym(void* handle, const char* symbol, const char* version);
+int glinject_hook_execl(const char* filename, const char* arg, ...);
+int glinject_hook_execlp(const char* filename, const char* arg, ...);
+int glinject_hook_execle(const char* filename, const char* arg, ...);
+int glinject_hook_execv(const char* filename, char* const argv[]);
+int glinject_hook_execve(const char* filename, char* const argv[], char* const envp[]);
+int glinject_hook_execvp(const char* filename, char* const argv[]);
+int glinject_hook_execvpe(const char* filename, char* const argv[], char* const envp[]);
+GLXWindow glinject_hook_glXCreateWindow(Display* dpy, GLXFBConfig config, Window win, const int* attrib_list);
+void glinject_hook_glXDestroyWindow(Display* dpy, GLXWindow win);
+int glinject_hook_XDestroyWindow(Display* dpy, Window win);
+void glinject_hook_glXSwapBuffers(Display* dpy, GLXDrawable drawable);
+GLXextFuncPtr glinject_hook_glXGetProcAddressARB(const GLubyte *proc_name);
-GLXWindow glinject_my_glXCreateWindow(Display* dpy, GLXFBConfig config, Window win, const int* attrib_list);
-void glinject_my_glXDestroyWindow(Display* dpy, GLXWindow win);
-int glinject_my_XDestroyWindow(Display* dpy, Window win);
-void glinject_my_glXSwapBuffers(Display* dpy, GLXDrawable drawable);
-GLXextFuncPtr glinject_my_glXGetProcAddressARB(const GLubyte *proc_name);
-int glinject_my_XNextEvent(Display* display, XEvent* event_return);
-
-void *(*g_glinject_real_dlsym)(void*, const char*) = NULL;
-void *(*g_glinject_real_dlvsym)(void*, const char*, const char*) = NULL;
-int (*g_glinject_real_execv)(const char*, char* const*) = NULL;
-int (*g_glinject_real_execve)(const char*, char* const*, char* const*) = NULL;
-int (*g_glinject_real_execvp)(const char*, char* const*) = NULL;
-int (*g_glinject_real_execvpe)(const char*, char* const*, char* const*) = NULL;
-GLXWindow (*g_glinject_real_glXCreateWindow)(Display*, GLXFBConfig, Window, const int*) = NULL;
-void (*g_glinject_real_glXDestroyWindow)(Display*, GLXWindow) = NULL;
-int (*g_glinject_real_XDestroyWindow)(Display*, Window) = NULL;
-void (*g_glinject_real_glXSwapBuffers)(Display*, GLXDrawable) = NULL;
-GLXextFuncPtr (*g_glinject_real_glXGetProcAddressARB)(const GLubyte*) = NULL;
-int (*g_glinject_real_XNextEvent)(Display*, XEvent*) = NULL;
+// hook table
+struct GLInjectHook {
+ const char *name;
+ void *address;
+};
+std::initializer_list<GLInjectHook> glinject_hook_table = {
+ {"dlsym" , (void*) &glinject_hook_dlsym},
+ {"dlvsym" , (void*) &glinject_hook_dlvsym},
+ {"execl" , (void*) &glinject_hook_execl},
+ {"execlp" , (void*) &glinject_hook_execlp},
+ {"execle" , (void*) &glinject_hook_execle},
+ {"execv" , (void*) &glinject_hook_execv},
+ {"execve" , (void*) &glinject_hook_execve},
+ {"execvp" , (void*) &glinject_hook_execvp},
+ {"execvpe" , (void*) &glinject_hook_execvpe},
+ {"glXCreateWindow" , (void*) &glinject_hook_glXCreateWindow},
+ {"glXDestroyWindow" , (void*) &glinject_hook_glXDestroyWindow},
+ {"XDestroyWindow" , (void*) &glinject_hook_XDestroyWindow},
+ {"glXSwapBuffers" , (void*) &glinject_hook_glXSwapBuffers},
+ {"glXGetProcAddressARB", (void*) &glinject_hook_glXGetProcAddressARB},
+};
+// main glinject object and mutex
static GLInject *g_glinject = NULL;
static std::mutex g_glinject_mutex;
-void InitGLInject() {
- std::lock_guard<std::mutex> lock(g_glinject_mutex);
+// hook initializer
+static struct GLInjectHooksInitializer {
+ GLInjectHooksInitializer() {
+
+ // get the link table of the glinject library (we can use any global variable for this)
+ Dl_info glinject_dlinfo;
+ struct link_map *glinject_lmap = NULL;
+ if(dladdr1((void*) &glinject_hook_table, &glinject_dlinfo, (void**) &glinject_lmap, RTLD_DL_LINKMAP) == 0) {
+ GLINJECT_PRINT("Error: Failed to get link map of glinject library!");
+ return;
+ }
- if(g_glinject != NULL)
- return;
+ // replace PLT entries everywhere except in the glinject library
+ void *mainhandle = dlopen(NULL, RTLD_NOW);
+ if(mainhandle == NULL) {
+ GLINJECT_PRINT("Error: Failed to get main program handle!");
+ return;
+ }
+ struct link_map *lmap = NULL;
+ if(dlinfo(mainhandle, RTLD_DI_LINKMAP, &lmap) != 0) {
+ GLINJECT_PRINT("Error: Failed to get link map of main program!");
+ return;
+ }
+ while(lmap) {
+ if(lmap != glinject_lmap) {
+ plthook_t *plthook;
+ if(plthook_open_by_linkmap(&plthook, lmap) == 0) {
+ for(const GLInjectHook &hook : glinject_hook_table) {
+ void *oldfunc;
+ if(plthook_replace(plthook, hook.name, hook.address, &oldfunc) == 0) {
+ GLINJECT_PRINT("Hooked " << hook.name << " PLT entry in '" << lmap->l_name << "'.");
+ }
+ }
+ plthook_close(plthook);
+ }
+ }
+ lmap = lmap->l_next;
+ }
+ dlclose(mainhandle);
- // part 1: get dlsym and dlvsym
- eh_obj_t libdl;
- if(eh_find_obj(&libdl, "*/libdl.so*")) {
- GLINJECT_PRINT("Error: Can't open libdl.so!");
- exit(1);
- }
- if(eh_find_sym(&libdl, "dlsym", (void**) &g_glinject_real_dlsym)) {
- GLINJECT_PRINT("Error: Can't get dlsym address!");
- eh_destroy_obj(&libdl);
- exit(1);
- }
- if(eh_find_sym(&libdl, "dlvsym", (void**) &g_glinject_real_dlvsym)) {
- GLINJECT_PRINT("Error: Can't get dlvsym address!");
- eh_destroy_obj(&libdl);
- exit(1);
- }
- eh_destroy_obj(&libdl);
-
- // part 2: get everything else
- g_glinject_real_execv = (int (*)(const char*, char* const*)) g_glinject_real_dlsym(RTLD_NEXT, "execv");
- if(g_glinject_real_execv == NULL) {
- GLINJECT_PRINT("Error: Can't get execv address!");
- exit(1);
- }
- g_glinject_real_execve = (int (*)(const char*, char* const*, char* const*)) g_glinject_real_dlsym(RTLD_NEXT, "execve");
- if(g_glinject_real_execve == NULL) {
- GLINJECT_PRINT("Error: Can't get execve address!");
- exit(1);
- }
- g_glinject_real_execvp = (int (*)(const char*, char* const*)) g_glinject_real_dlsym(RTLD_NEXT, "execvp");
- if(g_glinject_real_execvp == NULL) {
- GLINJECT_PRINT("Error: Can't get execvp address!");
- exit(1);
- }
- g_glinject_real_execvpe = (int (*)(const char*, char* const*, char* const*)) g_glinject_real_dlsym(RTLD_NEXT, "execvpe");
- if(g_glinject_real_execvpe == NULL) {
- GLINJECT_PRINT("Error: Can't get execvpe address!");
- exit(1);
- }
- g_glinject_real_glXCreateWindow = (GLXWindow (*)(Display*, GLXFBConfig, Window, const int*)) g_glinject_real_dlsym(RTLD_NEXT, "glXCreateWindow");
- if(g_glinject_real_glXCreateWindow == NULL) {
- GLINJECT_PRINT("Error: Can't get glXCreateWindow address!");
- exit(1);
- }
- g_glinject_real_glXDestroyWindow = (void (*)(Display*, GLXWindow)) g_glinject_real_dlsym(RTLD_NEXT, "glXDestroyWindow");
- if(g_glinject_real_glXDestroyWindow == NULL) {
- GLINJECT_PRINT("Error: Can't get glXDestroyWindow address!");
- exit(1);
- }
- g_glinject_real_XDestroyWindow = (int (*)(Display*, Window)) g_glinject_real_dlsym(RTLD_NEXT, "XDestroyWindow");
- if(g_glinject_real_XDestroyWindow == NULL) {
- GLINJECT_PRINT("Error: Can't get XDestroyWindow address!");
- exit(1);
- }
- g_glinject_real_glXSwapBuffers = (void (*)(Display*, GLXDrawable)) g_glinject_real_dlsym(RTLD_NEXT, "glXSwapBuffers");
- if(g_glinject_real_glXSwapBuffers == NULL) {
- GLINJECT_PRINT("Error: Can't get glXSwapBuffers address!");
- exit(1);
- }
- g_glinject_real_glXGetProcAddressARB = (GLXextFuncPtr (*)(const GLubyte*)) g_glinject_real_dlsym(RTLD_NEXT, "glXGetProcAddressARB");
- if(g_glinject_real_glXGetProcAddressARB == NULL) {
- GLINJECT_PRINT("Error: Can't get glXGetProcAddressARB address!");
- exit(1);
- }
- g_glinject_real_XNextEvent = (int (*)(Display*, XEvent*)) g_glinject_real_dlsym(RTLD_NEXT, "XNextEvent");
- if(g_glinject_real_XNextEvent == NULL) {
- GLINJECT_PRINT("Error: Can't get XNextEvent address!");
- exit(1);
}
+} glinject_hooks_initializer;
- g_glinject = new GLInject();
-
- atexit(FreeGLInject);
+void GLInjectInit();
+void GLInjectFree();
+void GLInjectInit() {
+ if(g_glinject != NULL)
+ return;
+ g_glinject = new GLInject();
+ atexit(GLInjectFree);
}
-void FreeGLInject() {
- std::lock_guard<std::mutex> lock(g_glinject_mutex);
+void GLInjectFree() {
if(g_glinject != NULL) {
delete g_glinject;
g_glinject = NULL;
}
}
-struct Hook {
- const char *name;
- void *address;
-};
-static Hook hook_table[] = {
- {"glXCreateWindow" , (void*) &glinject_my_glXCreateWindow},
- {"glXDestroyWindow" , (void*) &glinject_my_glXDestroyWindow},
- {"XDestroyWindow" , (void*) &glinject_my_XDestroyWindow},
- {"glXSwapBuffers" , (void*) &glinject_my_glXSwapBuffers},
- {"glXGetProcAddressARB", (void*) &glinject_my_glXGetProcAddressARB},
- {"XNextEvent" , (void*) &glinject_my_XNextEvent},
-};
-static const char* exec_blacklist[] = {
- "ping",
- "/bin/ping",
- "/usr/bin/ping",
-};
-
void FilterEnviron(const char* filename, std::vector<char*>* out, char* const* in) {
+ const char* exec_blacklist[] = {
+ "ping",
+ "/bin/ping",
+ "/usr/bin/ping",
+ };
bool filter = false;
for(unsigned int i = 0; i < sizeof(exec_blacklist) / sizeof(const char*); ++i) {
if(strcmp(exec_blacklist[i], filename) == 0) {
@@ -169,90 +147,35 @@
out->push_back(NULL);
}
-GLXWindow glinject_my_glXCreateWindow(Display* dpy, GLXFBConfig config, Window win, const int* attrib_list) {
- GLXWindow res = g_glinject_real_glXCreateWindow(dpy, config, win, attrib_list);
- if(res == 0)
- return 0;
- std::lock_guard<std::mutex> lock(g_glinject_mutex);
- g_glinject->NewGLXFrameGrabber(dpy, win, res);
- return res;
-}
-
-void glinject_my_glXDestroyWindow(Display* dpy, GLXWindow win) {
- g_glinject_real_glXDestroyWindow(dpy, win);
- std::lock_guard<std::mutex> lock(g_glinject_mutex);
- g_glinject->DeleteGLXFrameGrabberByDrawable(dpy, win);
-}
-
-int glinject_my_XDestroyWindow(Display* dpy, Window win) {
- int res = g_glinject_real_XDestroyWindow(dpy, win);
- std::lock_guard<std::mutex> lock(g_glinject_mutex);
- g_glinject->DeleteGLXFrameGrabberByWindow(dpy, win);
- return res;
-}
-
-void glinject_my_glXSwapBuffers(Display* dpy, GLXDrawable drawable) {
- {
- std::lock_guard<std::mutex> lock(g_glinject_mutex);
- GLXFrameGrabber *fg = g_glinject->FindGLXFrameGrabber(dpy, drawable);
- if(fg == NULL) {
- GLINJECT_PRINT("Warning: glXSwapBuffers called without existing frame grabber, creating one assuming window == drawable.");
- fg = g_glinject->NewGLXFrameGrabber(dpy, drawable, drawable);
- }
- fg->GrabFrame();
- }
- g_glinject_real_glXSwapBuffers(dpy, drawable);
-}
-
-GLXextFuncPtr glinject_my_glXGetProcAddressARB(const GLubyte *proc_name) {
- for(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) {
- if(strcmp(hook_table[i].name, (const char*) proc_name) == 0) {
- std::lock_guard<std::mutex> lock(g_glinject_mutex);
- GLINJECT_PRINT("Hooked: glXGetProcAddressARB(" << proc_name << ").");
- return (GLXextFuncPtr) hook_table[i].address;
- }
- }
- return g_glinject_real_glXGetProcAddressARB(proc_name);
-}
-
-int glinject_my_XNextEvent(Display* display, XEvent* event_return) {
- int res = g_glinject_real_XNextEvent(display, event_return);
- /*std::lock_guard<std::mutex> lock(g_glinject_mutex);
- if(g_hotkey_info.enabled && event_return->type == KeyPress && event_return->xkey.keycode == g_hotkey_info.keycode
- && (event_return->xkey.state & ~LockMask & ~Mod2Mask) == g_hotkey_info.modifiers) {
- g_hotkey_pressed = true;
- }*/
- return res;
-}
-
-// override existing functions
-
-extern "C" void* dlsym(void* handle, const char* symbol) {
- InitGLInject();
- for(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) {
- if(strcmp(hook_table[i].name, symbol) == 0) {
+void* glinject_hook_dlsym(void* handle, const char* symbol) {
+ const char *str = "(In glinject_hook_dlsym)\n";
+ write(2, str, strlen(str));
+ for(const GLInjectHook &hook : glinject_hook_table) {
+ if(strcmp(hook.name, symbol) == 0) {
std::lock_guard<std::mutex> lock(g_glinject_mutex);
- GLINJECT_PRINT("Hooked: dlsym(" << symbol << ").");
- return hook_table[i].address;
+ GLINJECT_PRINT("Hooked dlsym(" << symbol << ").");
+ return hook.address;
}
}
- return g_glinject_real_dlsym(handle, symbol);
+ return dlsym(handle, symbol);
}
-extern "C" void* dlvsym(void* handle, const char* symbol, const char* version) {
- InitGLInject();
- for(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) {
- if(strcmp(hook_table[i].name, symbol) == 0) {
+void* glinject_hook_dlvsym(void* handle, const char* symbol, const char* version) {
+ const char *str = "(In glinject_hook_dlvsym)\n";
+ write(2, str, strlen(str));
+ for(const GLInjectHook &hook : glinject_hook_table) {
+ if(strcmp(hook.name, symbol) == 0) {
std::lock_guard<std::mutex> lock(g_glinject_mutex);
- GLINJECT_PRINT("Hooked: dlvsym(" << symbol << "," << version << ").");
- return hook_table[i].address;
+ GLINJECT_PRINT("Hooked dlvsym(" << symbol << ").");
+ return hook.address;
}
}
- return g_glinject_real_dlvsym(handle, symbol, version);
+ return dlvsym(handle, symbol, version);
}
-extern "C" int execl(const char* filename, const char* arg, ...) {
- InitGLInject();
+int glinject_hook_execl(const char* filename, const char* arg, ...) {
+ const char *str = "(In glinject_hook_execl)\n";
+ write(2, str, strlen(str));
std::vector<char*> args;
args.push_back((char*) arg);
va_list vl;
@@ -263,11 +186,12 @@
va_end(vl);
std::vector<char*> filtered_environ;
FilterEnviron(filename, &filtered_environ, environ);
- return g_glinject_real_execve(filename, args.data(), filtered_environ.data());
+ return execve(filename, args.data(), filtered_environ.data());
}
-extern "C" int execlp(const char* filename, const char* arg, ...) {
- InitGLInject();
+int glinject_hook_execlp(const char* filename, const char* arg, ...) {
+ const char *str = "(In glinject_hook_execlp)\n";
+ write(2, str, strlen(str));
std::vector<char*> args;
args.push_back((char*) arg);
va_list vl;
@@ -278,11 +202,12 @@
va_end(vl);
std::vector<char*> filtered_environ;
FilterEnviron(filename, &filtered_environ, environ);
- return g_glinject_real_execvpe(filename, args.data(), filtered_environ.data());
+ return execvpe(filename, args.data(), filtered_environ.data());
}
-extern "C" int execle(const char* filename, const char* arg, ...) {
- InitGLInject();
+int glinject_hook_execle(const char* filename, const char* arg, ...) {
+ const char *str = "(In glinject_hook_execle)\n";
+ write(2, str, strlen(str));
std::vector<char*> args;
args.push_back((char*) arg);
va_list vl;
@@ -294,63 +219,103 @@
va_end(vl);
std::vector<char*> filtered_environ;
FilterEnviron(filename, &filtered_environ, envp);
- return g_glinject_real_execvpe(filename, args.data(), filtered_environ.data());
+ return execvpe(filename, args.data(), filtered_environ.data());
}
-extern "C" int execv(const char* filename, char* const argv[]) {
- InitGLInject();
+int glinject_hook_execv(const char* filename, char* const argv[]) {
+ const char *str = "(In glinject_hook_execv)\n";
+ write(2, str, strlen(str));
std::vector<char*> filtered_environ;
FilterEnviron(filename, &filtered_environ, environ);
- return g_glinject_real_execve(filename, argv, filtered_environ.data());
+ return execve(filename, argv, filtered_environ.data());
}
-extern "C" int execve(const char* filename, char* const argv[], char* const envp[]) {
- InitGLInject();
+int glinject_hook_execve(const char* filename, char* const argv[], char* const envp[]) {
+ const char *str = "(In glinject_hook_execve)\n";
+ write(2, str, strlen(str));
std::vector<char*> filtered_environ;
FilterEnviron(filename, &filtered_environ, envp);
- return g_glinject_real_execve(filename, argv, filtered_environ.data());
+ return execve(filename, argv, filtered_environ.data());
}
-extern "C" int execvp(const char* filename, char* const argv[]) {
- InitGLInject();
+int glinject_hook_execvp(const char* filename, char* const argv[]) {
+ const char *str = "(In glinject_hook_execvp)\n";
+ write(2, str, strlen(str));
std::vector<char*> filtered_environ;
FilterEnviron(filename, &filtered_environ, environ);
- return g_glinject_real_execvpe(filename, argv, filtered_environ.data());
+ return execvpe(filename, argv, filtered_environ.data());
}
-extern "C" int execvpe(const char* filename, char* const argv[], char* const envp[]) {
- InitGLInject();
+int glinject_hook_execvpe(const char* filename, char* const argv[], char* const envp[]) {
+ const char *str = "(In glinject_hook_execvpe)\n";
+ write(2, str, strlen(str));
std::vector<char*> filtered_environ;
FilterEnviron(filename, &filtered_environ, envp);
- return g_glinject_real_execvpe(filename, argv, filtered_environ.data());
+ return execvpe(filename, argv, filtered_environ.data());
}
-extern "C" GLXWindow glXCreateWindow(Display* dpy, GLXFBConfig config, Window win, const int* attrib_list) {
- InitGLInject();
- return glinject_my_glXCreateWindow(dpy, config, win, attrib_list);
-}
-
-extern "C" void glXDestroyWindow(Display* dpy, GLXWindow win) {
- InitGLInject();
- glinject_my_glXDestroyWindow(dpy, win);
+GLXWindow glinject_hook_glXCreateWindow(Display* dpy, GLXFBConfig config, Window win, const int* attrib_list) {
+ const char *str = "(In glinject_hook_glXCreateWindow)\n";
+ write(2, str, strlen(str));
+ GLXWindow res = glXCreateWindow(dpy, config, win, attrib_list);
+ if(res == 0)
+ return 0;
+ {
+ std::lock_guard<std::mutex> lock(g_glinject_mutex);
+ GLInjectInit();
+ g_glinject->NewGLXFrameGrabber(dpy, win, res);
+ }
+ return res;
}
-extern "C" int XDestroyWindow(Display* dpy, Window win) {
- InitGLInject();
- return glinject_my_XDestroyWindow(dpy, win);
+void glinject_hook_glXDestroyWindow(Display* dpy, GLXWindow win) {
+ const char *str = "(In glinject_hook_glXDestroyWindow)\n";
+ write(2, str, strlen(str));
+ glXDestroyWindow(dpy, win);
+ {
+ std::lock_guard<std::mutex> lock(g_glinject_mutex);
+ GLInjectInit();
+ g_glinject->DeleteGLXFrameGrabberByDrawable(dpy, win);
+ }
}
-extern "C" void glXSwapBuffers(Display* dpy, GLXDrawable drawable) {
- InitGLInject();
- glinject_my_glXSwapBuffers(dpy, drawable);
+int glinject_hook_XDestroyWindow(Display* dpy, Window win) {
+ const char *str = "(In glinject_hook_XDestroyWindow)\n";
+ write(2, str, strlen(str));
+ int res = XDestroyWindow(dpy, win);
+ {
+ std::lock_guard<std::mutex> lock(g_glinject_mutex);
+ GLInjectInit();
+ g_glinject->DeleteGLXFrameGrabberByWindow(dpy, win);
+ }
+ return res;
}
-extern "C" GLXextFuncPtr glXGetProcAddressARB(const GLubyte* proc_name) {
- InitGLInject();
- return glinject_my_glXGetProcAddressARB(proc_name);
+void glinject_hook_glXSwapBuffers(Display* dpy, GLXDrawable drawable) {
+ const char *str = "(In glinject_hook_glXSwapBuffers)\n";
+ write(2, str, strlen(str));
+ {
+ std::lock_guard<std::mutex> lock(g_glinject_mutex);
+ GLInjectInit();
+ GLXFrameGrabber *fg = g_glinject->FindGLXFrameGrabber(dpy, drawable);
+ if(fg == NULL) {
+ GLINJECT_PRINT("Warning: glXSwapBuffers called without existing frame grabber, creating one assuming window == drawable.");
+ fg = g_glinject->NewGLXFrameGrabber(dpy, drawable, drawable);
+ }
+ fg->GrabFrame();
+ }
+ glXSwapBuffers(dpy, drawable);
}
-extern "C" int XNextEvent(Display* display, XEvent* event_return) {
- InitGLInject();
- return glinject_my_XNextEvent(display, event_return);
+GLXextFuncPtr glinject_hook_glXGetProcAddressARB(const GLubyte *proc_name) {
+ const char *str = "(In glinject_hook_glXGetProcAddressARB)\n";
+ write(2, str, strlen(str));
+ for(const GLInjectHook &hook : glinject_hook_table) {
+ if(strcmp(hook.name, (const char*) proc_name) == 0) {
+ std::lock_guard<std::mutex> lock(g_glinject_mutex);
+ GLINJECT_PRINT("Hooked glXGetProcAddressARB(" << proc_name << ").");
+ return (GLXextFuncPtr) hook.address;
+ }
+ }
+ return glXGetProcAddressARB(proc_name);
}
Index: simplescreenrecorder-salsa/glinject/ShmStructs.h
===================================================================
--- simplescreenrecorder-salsa.orig/glinject/ShmStructs.h 2024-05-07 08:50:15.918773109 +0200
+++ simplescreenrecorder-salsa/glinject/ShmStructs.h 2024-05-07 08:50:15.910773036 +0200
@@ -8,6 +8,8 @@
#pragma once
+#include <stdint.h>
+
/*
A captured video stream is transmitted to SimpleScreenRecorder using shared memory files (i.e. files in /dev/shm).
The system is entirely lock-free and thread-safe, but supports only a single reader and a single writer.
Index: simplescreenrecorder-salsa/glinject/elfhacks.c
===================================================================
--- simplescreenrecorder-salsa.orig/glinject/elfhacks.c 2024-05-07 08:50:15.918773109 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,611 +0,0 @@
-/**
- * \file src/elfhacks.c
- * \brief various ELF run-time hacks
- * \author Pyry Haulos <pyry.haulos@gmail.com>
- * \date 2007-2008
- * For conditions of distribution and use, see copyright notice in elfhacks.h
- */
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <elf.h>
-#include <link.h>
-#include <fnmatch.h>
-#include "elfhacks.h"
-
-/**
- * \addtogroup elfhacks
- * \{
- */
-
-struct eh_iterate_callback_args {
- eh_iterate_obj_callback_func callback;
- void *arg;
-};
-
-int eh_check_addr(eh_obj_t *obj, const void *addr);
-int eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr);
-int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next);
-int eh_init_obj(eh_obj_t *obj);
-
-int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val);
-int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val);
-
-int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);
-int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);
-
-int eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);
-int eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);
-
-ElfW(Word) eh_hash_elf(const char *name);
-Elf32_Word eh_hash_gnu(const char *name);
-
-int eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr)
-{
- (void) (size);
- eh_obj_t *find = (eh_obj_t *) argptr;
-
- if (find->name == NULL) {
- if (strcmp(info->dlpi_name, ""))
- return 0;
- } else if (fnmatch(find->name, info->dlpi_name, 0))
- return 0;
-
- if (find->name == NULL) /* TODO readlink? */
- find->name = "/proc/self/exe";
- else
- find->name = info->dlpi_name;
- find->addr = info->dlpi_addr;
-
- /* segment headers */
- find->phdr = info->dlpi_phdr;
- find->phnum = info->dlpi_phnum;
-
- return 0;
-}
-
-int eh_iterate_callback(struct dl_phdr_info *info, size_t size, void *argptr)
-{
- (void) (size);
- struct eh_iterate_callback_args *args = (struct eh_iterate_callback_args*) argptr;
- eh_obj_t obj;
- int ret = 0;
-
- /* eh_init_obj needs phdr and phnum */
- obj.phdr = info->dlpi_phdr;
- obj.phnum = info->dlpi_phnum;
- obj.addr = info->dlpi_addr;
- obj.name = info->dlpi_name;
-
- if ((ret = eh_init_obj(&obj))) {
- if (ret == ENOTSUP) /* just skip */
- return 0;
- return ret;
- }
-
- if ((ret = args->callback(&obj, args->arg)))
- return ret;
-
- if ((ret = eh_destroy_obj(&obj)))
- return ret;
-
- return 0;
-}
-
-int eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg)
-{
- int ret;
- struct eh_iterate_callback_args args;
-
- args.callback = callback;
- args.arg = arg;
-
- if ((ret = dl_iterate_phdr(eh_iterate_callback, &args)))
- return ret;
-
- return 0;
-}
-
-int eh_find_obj(eh_obj_t *obj, const char *soname)
-{
- /* This function uses glibc-specific dl_iterate_phdr().
- Another way could be parsing /proc/self/exe or using
- pmap() on Solaris or *BSD */
- obj->phdr = NULL;
- obj->name = soname;
- dl_iterate_phdr(eh_find_callback, obj);
-
- if (!obj->phdr)
- return EAGAIN;
-
- return eh_init_obj(obj);
-}
-
-int eh_check_addr(eh_obj_t *obj, const void *addr)
-{
- /*
- Check that given address is inside program's
- memory maps. PT_LOAD program headers tell us
- where program has been loaded into.
- */
- int p;
- for (p = 0; p < obj->phnum; p++) {
- if (obj->phdr[p].p_type == PT_LOAD) {
- if (((ElfW(Addr)) addr < obj->phdr[p].p_memsz + obj->phdr[p].p_vaddr + obj->addr) &&
- ((ElfW(Addr)) addr >= obj->phdr[p].p_vaddr + obj->addr))
- return 0;
- }
- }
-
- return EINVAL;
-}
-
-int eh_init_obj(eh_obj_t *obj)
-{
- /*
- ELF spec says in section header documentation, that:
- "An object file may have only one dynamic section."
-
- Let's assume it means that object has only one PT_DYNAMIC
- as well.
- */
- int p;
- obj->dynamic = NULL;
- for (p = 0; p < obj->phnum; p++) {
- if (obj->phdr[p].p_type == PT_DYNAMIC) {
- if (obj->dynamic)
- return ENOTSUP;
-
- obj->dynamic = (ElfW(Dyn) *) (obj->phdr[p].p_vaddr + obj->addr);
- }
- }
-
- if (!obj->dynamic)
- return ENOTSUP;
-
- /*
- ELF spec says that program is allowed to have more than one
- .strtab but does not describe how string table indexes translate
- to multiple string tables.
-
- And spec says that only one SHT_HASH is allowed, does it mean that
- obj has only one DT_HASH?
-
- About .symtab it does not mention anything about if multiple
- symbol tables are allowed or not.
-
- Maybe st_shndx is the key here?
- */
- obj->strtab = NULL;
- obj->hash = NULL;
- obj->gnu_hash = NULL;
- obj->symtab = NULL;
- p = 0;
- while (obj->dynamic[p].d_tag != DT_NULL) {
- if (obj->dynamic[p].d_tag == DT_STRTAB) {
- if (obj->strtab)
- return ENOTSUP;
-
- obj->strtab = (const char *) obj->dynamic[p].d_un.d_ptr;
- } else if (obj->dynamic[p].d_tag == DT_HASH) {
- if (obj->hash)
- return ENOTSUP;
-
- obj->hash = (ElfW(Word) *) obj->dynamic[p].d_un.d_ptr;
- } else if (obj->dynamic[p].d_tag == DT_GNU_HASH) {
- if (obj->gnu_hash)
- return ENOTSUP;
-
- obj->gnu_hash = (Elf32_Word *) obj->dynamic[p].d_un.d_ptr;
- } else if (obj->dynamic[p].d_tag == DT_SYMTAB) {
- if (obj->symtab)
- return ENOTSUP;
-
- obj->symtab = (ElfW(Sym) *) obj->dynamic[p].d_un.d_ptr;
- }
- p++;
- }
-
- /* This is here to catch b0rken headers (vdso) */
- if ((eh_check_addr(obj, (const void *) obj->strtab)) |
- (eh_check_addr(obj, (const void *) obj->symtab)))
- return ENOTSUP;
-
- if (obj->hash) {
- /* DT_HASH found */
- if (eh_check_addr(obj, (void *) obj->hash))
- obj->hash = NULL;
- } else if (obj->gnu_hash) {
- /* DT_GNU_HASH found */
- if (eh_check_addr(obj, (void *) obj->gnu_hash))
- obj->gnu_hash = NULL;
- }
-
- return 0;
-}
-
-int eh_find_sym(eh_obj_t *obj, const char *name, void **to)
-{
- eh_sym_t sym;
-
- /* DT_GNU_HASH is faster ;) */
- if (obj->gnu_hash) {
- if (!eh_find_sym_gnu_hash(obj, name, &sym)) {
- *to = (void *) (sym.sym->st_value + obj->addr);
- return 0;
- }
- }
-
- /* maybe it is in DT_HASH or DT_GNU_HASH is not present */
- if (obj->hash) {
- if (!eh_find_sym_hash(obj, name, &sym)) {
- *to = (void *) (sym.sym->st_value + obj->addr);
- return 0;
- }
- }
-
- return EAGAIN;
-}
-
-ElfW(Word) eh_hash_elf(const char *name)
-{
- ElfW(Word) tmp, hash = 0;
- const unsigned char *uname = (const unsigned char *) name;
- int c;
-
- while ((c = *uname++) != '\0') {
- hash = (hash << 4) + c;
- if ((tmp = (hash & 0xf0000000)) != 0) {
- hash ^= tmp >> 24;
- hash ^= tmp;
- }
- }
-
- return hash;
-}
-
-int eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)
-{
- ElfW(Word) hash, *chain;
- ElfW(Sym) *esym;
- unsigned int bucket_idx, idx;
-
- if (!obj->hash)
- return ENOTSUP;
-
- if (obj->hash[0] == 0)
- return EAGAIN;
-
- hash = eh_hash_elf(name);
- /*
- First item in DT_HASH is nbucket, second is nchain.
- hash % nbucket gives us our bucket index.
- */
- bucket_idx = obj->hash[2 + (hash % obj->hash[0])];
- chain = &obj->hash[2 + obj->hash[0] + bucket_idx];
-
- idx = 0;
- sym->sym = NULL;
-
- /* we have to check symtab[bucket_idx] first */
- esym = &obj->symtab[bucket_idx];
- if (esym->st_name) {
- if (!strcmp(&obj->strtab[esym->st_name], name))
- sym->sym = esym;
- }
-
- while ((sym->sym == NULL) &&
- (chain[idx] != STN_UNDEF)) {
- esym = &obj->symtab[chain[idx]];
-
- if (esym->st_name) {
- if (!strcmp(&obj->strtab[esym->st_name], name))
- sym->sym = esym;
- }
-
- idx++;
- }
-
- /* symbol not found */
- if (sym->sym == NULL)
- return EAGAIN;
-
- sym->obj = obj;
- sym->name = &obj->strtab[sym->sym->st_name];
-
- return 0;
-}
-
-Elf32_Word eh_hash_gnu(const char *name)
-{
- Elf32_Word hash = 5381;
- const unsigned char *uname = (const unsigned char *) name;
- int c;
-
- while ((c = *uname++) != '\0')
- hash = (hash << 5) + hash + c;
-
- return hash & 0xffffffff;
-}
-
-int eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)
-{
- Elf32_Word *buckets, *chain_zero, *hasharr;
- ElfW(Addr) *bitmask, bitmask_word;
- Elf32_Word symbias, bitmask_nwords, bucket,
- nbuckets, bitmask_idxbits, shift;
- Elf32_Word hash, hashbit1, hashbit2;
- ElfW(Sym) *esym;
-
- if (!obj->gnu_hash)
- return ENOTSUP;
-
- if (obj->gnu_hash[0] == 0)
- return EAGAIN;
-
- sym->sym = NULL;
-
- /*
- Initialize our hash table stuff
-
- DT_GNU_HASH is(?):
- [nbuckets] [symbias] [bitmask_nwords] [shift]
- [bitmask_nwords * ElfW(Addr)] <- bitmask
- [nbuckets * Elf32_Word] <- buckets
- ...chains? - symbias...
- */
- nbuckets = obj->gnu_hash[0];
- symbias = obj->gnu_hash[1];
- bitmask_nwords = obj->gnu_hash[2]; /* must be power of two */
- bitmask_idxbits = bitmask_nwords - 1;
- shift = obj->gnu_hash[3];
- bitmask = (ElfW(Addr) *) &obj->gnu_hash[4];
- buckets = &obj->gnu_hash[4 + (__ELF_NATIVE_CLASS / 32) * bitmask_nwords];
- chain_zero = &buckets[nbuckets] - symbias;
-
- /* hash our symbol */
- hash = eh_hash_gnu(name);
-
- /* bitmask stuff... no idea really :D */
- bitmask_word = bitmask[(hash / __ELF_NATIVE_CLASS) & bitmask_idxbits];
- hashbit1 = hash & (__ELF_NATIVE_CLASS - 1);
- hashbit2 = (hash >> shift) & (__ELF_NATIVE_CLASS - 1);
-
- /* wtf this does actually? */
- if (!((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1))
- return EAGAIN;
-
- /* locate bucket */
- bucket = buckets[hash % nbuckets];
- if (bucket == 0)
- return EAGAIN;
-
- /* and find match in chain */
- hasharr = &chain_zero[bucket];
- do {
- if (((*hasharr ^ hash) >> 1) == 0) {
- /* hash matches, but does the name? */
- esym = &obj->symtab[hasharr - chain_zero];
- if (esym->st_name) {
- if (!strcmp(&obj->strtab[esym->st_name], name)) {
- sym->sym = esym;
- break;
- }
- }
- }
- } while ((*hasharr++ & 1u) == 0);
-
- /* symbol not found */
- if (sym->sym == NULL)
- return EAGAIN;
-
- sym->obj = obj;
- sym->name = &obj->strtab[sym->sym->st_name];
-
- return 0;
-}
-
-int eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback, void *arg)
-{
- (void) (obj);
- (void) (callback);
- (void) (arg);
- return ENOTSUP;
-}
-
-int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next)
-{
- /* first from i + 1 to end, then from start to i - 1 */
- int p;
- *next = NULL;
-
- p = i + 1;
- while (obj->dynamic[p].d_tag != DT_NULL) {
- if (obj->dynamic[p].d_tag == tag) {
- *next = &obj->dynamic[p];
- return 0;
- }
- p++;
- }
-
- p = 0;
- while ((obj->dynamic[i].d_tag != DT_NULL) && (p < i)) {
- if (obj->dynamic[p].d_tag == tag) {
- *next = &obj->dynamic[p];
- return 0;
- }
- p++;
- }
-
- return EAGAIN;
-}
-
-int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)
-{
- ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
- ElfW(Dyn) *relasize;
- unsigned int i;
-
- /* DT_PLTRELSZ contains PLT relocs size in bytes */
- if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relasize))
- return EINVAL; /* b0rken elf :/ */
-
- for (i = 0; i < relasize->d_un.d_val / sizeof(ElfW(Rela)); i++) {
- if (!obj->symtab[ELFW_R_SYM(rela[i].r_info)].st_name)
- continue;
-
- if (!strcmp(&obj->strtab[obj->symtab[ELFW_R_SYM(rela[i].r_info)].st_name], sym))
- *((void **) (rela[i].r_offset + obj->addr)) = val;
- }
-
- return 0;
-}
-
-int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val)
-{
- ElfW(Rel) *rel = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
- ElfW(Dyn) *relsize;
- unsigned int i;
-
- if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relsize))
- return EINVAL; /* b0rken elf :/ */
-
- for (i = 0; i < relsize->d_un.d_val / sizeof(ElfW(Rel)); i++) {
- if (!obj->symtab[ELFW_R_SYM(rel[i].r_info)].st_name)
- continue;
-
- if (!strcmp(&obj->strtab[obj->symtab[ELFW_R_SYM(rel[i].r_info)].st_name], sym))
- *((void **) (rel[i].r_offset + obj->addr)) = val;
- }
-
- return 0;
-}
-
-int eh_set_rel(eh_obj_t *obj, const char *sym, void *val)
-{
- /*
- Elf spec states that object is allowed to have multiple
- .rel.plt and .rela.plt tables, so we will support 'em - here.
- */
- ElfW(Dyn) *pltrel;
- int ret, p = 0;
-
- while (obj->dynamic[p].d_tag != DT_NULL) {
- /* DT_JMPREL contains .rel.plt or .rela.plt */
- if (obj->dynamic[p].d_tag == DT_JMPREL) {
- /* DT_PLTREL tells if it is Rela or Rel */
- eh_find_next_dyn(obj, DT_PLTREL, p, &pltrel);
-
- if (pltrel->d_un.d_val == DT_RELA) {
- if ((ret = eh_set_rela_plt(obj, p, sym, val)))
- return ret;
- } else if (pltrel->d_un.d_val == DT_REL) {
- if ((ret = eh_set_rel_plt(obj, p, sym, val)))
- return ret;
- } else
- return EINVAL;
- }
- p++;
- }
-
- return 0;
-}
-
-int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
-{
- ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
- ElfW(Dyn) *relasize;
- eh_rel_t rel;
- eh_sym_t sym;
- unsigned int i, ret;
-
- rel.sym = &sym;
- rel.rel = NULL;
- rel.obj = obj;
-
- if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relasize))
- return EINVAL;
-
- for (i = 0; i < relasize->d_un.d_val / sizeof(ElfW(Rela)); i++) {
- rel.rela = &rela[i];
- sym.sym = &obj->symtab[ELFW_R_SYM(rel.rela->r_info)];
- if (sym.sym->st_name)
- sym.name = &obj->strtab[sym.sym->st_name];
- else
- sym.name = NULL;
-
- if ((ret = callback(&rel, arg)))
- return ret;
- }
-
- return 0;
-}
-
-int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
-{
- ElfW(Rel) *relp = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
- ElfW(Dyn) *relsize;
- eh_rel_t rel;
- eh_sym_t sym;
- unsigned int i, ret;
-
- rel.sym = &sym;
- rel.rela = NULL;
- rel.obj = obj;
-
- if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relsize))
- return EINVAL;
-
- for (i = 0; i < relsize->d_un.d_val / sizeof(ElfW(Rel)); i++) {
- rel.rel = &relp[i];
- sym.sym = &obj->symtab[ELFW_R_SYM(rel.rel->r_info)];
- if (sym.sym->st_name)
- sym.name = &obj->strtab[sym.sym->st_name];
- else
- sym.name = NULL;
-
- if ((ret = callback(&rel, arg)))
- return ret;
- }
-
- return 0;
-}
-
-int eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback, void *arg)
-{
- ElfW(Dyn) *pltrel;
- int ret, p = 0;
-
- while (obj->dynamic[p].d_tag != DT_NULL) {
- if (obj->dynamic[p].d_tag == DT_JMPREL) {
- eh_find_next_dyn(obj, DT_PLTREL, p, &pltrel);
-
- if (pltrel->d_un.d_val == DT_RELA) {
- if ((ret = eh_iterate_rela_plt(obj, p, callback, arg)))
- return ret;
- } else if (pltrel->d_un.d_val == DT_REL) {
- if ((ret = eh_iterate_rel_plt(obj, p, callback, arg)))
- return ret;
- } else
- return EINVAL;
- }
- p++;
- }
-
- return 0;
-}
-
-int eh_destroy_obj(eh_obj_t *obj)
-{
- obj->phdr = NULL;
-
- return 0;
-}
-
-/** \} */
Index: simplescreenrecorder-salsa/glinject/plthook.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ simplescreenrecorder-salsa/glinject/plthook.h 2024-05-07 08:50:15.914773073 +0200
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil -*-
+ *
+ * plthook.h -- the header file of plthook
+ *
+ * URL: https://github.com/kubo/plthook
+ *
+ * ------------------------------------------------------
+ *
+ * Copyright 2013-2014 Kubo Takehiro <kubo@jiubao.org>
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of the authors.
+ *
+ */
+#ifndef PLTHOOK_H
+#define PLTHOOK_H 1
+
+#define PLTHOOK_SUCCESS 0
+#define PLTHOOK_FILE_NOT_FOUND 1
+#define PLTHOOK_INVALID_FILE_FORMAT 2
+#define PLTHOOK_FUNCTION_NOT_FOUND 3
+#define PLTHOOK_INVALID_ARGUMENT 4
+#define PLTHOOK_OUT_OF_MEMORY 5
+#define PLTHOOK_INTERNAL_ERROR 6
+#define PLTHOOK_NOT_IMPLEMENTED 7
+
+typedef struct plthook plthook_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int plthook_open(plthook_t **plthook_out, const char *filename);
+int plthook_open_by_handle(plthook_t **plthook_out, void *handle);
+int plthook_open_by_address(plthook_t **plthook_out, void *address);
+int plthook_open_by_linkmap(plthook_t **plthook_out, void *linkmap);
+int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out);
+int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc);
+void plthook_close(plthook_t *plthook);
+const char *plthook_error(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
Index: simplescreenrecorder-salsa/glinject/plthook_elf.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ simplescreenrecorder-salsa/glinject/plthook_elf.c 2024-05-07 08:50:15.914773073 +0200
@@ -0,0 +1,821 @@
+/* -*- indent-tabs-mode: nil -*-
+ *
+ * plthook_elf.c -- implementation of plthook for ELF format
+ *
+ * URL: https://github.com/kubo/plthook
+ *
+ * ------------------------------------------------------
+ *
+ * Copyright 2013-2019 Kubo Takehiro <kubo@jiubao.org>
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of the authors.
+ *
+ */
+#if defined(__sun) && defined(_XOPEN_SOURCE) && !defined(__EXTENSIONS__)
+#define __EXTENSIONS__
+#endif
+#if defined(__linux__) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <dlfcn.h>
+#ifdef __sun
+#include <sys/auxv.h>
+#include <procfs.h>
+#define ELF_TARGET_ALL
+#endif /* __sun */
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/user.h>
+#include <libutil.h>
+#endif
+#include <elf.h>
+#include <link.h>
+#include "plthook.h"
+
+#if defined __UCLIBC__ && !defined RTLD_NOLOAD
+#define RTLD_NOLOAD 0
+#endif
+
+#ifndef __GNUC__
+#define __attribute__(arg)
+#endif
+
+#if defined __FreeBSD__ && defined __i386__ && __ELF_WORD_SIZE == 64
+#error 32-bit application on 64-bit OS is not supported.
+#endif
+
+#if !defined(R_X86_64_JUMP_SLOT) && defined(R_X86_64_JMP_SLOT)
+#define R_X86_64_JUMP_SLOT R_X86_64_JMP_SLOT
+#endif
+
+#if defined __x86_64__ || defined __x86_64
+#define R_JUMP_SLOT R_X86_64_JUMP_SLOT
+#define R_GLOBAL_DATA R_X86_64_GLOB_DAT
+#elif defined __i386__ || defined __i386
+#define R_JUMP_SLOT R_386_JMP_SLOT
+#define R_GLOBAL_DATA R_386_GLOB_DAT
+#define USE_REL
+#elif defined __arm__ || defined __arm
+#define R_JUMP_SLOT R_ARM_JUMP_SLOT
+#define R_GLOBAL_DATA R_ARM_GLOB_DAT
+#define USE_REL
+#elif defined __aarch64__ || defined __aarch64 /* ARM64 */
+#define R_JUMP_SLOT R_AARCH64_JUMP_SLOT
+#define R_GLOBAL_DATA R_AARCH64_GLOB_DAT
+#elif defined __powerpc64__
+#define R_JUMP_SLOT R_PPC64_JMP_SLOT
+#define R_GLOBAL_DATA R_PPC64_GLOB_DAT
+#elif defined __powerpc__
+#define R_JUMP_SLOT R_PPC_JMP_SLOT
+#define R_GLOBAL_DATA R_PPC_GLOB_DAT
+#elif 0 /* disabled because not tested */ && (defined __sparcv9 || defined __sparc_v9__)
+#define R_JUMP_SLOT R_SPARC_JMP_SLOT
+#elif 0 /* disabled because not tested */ && (defined __sparc || defined __sparc__)
+#define R_JUMP_SLOT R_SPARC_JMP_SLOT
+#elif 0 /* disabled because not tested */ && (defined __ia64 || defined __ia64__)
+#define R_JUMP_SLOT R_IA64_IPLTMSB
+#else
+#error unsupported OS
+#endif
+
+#ifdef USE_REL
+#define Elf_Plt_Rel Elf_Rel
+#define PLT_DT_REL DT_REL
+#define PLT_DT_RELSZ DT_RELSZ
+#define PLT_DT_RELENT DT_RELENT
+#else
+#define Elf_Plt_Rel Elf_Rela
+#define PLT_DT_REL DT_RELA
+#define PLT_DT_RELSZ DT_RELASZ
+#define PLT_DT_RELENT DT_RELAENT
+#endif
+
+#if defined __LP64__
+#ifndef ELF_CLASS
+#define ELF_CLASS ELFCLASS64
+#endif
+#define SIZE_T_FMT "lu"
+#define ELF_WORD_FMT "u"
+#ifdef __ANDROID__
+#define ELF_XWORD_FMT "llu"
+#else
+#define ELF_XWORD_FMT "lu"
+#endif
+#define ELF_SXWORD_FMT "ld"
+#define Elf_Half Elf64_Half
+#define Elf_Xword Elf64_Xword
+#define Elf_Sxword Elf64_Sxword
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Phdr Elf64_Phdr
+#define Elf_Sym Elf64_Sym
+#define Elf_Dyn Elf64_Dyn
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#ifndef ELF_R_SYM
+#define ELF_R_SYM ELF64_R_SYM
+#endif
+#ifndef ELF_R_TYPE
+#define ELF_R_TYPE ELF64_R_TYPE
+#endif
+#else /* __LP64__ */
+#ifndef ELF_CLASS
+#define ELF_CLASS ELFCLASS32
+#endif
+#define SIZE_T_FMT "u"
+#ifdef __sun
+#define ELF_WORD_FMT "lu"
+#define ELF_XWORD_FMT "lu"
+#define ELF_SXWORD_FMT "ld"
+#else
+#define ELF_WORD_FMT "u"
+#define ELF_XWORD_FMT "u"
+#define ELF_SXWORD_FMT "d"
+#endif
+#define Elf_Half Elf32_Half
+#define Elf_Xword Elf32_Word
+#define Elf_Sxword Elf32_Sword
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Phdr Elf32_Phdr
+#define Elf_Sym Elf32_Sym
+#define Elf_Dyn Elf32_Dyn
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#ifndef ELF_R_SYM
+#define ELF_R_SYM ELF32_R_SYM
+#endif
+#ifndef ELF_R_TYPE
+#define ELF_R_TYPE ELF32_R_TYPE
+#endif
+#endif /* __LP64__ */
+
+struct plthook {
+ const Elf_Sym *dynsym;
+ const char *dynstr;
+ size_t dynstr_size;
+ const char *plt_addr_base;
+ const Elf_Plt_Rel *rela_plt;
+ size_t rela_plt_cnt;
+#ifdef R_GLOBAL_DATA
+ const Elf_Plt_Rel *rela_dyn;
+ size_t rela_dyn_cnt;
+#endif
+};
+
+static char errmsg[512];
+static size_t page_size;
+#define ALIGN_ADDR(addr) ((void*)((size_t)(addr) & ~(page_size - 1)))
+
+static int plthook_open_executable(plthook_t **plthook_out);
+static int plthook_open_shared_library(plthook_t **plthook_out, const char *filename);
+static const Elf_Dyn *find_dyn_by_tag(const Elf_Dyn *dyn, Elf_Sxword tag);
+static int plthook_open_real(plthook_t **plthook_out, struct link_map *lmap);
+#if defined __FreeBSD__ || defined __sun
+static int check_elf_header(const Elf_Ehdr *ehdr);
+#endif
+static void set_errmsg(const char *fmt, ...) __attribute__((__format__ (__printf__, 1, 2)));
+
+#if defined __ANDROID__ || defined __UCLIBC__
+struct dl_iterate_data {
+ char* addr;
+ struct link_map lmap;
+};
+
+static int dl_iterate_cb(struct dl_phdr_info *info, size_t size, void *cb_data)
+{
+ struct dl_iterate_data *data = (struct dl_iterate_data*)cb_data;
+ Elf_Half idx = 0;
+
+ for (idx = 0; idx < info->dlpi_phnum; ++idx) {
+ const Elf_Phdr *phdr = &info->dlpi_phdr[idx];
+ char* base = (char*)info->dlpi_addr + phdr->p_vaddr;
+ if (base <= data->addr && data->addr < base + phdr->p_memsz) {
+ break;
+ }
+ }
+ if (idx == info->dlpi_phnum) {
+ return 0;
+ }
+ for (idx = 0; idx < info->dlpi_phnum; ++idx) {
+ const Elf_Phdr *phdr = &info->dlpi_phdr[idx];
+ if (phdr->p_type == PT_DYNAMIC) {
+ data->lmap.l_addr = info->dlpi_addr;
+ data->lmap.l_ld = (Elf_Dyn*)(info->dlpi_addr + phdr->p_vaddr);
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+int plthook_open(plthook_t **plthook_out, const char *filename)
+{
+ *plthook_out = NULL;
+ if (filename == NULL) {
+ return plthook_open_executable(plthook_out);
+ } else {
+ return plthook_open_shared_library(plthook_out, filename);
+ }
+}
+
+int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
+{
+#if defined __ANDROID__ || defined __UCLIBC__
+ const static char *symbols[] = {
+ "__INIT_ARRAY__",
+ "_end",
+ "_start"
+ };
+ size_t i;
+
+ if (hndl == NULL) {
+ set_errmsg("NULL handle");
+ return PLTHOOK_FILE_NOT_FOUND;
+ }
+ for (i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
+ char *addr = dlsym(hndl, symbols[i]);
+ if (addr != NULL) {
+ return plthook_open_by_address(plthook_out, addr - 1);
+ }
+ }
+ set_errmsg("Could not find an address in the specified handle.");
+ return PLTHOOK_INTERNAL_ERROR;
+#else
+ struct link_map *lmap = NULL;
+
+ if (hndl == NULL) {
+ set_errmsg("NULL handle");
+ return PLTHOOK_FILE_NOT_FOUND;
+ }
+ if (dlinfo(hndl, RTLD_DI_LINKMAP, &lmap) != 0) {
+ set_errmsg("dlinfo error");
+ return PLTHOOK_FILE_NOT_FOUND;
+ }
+ return plthook_open_real(plthook_out, lmap);
+#endif
+}
+
+int plthook_open_by_address(plthook_t **plthook_out, void *address)
+{
+#if defined __FreeBSD__
+ return PLTHOOK_NOT_IMPLEMENTED;
+#elif defined __ANDROID__ || defined __UCLIBC__
+ struct dl_iterate_data data = {0,};
+ data.addr = address;
+ dl_iterate_phdr(dl_iterate_cb, &data);
+ if (data.lmap.l_ld == NULL) {
+ set_errmsg("Could not find memory region containing address %p", address);
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ return plthook_open_real(plthook_out, &data.lmap);
+#else
+ Dl_info info;
+ struct link_map *lmap = NULL;
+
+ *plthook_out = NULL;
+ if (dladdr1(address, &info, (void**)&lmap, RTLD_DL_LINKMAP) == 0) {
+ set_errmsg("dladdr error");
+ return PLTHOOK_FILE_NOT_FOUND;
+ }
+ return plthook_open_real(plthook_out, lmap);
+#endif
+}
+
+int plthook_open_by_linkmap(plthook_t **plthook_out, void *linkmap)
+{
+ return plthook_open_real(plthook_out, (struct link_map*)linkmap);
+}
+
+static int plthook_open_executable(plthook_t **plthook_out)
+{
+#if defined __ANDROID__ || defined __UCLIBC__
+ return plthook_open_shared_library(plthook_out, NULL);
+#elif defined __linux__
+ return plthook_open_real(plthook_out, _r_debug.r_map);
+#elif defined __sun
+ const char *auxv_file = "/proc/self/auxv";
+#define NUM_AUXV_CNT 10
+ FILE *fp = fopen(auxv_file, "r");
+ auxv_t auxv;
+ struct r_debug *r_debug = NULL;
+
+ if (fp == NULL) {
+ set_errmsg("Could not open %s: %s", auxv_file,
+ strerror(errno));
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ while (fread(&auxv, sizeof(auxv_t), 1, fp) == 1) {
+ if (auxv.a_type == AT_SUN_LDDATA) {
+ r_debug = (struct r_debug *)auxv.a_un.a_ptr;
+ break;
+ }
+ }
+ fclose(fp);
+ if (r_debug == NULL) {
+ set_errmsg("Could not find r_debug");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ return plthook_open_real(plthook_out, r_debug->r_map);
+#elif defined __FreeBSD__
+ return plthook_open_shared_library(plthook_out, NULL);
+#else
+ set_errmsg("Opening the main program is not supported on this platform.");
+ return PLTHOOK_NOT_IMPLEMENTED;
+#endif
+}
+
+static int plthook_open_shared_library(plthook_t **plthook_out, const char *filename)
+{
+ void *hndl = dlopen(filename, RTLD_LAZY | RTLD_NOLOAD);
+#if defined __ANDROID__ || defined __UCLIBC__
+ int rv;
+#else
+ struct link_map *lmap = NULL;
+#endif
+
+ if (hndl == NULL) {
+ set_errmsg("dlopen error: %s", dlerror());
+ return PLTHOOK_FILE_NOT_FOUND;
+ }
+#if defined __ANDROID__ || defined __UCLIBC__
+ rv = plthook_open_by_handle(plthook_out, hndl);
+ dlclose(hndl);
+ return rv;
+#else
+ if (dlinfo(hndl, RTLD_DI_LINKMAP, &lmap) != 0) {
+ set_errmsg("dlinfo error");
+ dlclose(hndl);
+ return PLTHOOK_FILE_NOT_FOUND;
+ }
+ dlclose(hndl);
+ return plthook_open_real(plthook_out, lmap);
+#endif
+}
+
+static const Elf_Dyn *find_dyn_by_tag(const Elf_Dyn *dyn, Elf_Sxword tag)
+{
+ while (dyn->d_tag != DT_NULL) {
+ if (dyn->d_tag == tag) {
+ return dyn;
+ }
+ dyn++;
+ }
+ return NULL;
+}
+
+#ifdef __linux__
+static int get_memory_permission(void *address)
+{
+ unsigned long addr = (unsigned long)address;
+ FILE *fp;
+ char buf[PATH_MAX];
+ char perms[5];
+ int bol = 1;
+
+ fp = fopen("/proc/self/maps", "r");
+ if (fp == NULL) {
+ set_errmsg("failed to open /proc/self/maps");
+ return 0;
+ }
+ while (fgets(buf, PATH_MAX, fp) != NULL) {
+ unsigned long start, end;
+ int eol = (strchr(buf, '\n') != NULL);
+ if (bol) {
+ /* The fgets reads from the beginning of a line. */
+ if (!eol) {
+ /* The next fgets reads from the middle of the same line. */
+ bol = 0;
+ }
+ } else {
+ /* The fgets reads from the middle of a line. */
+ if (eol) {
+ /* The next fgets reads from the beginnig of a line. */
+ bol = 1;
+ }
+ continue;
+ }
+
+ if (sscanf(buf, "%lx-%lx %4s", &start, &end, perms) != 3) {
+ continue;
+ }
+ if (start <= addr && addr < end) {
+ int prot = 0;
+ if (perms[0] == 'r') {
+ prot |= PROT_READ;
+ } else if (perms[0] != '-') {
+ goto unknown_perms;
+ }
+ if (perms[1] == 'w') {
+ prot |= PROT_WRITE;
+ } else if (perms[1] != '-') {
+ goto unknown_perms;
+ }
+ if (perms[2] == 'x') {
+ prot |= PROT_EXEC;
+ } else if (perms[2] != '-') {
+ goto unknown_perms;
+ }
+ if (perms[3] != 'p') {
+ goto unknown_perms;
+ }
+ if (perms[4] != '\0') {
+ perms[4] = '\0';
+ goto unknown_perms;
+ }
+ fclose(fp);
+ return prot;
+ }
+ }
+ fclose(fp);
+ set_errmsg("Could not find memory region containing %p", (void*)addr);
+ return 0;
+unknown_perms:
+ fclose(fp);
+ set_errmsg("Unexcepted memory permission %s at %p", perms, (void*)addr);
+ return 0;
+}
+#elif defined __FreeBSD__
+static int get_memory_permission(void *address)
+{
+ uint64_t addr = (uint64_t)address;
+ struct kinfo_vmentry *top;
+ int i, cnt;
+
+ top = kinfo_getvmmap(getpid(), &cnt);
+ if (top == NULL) {
+ set_errmsg("failed to call kinfo_getvmmap()\n");
+ return 0;
+ }
+ for (i = 0; i < cnt; i++) {
+ struct kinfo_vmentry *kve = top + i;
+
+ if (kve->kve_start <= addr && addr < kve->kve_end) {
+ int prot = 0;
+ if (kve->kve_protection & KVME_PROT_READ) {
+ prot |= PROT_READ;
+ }
+ if (kve->kve_protection & KVME_PROT_WRITE) {
+ prot |= PROT_WRITE;
+ }
+ if (kve->kve_protection & KVME_PROT_EXEC) {
+ prot |= PROT_EXEC;
+ }
+ if (prot == 0) {
+ set_errmsg("Unknown kve_protection 0x%x at %p", kve->kve_protection, (void*)addr);
+ }
+ free(top);
+ return prot;
+ }
+ }
+ free(top);
+ set_errmsg("Could not find memory region containing %p", (void*)addr);
+ return 0;
+}
+#elif defined(__sun)
+#define NUM_MAPS 20
+static int get_memory_permission(void *address)
+{
+ unsigned long addr = (unsigned long)address;
+ FILE *fp;
+ prmap_t maps[NUM_MAPS];
+ size_t num;
+
+ fp = fopen("/proc/self/map", "r");
+ if (fp == NULL) {
+ set_errmsg("failed to open /proc/self/map");
+ return 0;
+ }
+ while ((num = fread(maps, sizeof(prmap_t), NUM_MAPS, fp)) > 0) {
+ size_t i;
+ for (i = 0; i < num; i++) {
+ prmap_t *map = &maps[i];
+
+ if (map->pr_vaddr <= addr && addr < map->pr_vaddr + map->pr_size) {
+ int prot = 0;
+ if (map->pr_mflags & MA_READ) {
+ prot |= PROT_READ;
+ }
+ if (map->pr_mflags & MA_WRITE) {
+ prot |= PROT_WRITE;
+ }
+ if (map->pr_mflags & MA_EXEC) {
+ prot |= PROT_EXEC;
+ }
+ if (prot == 0) {
+ set_errmsg("Unknown pr_mflags 0x%x at %p", map->pr_mflags, (void*)addr);
+ }
+ fclose(fp);
+ return prot;
+ }
+ }
+ }
+ fclose(fp);
+ set_errmsg("Could not find memory region containing %p", (void*)addr);
+ return 0;
+}
+#else
+#error Unsupported platform
+#endif
+
+static int plthook_open_real(plthook_t **plthook_out, struct link_map *lmap)
+{
+ plthook_t plthook = {NULL,};
+ const Elf_Dyn *dyn;
+ const char *dyn_addr_base = NULL;
+
+ if (page_size == 0) {
+ page_size = sysconf(_SC_PAGESIZE);
+ }
+
+#if defined __linux__
+ plthook.plt_addr_base = (char*)lmap->l_addr;
+#if defined __ANDROID__ || defined __UCLIBC__
+ dyn_addr_base = (const char*)lmap->l_addr;
+#endif
+#elif defined __FreeBSD__ || defined __sun
+ const Elf_Ehdr *ehdr = (const Elf_Ehdr*)lmap->l_addr;
+ int rv_ = check_elf_header(ehdr);
+ if (rv_ != 0) {
+ return rv_;
+ }
+ if (ehdr->e_type == ET_DYN) {
+ dyn_addr_base = (const char*)lmap->l_addr;
+ plthook.plt_addr_base = (const char*)lmap->l_addr;
+ }
+#else
+#error unsupported OS
+#endif
+
+ /* get .dynsym section */
+ dyn = find_dyn_by_tag(lmap->l_ld, DT_SYMTAB);
+ if (dyn == NULL) {
+ set_errmsg("failed to find DT_SYMTAB");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ plthook.dynsym = (const Elf_Sym*)(dyn_addr_base + dyn->d_un.d_ptr);
+
+ /* Check sizeof(Elf_Sym) */
+ dyn = find_dyn_by_tag(lmap->l_ld, DT_SYMENT);
+ if (dyn == NULL) {
+ set_errmsg("failed to find DT_SYMTAB");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ if (dyn->d_un.d_val != sizeof(Elf_Sym)) {
+ set_errmsg("DT_SYMENT size %" ELF_XWORD_FMT " != %" SIZE_T_FMT, dyn->d_un.d_val, sizeof(Elf_Sym));
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+
+ /* get .dynstr section */
+ dyn = find_dyn_by_tag(lmap->l_ld, DT_STRTAB);
+ if (dyn == NULL) {
+ set_errmsg("failed to find DT_STRTAB");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ plthook.dynstr = dyn_addr_base + dyn->d_un.d_ptr;
+
+ /* get .dynstr size */
+ dyn = find_dyn_by_tag(lmap->l_ld, DT_STRSZ);
+ if (dyn == NULL) {
+ set_errmsg("failed to find DT_STRSZ");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ plthook.dynstr_size = dyn->d_un.d_val;
+
+ /* get .rela.plt or .rel.plt section */
+ dyn = find_dyn_by_tag(lmap->l_ld, DT_JMPREL);
+ if (dyn != NULL) {
+ plthook.rela_plt = (const Elf_Plt_Rel *)(dyn_addr_base + dyn->d_un.d_ptr);
+ dyn = find_dyn_by_tag(lmap->l_ld, DT_PLTRELSZ);
+ if (dyn == NULL) {
+ set_errmsg("failed to find DT_PLTRELSZ");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ plthook.rela_plt_cnt = dyn->d_un.d_val / sizeof(Elf_Plt_Rel);
+ }
+#ifdef R_GLOBAL_DATA
+ /* get .rela.dyn or .rel.dyn section */
+ dyn = find_dyn_by_tag(lmap->l_ld, PLT_DT_REL);
+ if (dyn != NULL) {
+ size_t total_size, elem_size;
+
+ plthook.rela_dyn = (const Elf_Plt_Rel *)(dyn_addr_base + dyn->d_un.d_ptr);
+ dyn = find_dyn_by_tag(lmap->l_ld, PLT_DT_RELSZ);
+ if (dyn == NULL) {
+ set_errmsg("failed to find PLT_DT_RELSZ");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ total_size = dyn->d_un.d_ptr;
+
+ dyn = find_dyn_by_tag(lmap->l_ld, PLT_DT_RELENT);
+ if (dyn == NULL) {
+ set_errmsg("failed to find PLT_DT_RELENT");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ elem_size = dyn->d_un.d_ptr;
+ plthook.rela_dyn_cnt = total_size / elem_size;
+ }
+#endif
+
+#ifdef R_GLOBAL_DATA
+ if (plthook.rela_plt == NULL && plthook.rela_dyn == NULL) {
+ set_errmsg("failed to find either of DT_JMPREL and DT_REL");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+#else
+ if (plthook.rela_plt == NULL) {
+ set_errmsg("failed to find DT_JMPREL");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+#endif
+
+ *plthook_out = malloc(sizeof(plthook_t));
+ if (*plthook_out == NULL) {
+ set_errmsg("failed to allocate memory: %" SIZE_T_FMT " bytes", sizeof(plthook_t));
+ return PLTHOOK_OUT_OF_MEMORY;
+ }
+ **plthook_out = plthook;
+ return 0;
+}
+
+#if defined __FreeBSD__ || defined __sun
+static int check_elf_header(const Elf_Ehdr *ehdr)
+{
+ static const unsigned short s = 1;
+ /* Check endianness at runtime. */
+ unsigned char elfdata = (*(const char*)&s) ? ELFDATA2LSB : ELFDATA2MSB;
+
+ if (ehdr == NULL) {
+ set_errmsg("invalid elf header address: NULL");
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ set_errmsg("invalid file signature: 0x%02x,0x%02x,0x%02x,0x%02x",
+ ehdr->e_ident[0], ehdr->e_ident[1], ehdr->e_ident[2], ehdr->e_ident[3]);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
+ set_errmsg("invalid elf class: 0x%02x", ehdr->e_ident[EI_CLASS]);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ if (ehdr->e_ident[EI_DATA] != elfdata) {
+ set_errmsg("invalid elf data: 0x%02x", ehdr->e_ident[EI_DATA]);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
+ set_errmsg("invalid elf version: 0x%02x", ehdr->e_ident[EI_VERSION]);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+ set_errmsg("invalid file type: 0x%04x", ehdr->e_type);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ if (ehdr->e_version != EV_CURRENT) {
+ set_errmsg("invalid object file version: %" ELF_WORD_FMT, ehdr->e_version);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ if (ehdr->e_ehsize != sizeof(Elf_Ehdr)) {
+ set_errmsg("invalid elf header size: %u", ehdr->e_ehsize);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ if (ehdr->e_phentsize != sizeof(Elf_Phdr)) {
+ set_errmsg("invalid program header table entry size: %u", ehdr->e_phentsize);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ return 0;
+}
+#endif
+
+static int check_rel(const plthook_t *plthook, const Elf_Plt_Rel *plt, Elf_Xword r_type, const char **name_out, void ***addr_out)
+{
+ if (ELF_R_TYPE(plt->r_info) == r_type) {
+ size_t idx = ELF_R_SYM(plt->r_info);
+ idx = plthook->dynsym[idx].st_name;
+ if (idx + 1 > plthook->dynstr_size) {
+ set_errmsg("too big section header string table index: %" SIZE_T_FMT, idx);
+ return PLTHOOK_INVALID_FILE_FORMAT;
+ }
+ *name_out = plthook->dynstr + idx;
+ *addr_out = (void**)(plthook->plt_addr_base + plt->r_offset);
+ return 0;
+ }
+ return -1;
+}
+
+int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out)
+{
+ while (*pos < plthook->rela_plt_cnt) {
+ const Elf_Plt_Rel *plt = plthook->rela_plt + *pos;
+ int rv = check_rel(plthook, plt, R_JUMP_SLOT, name_out, addr_out);
+ (*pos)++;
+ if (rv >= 0) {
+ return rv;
+ }
+ }
+#ifdef R_GLOBAL_DATA
+ while (*pos < plthook->rela_plt_cnt + plthook->rela_dyn_cnt) {
+ const Elf_Plt_Rel *plt = plthook->rela_dyn + (*pos - plthook->rela_plt_cnt);
+ int rv = check_rel(plthook, plt, R_GLOBAL_DATA, name_out, addr_out);
+ (*pos)++;
+ if (rv >= 0) {
+ return rv;
+ }
+ }
+#endif
+ *name_out = NULL;
+ *addr_out = NULL;
+ return EOF;
+}
+
+int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc)
+{
+ size_t funcnamelen = strlen(funcname);
+ unsigned int pos = 0;
+ const char *name;
+ void **addr;
+ int rv;
+
+ if (plthook == NULL) {
+ set_errmsg("invalid argument: The first argument is null.");
+ return PLTHOOK_INVALID_ARGUMENT;
+ }
+ while ((rv = plthook_enum(plthook, &pos, &name, &addr)) == 0) {
+ if (strncmp(name, funcname, funcnamelen) == 0) {
+ if (name[funcnamelen] == '\0' || name[funcnamelen] == '@') {
+ int prot = get_memory_permission(addr);
+ if (prot == 0) {
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ if (!(prot & PROT_WRITE)) {
+ if (mprotect(ALIGN_ADDR(addr), page_size, PROT_READ | PROT_WRITE) != 0) {
+ set_errmsg("Could not change the process memory permission at %p: %s",
+ ALIGN_ADDR(addr), strerror(errno));
+ return PLTHOOK_INTERNAL_ERROR;
+ }
+ }
+ if (oldfunc) {
+ *oldfunc = *addr;
+ }
+ *addr = funcaddr;
+ if (!(prot & PROT_WRITE)) {
+ mprotect(ALIGN_ADDR(addr), page_size, prot);
+ }
+ return 0;
+ }
+ }
+ }
+ if (rv == EOF) {
+ set_errmsg("no such function: %s", funcname);
+ rv = PLTHOOK_FUNCTION_NOT_FOUND;
+ }
+ return rv;
+}
+
+void plthook_close(plthook_t *plthook)
+{
+ if (plthook != NULL) {
+ free(plthook);
+ }
+}
+
+const char *plthook_error(void)
+{
+ return errmsg;
+}
+
+static void set_errmsg(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(errmsg, sizeof(errmsg) - 1, fmt, ap);
+ va_end(ap);
+}
Index: simplescreenrecorder-salsa/src/GUI/PageWelcome.cpp
===================================================================
--- simplescreenrecorder-salsa.orig/src/GUI/PageWelcome.cpp 2024-05-07 08:50:15.918773109 +0200
+++ simplescreenrecorder-salsa/src/GUI/PageWelcome.cpp 2024-05-07 08:50:15.914773073 +0200
@@ -121,8 +121,8 @@
html_about.replace("%SOURCECODE%", tr("The source code of this program can be found at:"));
html_about.replace("%USES%", tr("This program uses:"));
html_about.replace("%USES_QT%", tr("%1 for the graphical user interface").arg("<a href=\"https://qt-project.org/\">Qt</a>"));
- html_about.replace("%USES_LIBAV_FFMPEG%", tr("%1 or %2 (depending on your distribution) for video/audio encoding").arg("<a href=\"http://libav.org/\">libav</a>").arg("<a href=\"http://ffmpeg.org/\">ffmpeg</a>"));
- html_about.replace("%USES_ELFHACKS%", tr("%1 for hooking system functions for OpenGL recording").arg("<a href=\"https://github.com/nullkey/elfhacks\">elfhacks</a>"));
+ html_about.replace("%USES_FFMPEG%", tr("%1 for video/audio encoding").arg("<a href=\"https://ffmpeg.org/\">FFmpeg</a>"));
+ html_about.replace("%USES_PLTHOOK%", tr("%1 for hooking system functions for OpenGL recording").arg("<a href=\"https://github.com/kubo/plthook\">PLTHook</a>"));
html_about.replace("%VERSION%", SSR_VERSION);
html_about.replace("%VERSIONINFO%", GetVersionInfo().replace("\n", "<br>\n"));
Index: simplescreenrecorder-salsa/glinject/elfhacks.h
===================================================================
--- simplescreenrecorder-salsa.orig/glinject/elfhacks.h 2024-05-07 08:50:11.850735880 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,208 +0,0 @@
-/**
- * \file src/elfhacks.h
- * \brief elfhacks application interface
- * \author Pyry Haulos <pyry.haulos@gmail.com>
- * \date 2007-2008
- */
-
-/* elfhacks.h -- Various ELF run-time hacks
- version 0.4.1, March 9th, 2008
-
- Copyright (C) 2007-2008 Pyry Haulos
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-
- Pyry Haulos <pyry.haulos@gmail.com>
-*/
-
-#include <elf.h>
-#include <link.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define __PUBLIC __attribute__ ((visibility ("default")))
-
-#ifdef __x86_64__
-# define __elf64
-#endif
-#ifdef __i386__
-# define __elf32
-#endif
-
-#if defined(__elf64)
-# define ELFW_R_SYM ELF64_R_SYM
-# define ElfW_Sword Elf64_Sxword
-# ifndef ElfW
-# define ElfW(v) Elf64_##v
-# endif
-# ifndef __ELF_NATIVE_CLASS
-# define __ELF_NATIVE_CLASS 64
-# endif
-#elif defined(__elf32)
-# define ELFW_R_SYM ELF32_R_SYM
-# define ElfW_Sword Elf32_Sword
-# ifndef ElfW
-# define ElfW(v) Elf32_##v
-# endif
-# ifndef __ELF_NATIVE_CLASS
-# define __ELF_NATIVE_CLASS 32
-# endif
-#else
-# error neither __elf32 nor __elf64 is defined
-#endif
-
-/**
- * \defgroup elfhacks elfhacks
- * Elfhacks is a collection of functions that aim for retvieving
- * or modifying progam's dynamic linking information at run-time.
- * \{
- */
-
-/**
- * \brief elfhacks program object
- */
-typedef struct {
- /** file name */
- const char *name;
- /** base address in memory */
- ElfW(Addr) addr;
- /** program headers */
- const ElfW(Phdr) *phdr;
- /** number of program headers */
- ElfW(Half) phnum;
- /** .dynamic */
- ElfW(Dyn) *dynamic;
- /** .symtab */
- ElfW(Sym) *symtab;
- /** .strtab */
- const char *strtab;
- /** symbol hash table (DT_HASH) */
- ElfW(Word) *hash;
- /** symbol hash table (DT_GNU_HASH) */
- Elf32_Word *gnu_hash;
-} eh_obj_t;
-
-/**
- * \brief elfhacks symbol
- */
-typedef struct {
- /** symbol name */
- const char *name;
- /** corresponding ElfW(Sym) */
- ElfW(Sym) *sym;
- /** elfhacks object this symbol is associated to */
- eh_obj_t *obj;
-} eh_sym_t;
-
-/**
- * \brief elfhacks relocation
- */
-typedef struct {
- /** symbol this relocation is associated to */
- eh_sym_t *sym;
- /** corresponding ElfW(Rel) (NULL if this is Rela) */
- ElfW(Rel) *rel;
- /** corresponding ElfW(Rela) (NULL if this is Rel) */
- ElfW(Rela) *rela;
- /** elfhacks program object */
- eh_obj_t *obj;
-} eh_rel_t;
-
-/**
- * \brief Iterate objects callback
- */
-typedef int (*eh_iterate_obj_callback_func)(eh_obj_t *obj, void *arg);
-/**
- * \brief Iterate symbols callback
- */
-typedef int (*eh_iterate_sym_callback_func)(eh_sym_t *sym, void *arg);
-/**
- * \brief Iterate relocations callback
- */
-typedef int (*eh_iterate_rel_callback_func)(eh_rel_t *rel, void *arg);
-
-/**
- * \brief Initializes eh_obj_t for given soname
- *
- * Matching is done using fnmatch() so wildcards and other standard
- * filename metacharacters and expressions work.
- *
- * If soname is NULL, this function returns the main program object.
- * \param obj elfhacks object
- * \param soname object's soname (see /proc/pid/maps) or NULL for main
- * \return 0 on success otherwise a positive error code
-*/
-__PUBLIC int eh_find_obj(eh_obj_t *obj, const char *soname);
-
-/**
- * \brief Walk through list of objects
- * \param callback callback function
- * \param arg argument passed to callback function
- * \return 0 on success otherwise an error code
- */
-__PUBLIC int eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg);
-
-/**
- * \brief Finds symbol in object's .dynsym and retrvieves its value.
- * \param obj elfhacks program object
- * \param name symbol to find
- * \param to returned value
- * \return 0 on success otherwise a positive error code
-*/
-__PUBLIC int eh_find_sym(eh_obj_t *obj, const char *name, void **to);
-
-/**
- * \brief Walk through list of symbols in object
- * \param obj elfhacks program object
- * \param callback callback function
- * \param arg argument passed to callback function
- * \return 0 on success otherwise an error code
- */
-__PUBLIC int eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback, void *arg);
-
-/**
- * \brief Iterates through object's .rel.plt and .rela.plt and sets every
- * occurrence of some symbol to the specified value.
- * \param obj elfhacks program object
- * \param sym symbol to replace
- * \param val new value
- * \return 0 on success otherwise a positive error code
-*/
-__PUBLIC int eh_set_rel(eh_obj_t *obj, const char *sym, void *val);
-
-/**
- * \brief Walk through object's .rel.plt and .rela.plt
- * \param obj elfhacks program object
- * \param callback callback function
- * \param arg argument passed to callback function
- */
-__PUBLIC int eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback, void *arg);
-
-/**
- * \brief Destroy eh_obj_t object.
- * \param obj elfhacks program object
- * \return 0 on success otherwise a positive error code
-*/
-__PUBLIC int eh_destroy_obj(eh_obj_t *obj);
-
-/** \} */
-
-#ifdef __cplusplus
-}
-#endif