Created
May 10, 2026 19:36
-
-
Save ReimuNotMoe/70fd569cf5370c430b30ee2b1192a023 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // SPDX-License-Identifier: MIT | |
| // Idea by ReimuNotMoe <reimu@sudomaker.com> | |
| // Written by ChatGPT | |
| // Usage: | |
| // gcc -O2 -Wall -Wextra -fPIC -shared plasma-software-rendering.c -ldl -o plasma-software-rendering.so | |
| // cp plasma-software-rendering.so /usr/local/lib/ | |
| // export LD_PRELOAD=/usr/local/lib/plasma-software-rendering.so | |
| #define _GNU_SOURCE | |
| #include <dlfcn.h> | |
| #include <errno.h> | |
| #include <limits.h> | |
| #include <spawn.h> | |
| #include <pthread.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <time.h> | |
| #include <unistd.h> | |
| #ifdef __linux__ | |
| #include <sys/auxv.h> | |
| #ifndef AT_SECURE | |
| #define AT_SECURE 23 | |
| #endif | |
| #endif | |
| #ifndef SOFTGL_DRIVER | |
| #define SOFTGL_DRIVER "llvmpipe" | |
| #endif | |
| #ifndef SOFTGL_FRAME_LIMIT_FPS | |
| #define SOFTGL_FRAME_LIMIT_FPS 60 | |
| #endif | |
| #ifndef SOFTGL_FRAME_LIMIT_STALE_RESET_NS | |
| #define SOFTGL_FRAME_LIMIT_STALE_RESET_NS 1000000000ULL | |
| #endif | |
| extern char **environ; | |
| typedef struct _XDisplay Display; | |
| typedef unsigned long GLXDrawable; | |
| typedef unsigned char GLubyte; | |
| typedef void (*__GLXextFuncPtr)(void); | |
| typedef void *EGLDisplay; | |
| typedef void *EGLSurface; | |
| typedef int EGLint; | |
| typedef unsigned int EGLBoolean; | |
| typedef void (*__eglMustCastToProperFunctionPointerType)(void); | |
| typedef EGLBoolean (*eglSwapBuffersWithDamage_fn)(EGLDisplay, EGLSurface, EGLint *, EGLint); | |
| static char *(*real_getenv_fn)(const char *name); | |
| static char *(*real_secure_getenv_fn)(const char *name); | |
| static int (*real_execve_fn)(const char *, char *const[], char *const[]); | |
| static int (*real_execvpe_fn)(const char *, char *const[], char *const[]); | |
| static int (*real_posix_spawn_fn)( | |
| pid_t *, const char *, | |
| const posix_spawn_file_actions_t *, | |
| const posix_spawnattr_t *, | |
| char *const[], char *const[] | |
| ); | |
| static int (*real_posix_spawnp_fn)( | |
| pid_t *, const char *, | |
| const posix_spawn_file_actions_t *, | |
| const posix_spawnattr_t *, | |
| char *const[], char *const[] | |
| ); | |
| static void (*real_glXSwapBuffers_fn)(Display *, GLXDrawable); | |
| static void (*real_glXSwapIntervalEXT_fn)(Display *, GLXDrawable, int); | |
| static int (*real_glXSwapIntervalMESA_fn)(unsigned int); | |
| static int (*real_glXSwapIntervalSGI_fn)(int); | |
| static __GLXextFuncPtr (*real_glXGetProcAddressARB_fn)(const GLubyte *); | |
| static __GLXextFuncPtr (*real_glXGetProcAddress_fn)(const GLubyte *); | |
| static EGLBoolean (*real_eglSwapInterval_fn)(EGLDisplay, EGLint); | |
| static EGLBoolean (*real_eglSwapBuffers_fn)(EGLDisplay, EGLSurface); | |
| static eglSwapBuffersWithDamage_fn real_eglSwapBuffersWithDamageEXT_fn; | |
| static eglSwapBuffersWithDamage_fn real_eglSwapBuffersWithDamageKHR_fn; | |
| static __eglMustCastToProperFunctionPointerType (*real_eglGetProcAddress_fn)(const char *); | |
| static __thread int resolving; | |
| static int target_cache = -1; | |
| static const char *self_path; | |
| static const char *self_base; | |
| struct env_block { | |
| char **envp; | |
| char *ld_preload_value; | |
| int allocated; | |
| }; | |
| static const char *base_name(const char *s) { | |
| const char *p = s ? strrchr(s, '/') : NULL; | |
| return p ? p + 1 : s; | |
| } | |
| static int is_target_name(const char *base) { | |
| return base && | |
| (strcmp(base, "plasmashell") == 0 || | |
| strcmp(base, "kscreenlocker_greet") == 0); | |
| } | |
| static int child_is_target(const char *file, char *const argv[]) { | |
| if (is_target_name(base_name(file))) | |
| return 1; | |
| if (argv && argv[0] && is_target_name(base_name(argv[0]))) | |
| return 1; | |
| return 0; | |
| } | |
| #define RESOLVE(fn) do { \ | |
| if (!real_##fn##_fn && !resolving) { \ | |
| resolving = 1; \ | |
| real_##fn##_fn = dlsym(RTLD_NEXT, #fn); \ | |
| resolving = 0; \ | |
| } \ | |
| } while (0) | |
| static char *real_getenv_call(const char *name) { | |
| RESOLVE(getenv); | |
| return real_getenv_fn ? real_getenv_fn(name) : NULL; | |
| } | |
| static char *real_secure_getenv_call(const char *name) { | |
| RESOLVE(secure_getenv); | |
| if (real_secure_getenv_fn) | |
| return real_secure_getenv_fn(name); | |
| return real_getenv_call(name); | |
| } | |
| static int is_secure_exec(void) { | |
| #ifdef __linux__ | |
| errno = 0; | |
| unsigned long v = getauxval(AT_SECURE); | |
| if (errno == 0 && v != 0) | |
| return 1; | |
| #endif | |
| return 0; | |
| } | |
| static int is_target_process(void) { | |
| if (target_cache >= 0) | |
| return target_cache; | |
| char path[PATH_MAX]; | |
| ssize_t n = readlink("/proc/self/exe", path, sizeof(path) - 1); | |
| if (n > 0) { | |
| path[n] = '\0'; | |
| if (is_target_name(base_name(path))) { | |
| target_cache = 1; | |
| return 1; | |
| } | |
| } | |
| #ifdef __GLIBC__ | |
| extern char *program_invocation_short_name; | |
| if (is_target_name(program_invocation_short_name)) { | |
| target_cache = 1; | |
| return 1; | |
| } | |
| #endif | |
| target_cache = 0; | |
| return 0; | |
| } | |
| static char *fake_mesa_env(const char *name, int secure_variant) { | |
| if (!name || !is_target_process()) | |
| return NULL; | |
| if (secure_variant && is_secure_exec()) | |
| return NULL; | |
| if (strcmp(name, "LIBGL_ALWAYS_SOFTWARE") == 0) | |
| return (char *)"true"; | |
| if (strcmp(name, "GALLIUM_DRIVER") == 0) | |
| return (char *)SOFTGL_DRIVER; | |
| /* Qt Quick: avoid the threaded render loop running as fast as llvmpipe can render | |
| when swap/present does not block. The basic loop is timer-driven. */ | |
| if (strcmp(name, "QSG_RENDER_LOOP") == 0) | |
| return (char *)"basic"; | |
| return NULL; | |
| } | |
| char *getenv(const char *name) { | |
| char *v = fake_mesa_env(name, 0); | |
| return v ? v : real_getenv_call(name); | |
| } | |
| char *secure_getenv(const char *name) { | |
| char *v = fake_mesa_env(name, 1); | |
| return v ? v : real_secure_getenv_call(name); | |
| } | |
| char *__secure_getenv(const char *name) { | |
| return secure_getenv(name); | |
| } | |
| static int target_should_force_vsync(void) { | |
| return is_target_process(); | |
| } | |
| static uint64_t monotonic_ns(void) { | |
| struct timespec ts; | |
| if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) | |
| return 0; | |
| return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec; | |
| } | |
| static void sleep_until_ns(uint64_t deadline_ns) { | |
| struct timespec ts; | |
| ts.tv_sec = (time_t)(deadline_ns / 1000000000ULL); | |
| ts.tv_nsec = (long)(deadline_ns % 1000000000ULL); | |
| while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) != 0) { | |
| if (errno != EINTR) | |
| break; | |
| } | |
| } | |
| static void throttle_target_frame(void) { | |
| #if SOFTGL_FRAME_LIMIT_FPS > 0 | |
| static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; | |
| static uint64_t next_frame_ns; | |
| const uint64_t interval_ns = 1000000000ULL / (uint64_t)SOFTGL_FRAME_LIMIT_FPS; | |
| uint64_t now; | |
| uint64_t deadline; | |
| if (!target_should_force_vsync() || interval_ns == 0) | |
| return; | |
| now = monotonic_ns(); | |
| if (now == 0) | |
| return; | |
| pthread_mutex_lock(&lock); | |
| if (next_frame_ns == 0 || next_frame_ns + SOFTGL_FRAME_LIMIT_STALE_RESET_NS < now) | |
| next_frame_ns = now; | |
| if (now > next_frame_ns) | |
| next_frame_ns = now; | |
| deadline = next_frame_ns; | |
| next_frame_ns = deadline + interval_ns; | |
| pthread_mutex_unlock(&lock); | |
| if (deadline > now) | |
| sleep_until_ns(deadline); | |
| #else | |
| (void)target_should_force_vsync; | |
| #endif | |
| } | |
| static int procname_eq(const GLubyte *name, const char *s) { | |
| return name && strcmp((const char *)name, s) == 0; | |
| } | |
| static void *resolve_default_next(const char *name) { | |
| void *p = NULL; | |
| if (!resolving) { | |
| resolving = 1; | |
| p = dlsym(RTLD_NEXT, name); | |
| resolving = 0; | |
| } | |
| return p; | |
| } | |
| static void resolve_glx_getproc(void) { | |
| if (!real_glXGetProcAddressARB_fn) | |
| real_glXGetProcAddressARB_fn = resolve_default_next("glXGetProcAddressARB"); | |
| if (!real_glXGetProcAddress_fn) | |
| real_glXGetProcAddress_fn = resolve_default_next("glXGetProcAddress"); | |
| } | |
| static __GLXextFuncPtr real_glx_getproc_any(const char *name) { | |
| resolve_glx_getproc(); | |
| if (real_glXGetProcAddressARB_fn) { | |
| __GLXextFuncPtr p = real_glXGetProcAddressARB_fn((const GLubyte *)name); | |
| if (p) | |
| return p; | |
| } | |
| if (real_glXGetProcAddress_fn) { | |
| __GLXextFuncPtr p = real_glXGetProcAddress_fn((const GLubyte *)name); | |
| if (p) | |
| return p; | |
| } | |
| return NULL; | |
| } | |
| static void resolve_glx_swap_interval(void) { | |
| if (!real_glXSwapIntervalEXT_fn) | |
| real_glXSwapIntervalEXT_fn = resolve_default_next("glXSwapIntervalEXT"); | |
| if (!real_glXSwapIntervalMESA_fn) | |
| real_glXSwapIntervalMESA_fn = resolve_default_next("glXSwapIntervalMESA"); | |
| if (!real_glXSwapIntervalSGI_fn) | |
| real_glXSwapIntervalSGI_fn = resolve_default_next("glXSwapIntervalSGI"); | |
| if (!real_glXSwapIntervalEXT_fn) | |
| real_glXSwapIntervalEXT_fn = (void (*)(Display *, GLXDrawable, int))real_glx_getproc_any("glXSwapIntervalEXT"); | |
| if (!real_glXSwapIntervalMESA_fn) | |
| real_glXSwapIntervalMESA_fn = (int (*)(unsigned int))real_glx_getproc_any("glXSwapIntervalMESA"); | |
| if (!real_glXSwapIntervalSGI_fn) | |
| real_glXSwapIntervalSGI_fn = (int (*)(int))real_glx_getproc_any("glXSwapIntervalSGI"); | |
| } | |
| static void force_glx_vsync(Display *dpy, GLXDrawable drawable) { | |
| if (!target_should_force_vsync()) | |
| return; | |
| resolve_glx_swap_interval(); | |
| if (real_glXSwapIntervalEXT_fn) { | |
| real_glXSwapIntervalEXT_fn(dpy, drawable, 1); | |
| return; | |
| } | |
| if (real_glXSwapIntervalMESA_fn) { | |
| (void)real_glXSwapIntervalMESA_fn(1); | |
| return; | |
| } | |
| if (real_glXSwapIntervalSGI_fn) | |
| (void)real_glXSwapIntervalSGI_fn(1); | |
| } | |
| void glXSwapBuffers(Display *dpy, GLXDrawable drawable) { | |
| if (!real_glXSwapBuffers_fn) | |
| real_glXSwapBuffers_fn = resolve_default_next("glXSwapBuffers"); | |
| force_glx_vsync(dpy, drawable); | |
| throttle_target_frame(); | |
| if (real_glXSwapBuffers_fn) | |
| real_glXSwapBuffers_fn(dpy, drawable); | |
| } | |
| void glXSwapIntervalEXT(Display *dpy, GLXDrawable drawable, int interval) { | |
| (void)interval; | |
| resolve_glx_swap_interval(); | |
| if (real_glXSwapIntervalEXT_fn) | |
| real_glXSwapIntervalEXT_fn(dpy, drawable, target_should_force_vsync() ? 1 : interval); | |
| } | |
| int glXSwapIntervalMESA(unsigned int interval) { | |
| resolve_glx_swap_interval(); | |
| if (real_glXSwapIntervalMESA_fn) | |
| return real_glXSwapIntervalMESA_fn(target_should_force_vsync() ? 1U : interval); | |
| return 0; | |
| } | |
| int glXSwapIntervalSGI(int interval) { | |
| resolve_glx_swap_interval(); | |
| if (real_glXSwapIntervalSGI_fn) | |
| return real_glXSwapIntervalSGI_fn(target_should_force_vsync() ? 1 : interval); | |
| return 0; | |
| } | |
| __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *procName) { | |
| if (target_should_force_vsync()) { | |
| if (procname_eq(procName, "glXSwapIntervalEXT")) | |
| return (__GLXextFuncPtr)glXSwapIntervalEXT; | |
| if (procname_eq(procName, "glXSwapIntervalMESA")) | |
| return (__GLXextFuncPtr)glXSwapIntervalMESA; | |
| if (procname_eq(procName, "glXSwapIntervalSGI")) | |
| return (__GLXextFuncPtr)glXSwapIntervalSGI; | |
| if (procname_eq(procName, "glXSwapBuffers")) | |
| return (__GLXextFuncPtr)glXSwapBuffers; | |
| } | |
| resolve_glx_getproc(); | |
| return real_glXGetProcAddressARB_fn ? real_glXGetProcAddressARB_fn(procName) : NULL; | |
| } | |
| __GLXextFuncPtr glXGetProcAddress(const GLubyte *procName) { | |
| if (target_should_force_vsync()) { | |
| if (procname_eq(procName, "glXSwapIntervalEXT")) | |
| return (__GLXextFuncPtr)glXSwapIntervalEXT; | |
| if (procname_eq(procName, "glXSwapIntervalMESA")) | |
| return (__GLXextFuncPtr)glXSwapIntervalMESA; | |
| if (procname_eq(procName, "glXSwapIntervalSGI")) | |
| return (__GLXextFuncPtr)glXSwapIntervalSGI; | |
| if (procname_eq(procName, "glXSwapBuffers")) | |
| return (__GLXextFuncPtr)glXSwapBuffers; | |
| } | |
| resolve_glx_getproc(); | |
| return real_glXGetProcAddress_fn ? real_glXGetProcAddress_fn(procName) : NULL; | |
| } | |
| static void resolve_egl_getproc(void) { | |
| if (!real_eglGetProcAddress_fn) | |
| real_eglGetProcAddress_fn = resolve_default_next("eglGetProcAddress"); | |
| } | |
| static void resolve_egl_swap(void) { | |
| if (!real_eglSwapInterval_fn) | |
| real_eglSwapInterval_fn = resolve_default_next("eglSwapInterval"); | |
| if (!real_eglSwapBuffers_fn) | |
| real_eglSwapBuffers_fn = resolve_default_next("eglSwapBuffers"); | |
| if (!real_eglSwapBuffersWithDamageEXT_fn) | |
| real_eglSwapBuffersWithDamageEXT_fn = (eglSwapBuffersWithDamage_fn)resolve_default_next("eglSwapBuffersWithDamageEXT"); | |
| if (!real_eglSwapBuffersWithDamageKHR_fn) | |
| real_eglSwapBuffersWithDamageKHR_fn = (eglSwapBuffersWithDamage_fn)resolve_default_next("eglSwapBuffersWithDamageKHR"); | |
| resolve_egl_getproc(); | |
| if (!real_eglSwapInterval_fn && real_eglGetProcAddress_fn) | |
| real_eglSwapInterval_fn = (EGLBoolean (*)(EGLDisplay, EGLint))real_eglGetProcAddress_fn("eglSwapInterval"); | |
| if (!real_eglSwapBuffers_fn && real_eglGetProcAddress_fn) | |
| real_eglSwapBuffers_fn = (EGLBoolean (*)(EGLDisplay, EGLSurface))real_eglGetProcAddress_fn("eglSwapBuffers"); | |
| if (!real_eglSwapBuffersWithDamageEXT_fn && real_eglGetProcAddress_fn) | |
| real_eglSwapBuffersWithDamageEXT_fn = (eglSwapBuffersWithDamage_fn)real_eglGetProcAddress_fn("eglSwapBuffersWithDamageEXT"); | |
| if (!real_eglSwapBuffersWithDamageKHR_fn && real_eglGetProcAddress_fn) | |
| real_eglSwapBuffersWithDamageKHR_fn = (eglSwapBuffersWithDamage_fn)real_eglGetProcAddress_fn("eglSwapBuffersWithDamageKHR"); | |
| } | |
| static void force_egl_vsync(EGLDisplay dpy) { | |
| if (!target_should_force_vsync()) | |
| return; | |
| resolve_egl_swap(); | |
| if (real_eglSwapInterval_fn) | |
| (void)real_eglSwapInterval_fn(dpy, 1); | |
| } | |
| EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) { | |
| resolve_egl_swap(); | |
| if (real_eglSwapInterval_fn) | |
| return real_eglSwapInterval_fn(dpy, target_should_force_vsync() ? 1 : interval); | |
| return 1; | |
| } | |
| EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { | |
| resolve_egl_swap(); | |
| force_egl_vsync(dpy); | |
| throttle_target_frame(); | |
| if (real_eglSwapBuffers_fn) | |
| return real_eglSwapBuffers_fn(dpy, surface); | |
| return 0; | |
| } | |
| EGLBoolean eglSwapBuffersWithDamageEXT( | |
| EGLDisplay dpy, | |
| EGLSurface surface, | |
| EGLint *rects, | |
| EGLint n_rects | |
| ) { | |
| resolve_egl_swap(); | |
| force_egl_vsync(dpy); | |
| throttle_target_frame(); | |
| if (real_eglSwapBuffersWithDamageEXT_fn) | |
| return real_eglSwapBuffersWithDamageEXT_fn(dpy, surface, rects, n_rects); | |
| if (real_eglSwapBuffers_fn) | |
| return real_eglSwapBuffers_fn(dpy, surface); | |
| return 0; | |
| } | |
| EGLBoolean eglSwapBuffersWithDamageKHR( | |
| EGLDisplay dpy, | |
| EGLSurface surface, | |
| EGLint *rects, | |
| EGLint n_rects | |
| ) { | |
| resolve_egl_swap(); | |
| force_egl_vsync(dpy); | |
| throttle_target_frame(); | |
| if (real_eglSwapBuffersWithDamageKHR_fn) | |
| return real_eglSwapBuffersWithDamageKHR_fn(dpy, surface, rects, n_rects); | |
| if (real_eglSwapBuffers_fn) | |
| return real_eglSwapBuffers_fn(dpy, surface); | |
| return 0; | |
| } | |
| __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) { | |
| if (target_should_force_vsync()) { | |
| if (procname && strcmp(procname, "eglSwapInterval") == 0) | |
| return (__eglMustCastToProperFunctionPointerType)eglSwapInterval; | |
| if (procname && strcmp(procname, "eglSwapBuffers") == 0) | |
| return (__eglMustCastToProperFunctionPointerType)eglSwapBuffers; | |
| if (procname && strcmp(procname, "eglSwapBuffersWithDamageEXT") == 0) | |
| return (__eglMustCastToProperFunctionPointerType)eglSwapBuffersWithDamageEXT; | |
| if (procname && strcmp(procname, "eglSwapBuffersWithDamageKHR") == 0) | |
| return (__eglMustCastToProperFunctionPointerType)eglSwapBuffersWithDamageKHR; | |
| } | |
| resolve_egl_getproc(); | |
| return real_eglGetProcAddress_fn ? real_eglGetProcAddress_fn(procname) : NULL; | |
| } | |
| static int token_is_our_so(const char *tok) { | |
| if (!tok || !*tok || !self_base) | |
| return 0; | |
| if (self_path && strcmp(tok, self_path) == 0) | |
| return 1; | |
| return strcmp(base_name(tok), self_base) == 0; | |
| } | |
| static char *strip_own_from_ld_preload(const char *old, int *changed) { | |
| *changed = 0; | |
| if (!old || !*old) | |
| return NULL; | |
| char *copy = strdup(old); | |
| char *out = malloc(strlen(old) + 1); | |
| if (!copy || !out) { | |
| free(copy); | |
| free(out); | |
| errno = ENOMEM; | |
| return NULL; | |
| } | |
| out[0] = '\0'; | |
| for (char *p = copy; *p;) { | |
| while (*p == ':' || *p == ' ' || *p == '\t') | |
| ++p; | |
| if (!*p) | |
| break; | |
| char *start = p; | |
| while (*p && *p != ':' && *p != ' ' && *p != '\t') | |
| ++p; | |
| char saved = *p; | |
| *p = '\0'; | |
| if (token_is_our_so(start)) { | |
| *changed = 1; | |
| } else { | |
| if (out[0]) | |
| strcat(out, ":"); | |
| strcat(out, start); | |
| } | |
| if (!saved) | |
| break; | |
| ++p; | |
| } | |
| free(copy); | |
| if (!*changed || out[0]) | |
| return out; | |
| free(out); | |
| return NULL; | |
| } | |
| static int make_sanitized_env( | |
| char *const in_envp[], | |
| int keep_preload, | |
| struct env_block *out | |
| ) { | |
| out->envp = (char **)in_envp; | |
| out->ld_preload_value = NULL; | |
| out->allocated = 0; | |
| if (keep_preload) | |
| return 0; | |
| char *const *src = in_envp ? in_envp : environ; | |
| size_t n = 0; | |
| int idx = -1; | |
| for (; src[n]; ++n) { | |
| if (strncmp(src[n], "LD_PRELOAD=", 11) == 0) | |
| idx = (int)n; | |
| } | |
| if (idx < 0) | |
| return 0; | |
| int changed = 0; | |
| char *new_ld = strip_own_from_ld_preload(src[idx] + 11, &changed); | |
| if (!changed) { | |
| free(new_ld); | |
| return 0; | |
| } | |
| char **dst = calloc(n + 1, sizeof(char *)); | |
| if (!dst) { | |
| free(new_ld); | |
| errno = ENOMEM; | |
| return -1; | |
| } | |
| size_t j = 0; | |
| for (size_t i = 0; i < n; ++i) { | |
| if ((int)i == idx) { | |
| if (new_ld && *new_ld) { | |
| size_t len = strlen(new_ld) + 12; | |
| out->ld_preload_value = malloc(len); | |
| if (!out->ld_preload_value) { | |
| free(dst); | |
| free(new_ld); | |
| errno = ENOMEM; | |
| return -1; | |
| } | |
| snprintf(out->ld_preload_value, len, "LD_PRELOAD=%s", new_ld); | |
| dst[j++] = out->ld_preload_value; | |
| } | |
| } else { | |
| dst[j++] = (char *)src[i]; | |
| } | |
| } | |
| dst[j] = NULL; | |
| free(new_ld); | |
| out->envp = dst; | |
| out->allocated = 1; | |
| return 0; | |
| } | |
| static void free_env_block(struct env_block *b) { | |
| if (b->allocated) | |
| free(b->envp); | |
| free(b->ld_preload_value); | |
| } | |
| static void strip_current_env_if_target(void) { | |
| if (!is_target_process()) | |
| return; | |
| const char *old = real_getenv_call("LD_PRELOAD"); | |
| int changed = 0; | |
| char *new_ld = strip_own_from_ld_preload(old, &changed); | |
| if (changed) { | |
| if (new_ld && *new_ld) | |
| setenv("LD_PRELOAD", new_ld, 1); | |
| else | |
| unsetenv("LD_PRELOAD"); | |
| } | |
| free(new_ld); | |
| } | |
| int execve(const char *path, char *const argv[], char *const envp[]) { | |
| RESOLVE(execve); | |
| if (!real_execve_fn) { | |
| errno = ENOSYS; | |
| return -1; | |
| } | |
| struct env_block b; | |
| if (make_sanitized_env(envp, child_is_target(path, argv), &b) < 0) | |
| return -1; | |
| int r = real_execve_fn(path, argv, b.envp); | |
| int saved = errno; | |
| free_env_block(&b); | |
| errno = saved; | |
| return r; | |
| } | |
| int execv(const char *path, char *const argv[]) { | |
| return execve(path, argv, environ); | |
| } | |
| int execvpe(const char *file, char *const argv[], char *const envp[]) { | |
| RESOLVE(execvpe); | |
| if (!real_execvpe_fn) { | |
| errno = ENOSYS; | |
| return -1; | |
| } | |
| struct env_block b; | |
| if (make_sanitized_env(envp, child_is_target(file, argv), &b) < 0) | |
| return -1; | |
| int r = real_execvpe_fn(file, argv, b.envp); | |
| int saved = errno; | |
| free_env_block(&b); | |
| errno = saved; | |
| return r; | |
| } | |
| int execvp(const char *file, char *const argv[]) { | |
| return execvpe(file, argv, environ); | |
| } | |
| int posix_spawn( | |
| pid_t *pid, | |
| const char *path, | |
| const posix_spawn_file_actions_t *file_actions, | |
| const posix_spawnattr_t *attrp, | |
| char *const argv[], | |
| char *const envp[] | |
| ) { | |
| RESOLVE(posix_spawn); | |
| if (!real_posix_spawn_fn) | |
| return ENOSYS; | |
| struct env_block b; | |
| if (make_sanitized_env(envp, child_is_target(path, argv), &b) < 0) | |
| return errno; | |
| int r = real_posix_spawn_fn(pid, path, file_actions, attrp, argv, b.envp); | |
| free_env_block(&b); | |
| return r; | |
| } | |
| int posix_spawnp( | |
| pid_t *pid, | |
| const char *file, | |
| const posix_spawn_file_actions_t *file_actions, | |
| const posix_spawnattr_t *attrp, | |
| char *const argv[], | |
| char *const envp[] | |
| ) { | |
| RESOLVE(posix_spawnp); | |
| if (!real_posix_spawnp_fn) | |
| return ENOSYS; | |
| struct env_block b; | |
| if (make_sanitized_env(envp, child_is_target(file, argv), &b) < 0) | |
| return errno; | |
| int r = real_posix_spawnp_fn(pid, file, file_actions, attrp, argv, b.envp); | |
| free_env_block(&b); | |
| return r; | |
| } | |
| __attribute__((constructor)) | |
| static void softgl_preload_init(void) { | |
| Dl_info di; | |
| if (dladdr((void *)&softgl_preload_init, &di) && di.dli_fname) { | |
| self_path = di.dli_fname; | |
| self_base = base_name(di.dli_fname); | |
| } | |
| strip_current_env_if_target(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment